Mercurial > xmpp-account-manager
comparison strophe.js @ 0:2a8d4e8600d0
Initial commit.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Fri, 21 Dec 2018 21:34:17 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:2a8d4e8600d0 |
---|---|
1 (function webpackUniversalModuleDefinition(root, factory) { | |
2 if(typeof exports === 'object' && typeof module === 'object') | |
3 module.exports = factory(); | |
4 else if(typeof define === 'function' && define.amd) | |
5 define([], factory); | |
6 else if(typeof exports === 'object') | |
7 exports["strophe"] = factory(); | |
8 else | |
9 root["strophe"] = factory(); | |
10 })(window, function() { | |
11 return /******/ (function(modules) { // webpackBootstrap | |
12 /******/ // The module cache | |
13 /******/ var installedModules = {}; | |
14 /******/ | |
15 /******/ // The require function | |
16 /******/ function __webpack_require__(moduleId) { | |
17 /******/ | |
18 /******/ // Check if module is in cache | |
19 /******/ if(installedModules[moduleId]) { | |
20 /******/ return installedModules[moduleId].exports; | |
21 /******/ } | |
22 /******/ // Create a new module (and put it into the cache) | |
23 /******/ var module = installedModules[moduleId] = { | |
24 /******/ i: moduleId, | |
25 /******/ l: false, | |
26 /******/ exports: {} | |
27 /******/ }; | |
28 /******/ | |
29 /******/ // Execute the module function | |
30 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | |
31 /******/ | |
32 /******/ // Flag the module as loaded | |
33 /******/ module.l = true; | |
34 /******/ | |
35 /******/ // Return the exports of the module | |
36 /******/ return module.exports; | |
37 /******/ } | |
38 /******/ | |
39 /******/ | |
40 /******/ // expose the modules object (__webpack_modules__) | |
41 /******/ __webpack_require__.m = modules; | |
42 /******/ | |
43 /******/ // expose the module cache | |
44 /******/ __webpack_require__.c = installedModules; | |
45 /******/ | |
46 /******/ // define getter function for harmony exports | |
47 /******/ __webpack_require__.d = function(exports, name, getter) { | |
48 /******/ if(!__webpack_require__.o(exports, name)) { | |
49 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); | |
50 /******/ } | |
51 /******/ }; | |
52 /******/ | |
53 /******/ // define __esModule on exports | |
54 /******/ __webpack_require__.r = function(exports) { | |
55 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { | |
56 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | |
57 /******/ } | |
58 /******/ Object.defineProperty(exports, '__esModule', { value: true }); | |
59 /******/ }; | |
60 /******/ | |
61 /******/ // create a fake namespace object | |
62 /******/ // mode & 1: value is a module id, require it | |
63 /******/ // mode & 2: merge all properties of value into the ns | |
64 /******/ // mode & 4: return value when already ns object | |
65 /******/ // mode & 8|1: behave like require | |
66 /******/ __webpack_require__.t = function(value, mode) { | |
67 /******/ if(mode & 1) value = __webpack_require__(value); | |
68 /******/ if(mode & 8) return value; | |
69 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; | |
70 /******/ var ns = Object.create(null); | |
71 /******/ __webpack_require__.r(ns); | |
72 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); | |
73 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); | |
74 /******/ return ns; | |
75 /******/ }; | |
76 /******/ | |
77 /******/ // getDefaultExport function for compatibility with non-harmony modules | |
78 /******/ __webpack_require__.n = function(module) { | |
79 /******/ var getter = module && module.__esModule ? | |
80 /******/ function getDefault() { return module['default']; } : | |
81 /******/ function getModuleExports() { return module; }; | |
82 /******/ __webpack_require__.d(getter, 'a', getter); | |
83 /******/ return getter; | |
84 /******/ }; | |
85 /******/ | |
86 /******/ // Object.prototype.hasOwnProperty.call | |
87 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; | |
88 /******/ | |
89 /******/ // __webpack_public_path__ | |
90 /******/ __webpack_require__.p = ""; | |
91 /******/ | |
92 /******/ | |
93 /******/ // Load entry module and return exports | |
94 /******/ return __webpack_require__(__webpack_require__.s = "./src/strophe.js"); | |
95 /******/ }) | |
96 /************************************************************************/ | |
97 /******/ ({ | |
98 | |
99 /***/ "./node_modules/webpack/buildin/global.js": | |
100 /*!***********************************!*\ | |
101 !*** (webpack)/buildin/global.js ***! | |
102 \***********************************/ | |
103 /*! no static exports found */ | |
104 /***/ (function(module, exports) { | |
105 | |
106 var g; | |
107 | |
108 // This works in non-strict mode | |
109 g = (function() { | |
110 return this; | |
111 })(); | |
112 | |
113 try { | |
114 // This works if eval is allowed (see CSP) | |
115 g = g || Function("return this")() || (1, eval)("this"); | |
116 } catch (e) { | |
117 // This works if the window reference is available | |
118 if (typeof window === "object") g = window; | |
119 } | |
120 | |
121 // g can still be undefined, but nothing to do about it... | |
122 // We return undefined, instead of nothing here, so it's | |
123 // easier to handle this case. if(!global) { ...} | |
124 | |
125 module.exports = g; | |
126 | |
127 | |
128 /***/ }), | |
129 | |
130 /***/ "./src/bosh.js": | |
131 /*!*********************!*\ | |
132 !*** ./src/bosh.js ***! | |
133 \*********************/ | |
134 /*! no exports provided */ | |
135 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
136 | |
137 "use strict"; | |
138 __webpack_require__.r(__webpack_exports__); | |
139 /* harmony import */ var core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core */ "./src/core.js"); | |
140 /* | |
141 This program is distributed under the terms of the MIT license. | |
142 Please see the LICENSE file for details. | |
143 | |
144 Copyright 2006-2008, OGG, LLC | |
145 */ | |
146 | |
147 /* global window, setTimeout, clearTimeout, XMLHttpRequest, ActiveXObject */ | |
148 | |
149 var Strophe = core__WEBPACK_IMPORTED_MODULE_0__["default"].Strophe; | |
150 var $build = core__WEBPACK_IMPORTED_MODULE_0__["default"].$build; | |
151 /** PrivateClass: Strophe.Request | |
152 * _Private_ helper class that provides a cross implementation abstraction | |
153 * for a BOSH related XMLHttpRequest. | |
154 * | |
155 * The Strophe.Request class is used internally to encapsulate BOSH request | |
156 * information. It is not meant to be used from user's code. | |
157 */ | |
158 | |
159 /** PrivateConstructor: Strophe.Request | |
160 * Create and initialize a new Strophe.Request object. | |
161 * | |
162 * Parameters: | |
163 * (XMLElement) elem - The XML data to be sent in the request. | |
164 * (Function) func - The function that will be called when the | |
165 * XMLHttpRequest readyState changes. | |
166 * (Integer) rid - The BOSH rid attribute associated with this request. | |
167 * (Integer) sends - The number of times this same request has been sent. | |
168 */ | |
169 | |
170 Strophe.Request = function (elem, func, rid, sends) { | |
171 this.id = ++Strophe._requestId; | |
172 this.xmlData = elem; | |
173 this.data = Strophe.serialize(elem); // save original function in case we need to make a new request | |
174 // from this one. | |
175 | |
176 this.origFunc = func; | |
177 this.func = func; | |
178 this.rid = rid; | |
179 this.date = NaN; | |
180 this.sends = sends || 0; | |
181 this.abort = false; | |
182 this.dead = null; | |
183 | |
184 this.age = function () { | |
185 if (!this.date) { | |
186 return 0; | |
187 } | |
188 | |
189 var now = new Date(); | |
190 return (now - this.date) / 1000; | |
191 }; | |
192 | |
193 this.timeDead = function () { | |
194 if (!this.dead) { | |
195 return 0; | |
196 } | |
197 | |
198 var now = new Date(); | |
199 return (now - this.dead) / 1000; | |
200 }; | |
201 | |
202 this.xhr = this._newXHR(); | |
203 }; | |
204 | |
205 Strophe.Request.prototype = { | |
206 /** PrivateFunction: getResponse | |
207 * Get a response from the underlying XMLHttpRequest. | |
208 * | |
209 * This function attempts to get a response from the request and checks | |
210 * for errors. | |
211 * | |
212 * Throws: | |
213 * "parsererror" - A parser error occured. | |
214 * "bad-format" - The entity has sent XML that cannot be processed. | |
215 * | |
216 * Returns: | |
217 * The DOM element tree of the response. | |
218 */ | |
219 getResponse: function getResponse() { | |
220 var node = null; | |
221 | |
222 if (this.xhr.responseXML && this.xhr.responseXML.documentElement) { | |
223 node = this.xhr.responseXML.documentElement; | |
224 | |
225 if (node.tagName === "parsererror") { | |
226 Strophe.error("invalid response received"); | |
227 Strophe.error("responseText: " + this.xhr.responseText); | |
228 Strophe.error("responseXML: " + Strophe.serialize(this.xhr.responseXML)); | |
229 throw new Error("parsererror"); | |
230 } | |
231 } else if (this.xhr.responseText) { | |
232 // In React Native, we may get responseText but no responseXML. We can try to parse it manually. | |
233 Strophe.debug("Got responseText but no responseXML; attempting to parse it with DOMParser..."); | |
234 node = new DOMParser().parseFromString(this.xhr.responseText, 'application/xml').documentElement; | |
235 | |
236 if (!node) { | |
237 throw new Error('Parsing produced null node'); | |
238 } else if (node.querySelector('parsererror')) { | |
239 Strophe.error("invalid response received: " + node.querySelector('parsererror').textContent); | |
240 Strophe.error("responseText: " + this.xhr.responseText); | |
241 var error = new Error(); | |
242 error.name = Strophe.ErrorCondition.BAD_FORMAT; | |
243 throw error; | |
244 } | |
245 } | |
246 | |
247 return node; | |
248 }, | |
249 | |
250 /** PrivateFunction: _newXHR | |
251 * _Private_ helper function to create XMLHttpRequests. | |
252 * | |
253 * This function creates XMLHttpRequests across all implementations. | |
254 * | |
255 * Returns: | |
256 * A new XMLHttpRequest. | |
257 */ | |
258 _newXHR: function _newXHR() { | |
259 var xhr = null; | |
260 | |
261 if (window.XMLHttpRequest) { | |
262 xhr = new XMLHttpRequest(); | |
263 | |
264 if (xhr.overrideMimeType) { | |
265 xhr.overrideMimeType("text/xml; charset=utf-8"); | |
266 } | |
267 } else if (window.ActiveXObject) { | |
268 xhr = new ActiveXObject("Microsoft.XMLHTTP"); | |
269 } // use Function.bind() to prepend ourselves as an argument | |
270 | |
271 | |
272 xhr.onreadystatechange = this.func.bind(null, this); | |
273 return xhr; | |
274 } | |
275 }; | |
276 /** Class: Strophe.Bosh | |
277 * _Private_ helper class that handles BOSH Connections | |
278 * | |
279 * The Strophe.Bosh class is used internally by Strophe.Connection | |
280 * to encapsulate BOSH sessions. It is not meant to be used from user's code. | |
281 */ | |
282 | |
283 /** File: bosh.js | |
284 * A JavaScript library to enable BOSH in Strophejs. | |
285 * | |
286 * this library uses Bidirectional-streams Over Synchronous HTTP (BOSH) | |
287 * to emulate a persistent, stateful, two-way connection to an XMPP server. | |
288 * More information on BOSH can be found in XEP 124. | |
289 */ | |
290 | |
291 /** PrivateConstructor: Strophe.Bosh | |
292 * Create and initialize a Strophe.Bosh object. | |
293 * | |
294 * Parameters: | |
295 * (Strophe.Connection) connection - The Strophe.Connection that will use BOSH. | |
296 * | |
297 * Returns: | |
298 * A new Strophe.Bosh object. | |
299 */ | |
300 | |
301 Strophe.Bosh = function (connection) { | |
302 this._conn = connection; | |
303 /* request id for body tags */ | |
304 | |
305 this.rid = Math.floor(Math.random() * 4294967295); | |
306 /* The current session ID. */ | |
307 | |
308 this.sid = null; // default BOSH values | |
309 | |
310 this.hold = 1; | |
311 this.wait = 60; | |
312 this.window = 5; | |
313 this.errors = 0; | |
314 this.inactivity = null; | |
315 this.lastResponseHeaders = null; | |
316 this._requests = []; | |
317 }; | |
318 | |
319 Strophe.Bosh.prototype = { | |
320 /** Variable: strip | |
321 * | |
322 * BOSH-Connections will have all stanzas wrapped in a <body> tag when | |
323 * passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>. | |
324 * To strip this tag, User code can set <Strophe.Bosh.strip> to "body": | |
325 * | |
326 * > Strophe.Bosh.prototype.strip = "body"; | |
327 * | |
328 * This will enable stripping of the body tag in both | |
329 * <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>. | |
330 */ | |
331 strip: null, | |
332 | |
333 /** PrivateFunction: _buildBody | |
334 * _Private_ helper function to generate the <body/> wrapper for BOSH. | |
335 * | |
336 * Returns: | |
337 * A Strophe.Builder with a <body/> element. | |
338 */ | |
339 _buildBody: function _buildBody() { | |
340 var bodyWrap = $build('body', { | |
341 'rid': this.rid++, | |
342 'xmlns': Strophe.NS.HTTPBIND | |
343 }); | |
344 | |
345 if (this.sid !== null) { | |
346 bodyWrap.attrs({ | |
347 'sid': this.sid | |
348 }); | |
349 } | |
350 | |
351 if (this._conn.options.keepalive && this._conn._sessionCachingSupported()) { | |
352 this._cacheSession(); | |
353 } | |
354 | |
355 return bodyWrap; | |
356 }, | |
357 | |
358 /** PrivateFunction: _reset | |
359 * Reset the connection. | |
360 * | |
361 * This function is called by the reset function of the Strophe Connection | |
362 */ | |
363 _reset: function _reset() { | |
364 this.rid = Math.floor(Math.random() * 4294967295); | |
365 this.sid = null; | |
366 this.errors = 0; | |
367 | |
368 if (this._conn._sessionCachingSupported()) { | |
369 window.sessionStorage.removeItem('strophe-bosh-session'); | |
370 } | |
371 | |
372 this._conn.nextValidRid(this.rid); | |
373 }, | |
374 | |
375 /** PrivateFunction: _connect | |
376 * _Private_ function that initializes the BOSH connection. | |
377 * | |
378 * Creates and sends the Request that initializes the BOSH connection. | |
379 */ | |
380 _connect: function _connect(wait, hold, route) { | |
381 this.wait = wait || this.wait; | |
382 this.hold = hold || this.hold; | |
383 this.errors = 0; | |
384 | |
385 var body = this._buildBody().attrs({ | |
386 "to": this._conn.domain, | |
387 "xml:lang": "en", | |
388 "wait": this.wait, | |
389 "hold": this.hold, | |
390 "content": "text/xml; charset=utf-8", | |
391 "ver": "1.6", | |
392 "xmpp:version": "1.0", | |
393 "xmlns:xmpp": Strophe.NS.BOSH | |
394 }); | |
395 | |
396 if (route) { | |
397 body.attrs({ | |
398 'route': route | |
399 }); | |
400 } | |
401 | |
402 var _connect_cb = this._conn._connect_cb; | |
403 | |
404 this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, _connect_cb.bind(this._conn)), body.tree().getAttribute("rid"))); | |
405 | |
406 this._throttledRequestHandler(); | |
407 }, | |
408 | |
409 /** PrivateFunction: _attach | |
410 * Attach to an already created and authenticated BOSH session. | |
411 * | |
412 * This function is provided to allow Strophe to attach to BOSH | |
413 * sessions which have been created externally, perhaps by a Web | |
414 * application. This is often used to support auto-login type features | |
415 * without putting user credentials into the page. | |
416 * | |
417 * Parameters: | |
418 * (String) jid - The full JID that is bound by the session. | |
419 * (String) sid - The SID of the BOSH session. | |
420 * (String) rid - The current RID of the BOSH session. This RID | |
421 * will be used by the next request. | |
422 * (Function) callback The connect callback function. | |
423 * (Integer) wait - The optional HTTPBIND wait value. This is the | |
424 * time the server will wait before returning an empty result for | |
425 * a request. The default setting of 60 seconds is recommended. | |
426 * Other settings will require tweaks to the Strophe.TIMEOUT value. | |
427 * (Integer) hold - The optional HTTPBIND hold value. This is the | |
428 * number of connections the server will hold at one time. This | |
429 * should almost always be set to 1 (the default). | |
430 * (Integer) wind - The optional HTTBIND window value. This is the | |
431 * allowed range of request ids that are valid. The default is 5. | |
432 */ | |
433 _attach: function _attach(jid, sid, rid, callback, wait, hold, wind) { | |
434 this._conn.jid = jid; | |
435 this.sid = sid; | |
436 this.rid = rid; | |
437 this._conn.connect_callback = callback; | |
438 this._conn.domain = Strophe.getDomainFromJid(this._conn.jid); | |
439 this._conn.authenticated = true; | |
440 this._conn.connected = true; | |
441 this.wait = wait || this.wait; | |
442 this.hold = hold || this.hold; | |
443 this.window = wind || this.window; | |
444 | |
445 this._conn._changeConnectStatus(Strophe.Status.ATTACHED, null); | |
446 }, | |
447 | |
448 /** PrivateFunction: _restore | |
449 * Attempt to restore a cached BOSH session | |
450 * | |
451 * Parameters: | |
452 * (String) jid - The full JID that is bound by the session. | |
453 * This parameter is optional but recommended, specifically in cases | |
454 * where prebinded BOSH sessions are used where it's important to know | |
455 * that the right session is being restored. | |
456 * (Function) callback The connect callback function. | |
457 * (Integer) wait - The optional HTTPBIND wait value. This is the | |
458 * time the server will wait before returning an empty result for | |
459 * a request. The default setting of 60 seconds is recommended. | |
460 * Other settings will require tweaks to the Strophe.TIMEOUT value. | |
461 * (Integer) hold - The optional HTTPBIND hold value. This is the | |
462 * number of connections the server will hold at one time. This | |
463 * should almost always be set to 1 (the default). | |
464 * (Integer) wind - The optional HTTBIND window value. This is the | |
465 * allowed range of request ids that are valid. The default is 5. | |
466 */ | |
467 _restore: function _restore(jid, callback, wait, hold, wind) { | |
468 var session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session')); | |
469 | |
470 if (typeof session !== "undefined" && session !== null && session.rid && session.sid && session.jid && (typeof jid === "undefined" || jid === null || Strophe.getBareJidFromJid(session.jid) === Strophe.getBareJidFromJid(jid) || // If authcid is null, then it's an anonymous login, so | |
471 // we compare only the domains: | |
472 Strophe.getNodeFromJid(jid) === null && Strophe.getDomainFromJid(session.jid) === jid)) { | |
473 this._conn.restored = true; | |
474 | |
475 this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind); | |
476 } else { | |
477 var error = new Error("_restore: no restoreable session."); | |
478 error.name = "StropheSessionError"; | |
479 throw error; | |
480 } | |
481 }, | |
482 | |
483 /** PrivateFunction: _cacheSession | |
484 * _Private_ handler for the beforeunload event. | |
485 * | |
486 * This handler is used to process the Bosh-part of the initial request. | |
487 * Parameters: | |
488 * (Strophe.Request) bodyWrap - The received stanza. | |
489 */ | |
490 _cacheSession: function _cacheSession() { | |
491 if (this._conn.authenticated) { | |
492 if (this._conn.jid && this.rid && this.sid) { | |
493 window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({ | |
494 'jid': this._conn.jid, | |
495 'rid': this.rid, | |
496 'sid': this.sid | |
497 })); | |
498 } | |
499 } else { | |
500 window.sessionStorage.removeItem('strophe-bosh-session'); | |
501 } | |
502 }, | |
503 | |
504 /** PrivateFunction: _connect_cb | |
505 * _Private_ handler for initial connection request. | |
506 * | |
507 * This handler is used to process the Bosh-part of the initial request. | |
508 * Parameters: | |
509 * (Strophe.Request) bodyWrap - The received stanza. | |
510 */ | |
511 _connect_cb: function _connect_cb(bodyWrap) { | |
512 var typ = bodyWrap.getAttribute("type"); | |
513 | |
514 if (typ !== null && typ === "terminate") { | |
515 // an error occurred | |
516 var cond = bodyWrap.getAttribute("condition"); | |
517 Strophe.error("BOSH-Connection failed: " + cond); | |
518 var conflict = bodyWrap.getElementsByTagName("conflict"); | |
519 | |
520 if (cond !== null) { | |
521 if (cond === "remote-stream-error" && conflict.length > 0) { | |
522 cond = "conflict"; | |
523 } | |
524 | |
525 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, cond); | |
526 } else { | |
527 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown"); | |
528 } | |
529 | |
530 this._conn._doDisconnect(cond); | |
531 | |
532 return Strophe.Status.CONNFAIL; | |
533 } // check to make sure we don't overwrite these if _connect_cb is | |
534 // called multiple times in the case of missing stream:features | |
535 | |
536 | |
537 if (!this.sid) { | |
538 this.sid = bodyWrap.getAttribute("sid"); | |
539 } | |
540 | |
541 var wind = bodyWrap.getAttribute('requests'); | |
542 | |
543 if (wind) { | |
544 this.window = parseInt(wind, 10); | |
545 } | |
546 | |
547 var hold = bodyWrap.getAttribute('hold'); | |
548 | |
549 if (hold) { | |
550 this.hold = parseInt(hold, 10); | |
551 } | |
552 | |
553 var wait = bodyWrap.getAttribute('wait'); | |
554 | |
555 if (wait) { | |
556 this.wait = parseInt(wait, 10); | |
557 } | |
558 | |
559 var inactivity = bodyWrap.getAttribute('inactivity'); | |
560 | |
561 if (inactivity) { | |
562 this.inactivity = parseInt(inactivity, 10); | |
563 } | |
564 }, | |
565 | |
566 /** PrivateFunction: _disconnect | |
567 * _Private_ part of Connection.disconnect for Bosh | |
568 * | |
569 * Parameters: | |
570 * (Request) pres - This stanza will be sent before disconnecting. | |
571 */ | |
572 _disconnect: function _disconnect(pres) { | |
573 this._sendTerminate(pres); | |
574 }, | |
575 | |
576 /** PrivateFunction: _doDisconnect | |
577 * _Private_ function to disconnect. | |
578 * | |
579 * Resets the SID and RID. | |
580 */ | |
581 _doDisconnect: function _doDisconnect() { | |
582 this.sid = null; | |
583 this.rid = Math.floor(Math.random() * 4294967295); | |
584 | |
585 if (this._conn._sessionCachingSupported()) { | |
586 window.sessionStorage.removeItem('strophe-bosh-session'); | |
587 } | |
588 | |
589 this._conn.nextValidRid(this.rid); | |
590 }, | |
591 | |
592 /** PrivateFunction: _emptyQueue | |
593 * _Private_ function to check if the Request queue is empty. | |
594 * | |
595 * Returns: | |
596 * True, if there are no Requests queued, False otherwise. | |
597 */ | |
598 _emptyQueue: function _emptyQueue() { | |
599 return this._requests.length === 0; | |
600 }, | |
601 | |
602 /** PrivateFunction: _callProtocolErrorHandlers | |
603 * _Private_ function to call error handlers registered for HTTP errors. | |
604 * | |
605 * Parameters: | |
606 * (Strophe.Request) req - The request that is changing readyState. | |
607 */ | |
608 _callProtocolErrorHandlers: function _callProtocolErrorHandlers(req) { | |
609 var reqStatus = this._getRequestStatus(req); | |
610 | |
611 var err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus]; | |
612 | |
613 if (err_callback) { | |
614 err_callback.call(this, reqStatus); | |
615 } | |
616 }, | |
617 | |
618 /** PrivateFunction: _hitError | |
619 * _Private_ function to handle the error count. | |
620 * | |
621 * Requests are resent automatically until their error count reaches | |
622 * 5. Each time an error is encountered, this function is called to | |
623 * increment the count and disconnect if the count is too high. | |
624 * | |
625 * Parameters: | |
626 * (Integer) reqStatus - The request status. | |
627 */ | |
628 _hitError: function _hitError(reqStatus) { | |
629 this.errors++; | |
630 Strophe.warn("request errored, status: " + reqStatus + ", number of errors: " + this.errors); | |
631 | |
632 if (this.errors > 4) { | |
633 this._conn._onDisconnectTimeout(); | |
634 } | |
635 }, | |
636 | |
637 /** PrivateFunction: _no_auth_received | |
638 * | |
639 * Called on stream start/restart when no stream:features | |
640 * has been received and sends a blank poll request. | |
641 */ | |
642 _no_auth_received: function _no_auth_received(callback) { | |
643 Strophe.warn("Server did not yet offer a supported authentication " + "mechanism. Sending a blank poll request."); | |
644 | |
645 if (callback) { | |
646 callback = callback.bind(this._conn); | |
647 } else { | |
648 callback = this._conn._connect_cb.bind(this._conn); | |
649 } | |
650 | |
651 var body = this._buildBody(); | |
652 | |
653 this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, callback), body.tree().getAttribute("rid"))); | |
654 | |
655 this._throttledRequestHandler(); | |
656 }, | |
657 | |
658 /** PrivateFunction: _onDisconnectTimeout | |
659 * _Private_ timeout handler for handling non-graceful disconnection. | |
660 * | |
661 * Cancels all remaining Requests and clears the queue. | |
662 */ | |
663 _onDisconnectTimeout: function _onDisconnectTimeout() { | |
664 this._abortAllRequests(); | |
665 }, | |
666 | |
667 /** PrivateFunction: _abortAllRequests | |
668 * _Private_ helper function that makes sure all pending requests are aborted. | |
669 */ | |
670 _abortAllRequests: function _abortAllRequests() { | |
671 while (this._requests.length > 0) { | |
672 var req = this._requests.pop(); | |
673 | |
674 req.abort = true; | |
675 req.xhr.abort(); // jslint complains, but this is fine. setting to empty func | |
676 // is necessary for IE6 | |
677 | |
678 req.xhr.onreadystatechange = function () {}; // jshint ignore:line | |
679 | |
680 } | |
681 }, | |
682 | |
683 /** PrivateFunction: _onIdle | |
684 * _Private_ handler called by Strophe.Connection._onIdle | |
685 * | |
686 * Sends all queued Requests or polls with empty Request if there are none. | |
687 */ | |
688 _onIdle: function _onIdle() { | |
689 var data = this._conn._data; // if no requests are in progress, poll | |
690 | |
691 if (this._conn.authenticated && this._requests.length === 0 && data.length === 0 && !this._conn.disconnecting) { | |
692 Strophe.info("no requests during idle cycle, sending " + "blank request"); | |
693 data.push(null); | |
694 } | |
695 | |
696 if (this._conn.paused) { | |
697 return; | |
698 } | |
699 | |
700 if (this._requests.length < 2 && data.length > 0) { | |
701 var body = this._buildBody(); | |
702 | |
703 for (var i = 0; i < data.length; i++) { | |
704 if (data[i] !== null) { | |
705 if (data[i] === "restart") { | |
706 body.attrs({ | |
707 "to": this._conn.domain, | |
708 "xml:lang": "en", | |
709 "xmpp:restart": "true", | |
710 "xmlns:xmpp": Strophe.NS.BOSH | |
711 }); | |
712 } else { | |
713 body.cnode(data[i]).up(); | |
714 } | |
715 } | |
716 } | |
717 | |
718 delete this._conn._data; | |
719 this._conn._data = []; | |
720 | |
721 this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute("rid"))); | |
722 | |
723 this._throttledRequestHandler(); | |
724 } | |
725 | |
726 if (this._requests.length > 0) { | |
727 var time_elapsed = this._requests[0].age(); | |
728 | |
729 if (this._requests[0].dead !== null) { | |
730 if (this._requests[0].timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) { | |
731 this._throttledRequestHandler(); | |
732 } | |
733 } | |
734 | |
735 if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) { | |
736 Strophe.warn("Request " + this._requests[0].id + " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) + " seconds since last activity"); | |
737 | |
738 this._throttledRequestHandler(); | |
739 } | |
740 } | |
741 }, | |
742 | |
743 /** PrivateFunction: _getRequestStatus | |
744 * | |
745 * Returns the HTTP status code from a Strophe.Request | |
746 * | |
747 * Parameters: | |
748 * (Strophe.Request) req - The Strophe.Request instance. | |
749 * (Integer) def - The default value that should be returned if no | |
750 * status value was found. | |
751 */ | |
752 _getRequestStatus: function _getRequestStatus(req, def) { | |
753 var reqStatus; | |
754 | |
755 if (req.xhr.readyState === 4) { | |
756 try { | |
757 reqStatus = req.xhr.status; | |
758 } catch (e) { | |
759 // ignore errors from undefined status attribute. Works | |
760 // around a browser bug | |
761 Strophe.error("Caught an error while retrieving a request's status, " + "reqStatus: " + reqStatus); | |
762 } | |
763 } | |
764 | |
765 if (typeof reqStatus === "undefined") { | |
766 reqStatus = typeof def === 'number' ? def : 0; | |
767 } | |
768 | |
769 return reqStatus; | |
770 }, | |
771 | |
772 /** PrivateFunction: _onRequestStateChange | |
773 * _Private_ handler for Strophe.Request state changes. | |
774 * | |
775 * This function is called when the XMLHttpRequest readyState changes. | |
776 * It contains a lot of error handling logic for the many ways that | |
777 * requests can fail, and calls the request callback when requests | |
778 * succeed. | |
779 * | |
780 * Parameters: | |
781 * (Function) func - The handler for the request. | |
782 * (Strophe.Request) req - The request that is changing readyState. | |
783 */ | |
784 _onRequestStateChange: function _onRequestStateChange(func, req) { | |
785 Strophe.debug("request id " + req.id + "." + req.sends + " state changed to " + req.xhr.readyState); | |
786 | |
787 if (req.abort) { | |
788 req.abort = false; | |
789 return; | |
790 } | |
791 | |
792 if (req.xhr.readyState !== 4) { | |
793 // The request is not yet complete | |
794 return; | |
795 } | |
796 | |
797 var reqStatus = this._getRequestStatus(req); | |
798 | |
799 this.lastResponseHeaders = req.xhr.getAllResponseHeaders(); | |
800 | |
801 if (this.disconnecting && reqStatus >= 400) { | |
802 this._hitError(reqStatus); | |
803 | |
804 this._callProtocolErrorHandlers(req); | |
805 | |
806 return; | |
807 } | |
808 | |
809 var valid_request = reqStatus > 0 && reqStatus < 500; | |
810 var too_many_retries = req.sends > this._conn.maxRetries; | |
811 | |
812 if (valid_request || too_many_retries) { | |
813 // remove from internal queue | |
814 this._removeRequest(req); | |
815 | |
816 Strophe.debug("request id " + req.id + " should now be removed"); | |
817 } | |
818 | |
819 if (reqStatus === 200) { | |
820 // request succeeded | |
821 var reqIs0 = this._requests[0] === req; | |
822 var reqIs1 = this._requests[1] === req; // if request 1 finished, or request 0 finished and request | |
823 // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to | |
824 // restart the other - both will be in the first spot, as the | |
825 // completed request has been removed from the queue already | |
826 | |
827 if (reqIs1 || reqIs0 && this._requests.length > 0 && this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) { | |
828 this._restartRequest(0); | |
829 } | |
830 | |
831 this._conn.nextValidRid(Number(req.rid) + 1); | |
832 | |
833 Strophe.debug("request id " + req.id + "." + req.sends + " got 200"); | |
834 func(req); // call handler | |
835 | |
836 this.errors = 0; | |
837 } else if (reqStatus === 0 || reqStatus >= 400 && reqStatus < 600 || reqStatus >= 12000) { | |
838 // request failed | |
839 Strophe.error("request id " + req.id + "." + req.sends + " error " + reqStatus + " happened"); | |
840 | |
841 this._hitError(reqStatus); | |
842 | |
843 this._callProtocolErrorHandlers(req); | |
844 | |
845 if (reqStatus >= 400 && reqStatus < 500) { | |
846 this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null); | |
847 | |
848 this._conn._doDisconnect(); | |
849 } | |
850 } else { | |
851 Strophe.error("request id " + req.id + "." + req.sends + " error " + reqStatus + " happened"); | |
852 } | |
853 | |
854 if (!valid_request && !too_many_retries) { | |
855 this._throttledRequestHandler(); | |
856 } else if (too_many_retries && !this._conn.connected) { | |
857 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "giving-up"); | |
858 } | |
859 }, | |
860 | |
861 /** PrivateFunction: _processRequest | |
862 * _Private_ function to process a request in the queue. | |
863 * | |
864 * This function takes requests off the queue and sends them and | |
865 * restarts dead requests. | |
866 * | |
867 * Parameters: | |
868 * (Integer) i - The index of the request in the queue. | |
869 */ | |
870 _processRequest: function _processRequest(i) { | |
871 var _this = this; | |
872 | |
873 var req = this._requests[i]; | |
874 | |
875 var reqStatus = this._getRequestStatus(req, -1); // make sure we limit the number of retries | |
876 | |
877 | |
878 if (req.sends > this._conn.maxRetries) { | |
879 this._conn._onDisconnectTimeout(); | |
880 | |
881 return; | |
882 } | |
883 | |
884 var time_elapsed = req.age(); | |
885 var primary_timeout = !isNaN(time_elapsed) && time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait); | |
886 var secondary_timeout = req.dead !== null && req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait); | |
887 var server_error = req.xhr.readyState === 4 && (reqStatus < 1 || reqStatus >= 500); | |
888 | |
889 if (primary_timeout || secondary_timeout || server_error) { | |
890 if (secondary_timeout) { | |
891 Strophe.error("Request ".concat(this._requests[i].id, " timed out (secondary), restarting")); | |
892 } | |
893 | |
894 req.abort = true; | |
895 req.xhr.abort(); // setting to null fails on IE6, so set to empty function | |
896 | |
897 req.xhr.onreadystatechange = function () {}; | |
898 | |
899 this._requests[i] = new Strophe.Request(req.xmlData, req.origFunc, req.rid, req.sends); | |
900 req = this._requests[i]; | |
901 } | |
902 | |
903 if (req.xhr.readyState === 0) { | |
904 Strophe.debug("request id " + req.id + "." + req.sends + " posting"); | |
905 | |
906 try { | |
907 var content_type = this._conn.options.contentType || "text/xml; charset=utf-8"; | |
908 req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true); | |
909 | |
910 if (typeof req.xhr.setRequestHeader !== 'undefined') { | |
911 // IE9 doesn't have setRequestHeader | |
912 req.xhr.setRequestHeader("Content-Type", content_type); | |
913 } | |
914 | |
915 if (this._conn.options.withCredentials) { | |
916 req.xhr.withCredentials = true; | |
917 } | |
918 } catch (e2) { | |
919 Strophe.error("XHR open failed: " + e2.toString()); | |
920 | |
921 if (!this._conn.connected) { | |
922 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "bad-service"); | |
923 } | |
924 | |
925 this._conn.disconnect(); | |
926 | |
927 return; | |
928 } // Fires the XHR request -- may be invoked immediately | |
929 // or on a gradually expanding retry window for reconnects | |
930 | |
931 | |
932 var sendFunc = function sendFunc() { | |
933 req.date = new Date(); | |
934 | |
935 if (_this._conn.options.customHeaders) { | |
936 var headers = _this._conn.options.customHeaders; | |
937 | |
938 for (var header in headers) { | |
939 if (Object.prototype.hasOwnProperty.call(headers, header)) { | |
940 req.xhr.setRequestHeader(header, headers[header]); | |
941 } | |
942 } | |
943 } | |
944 | |
945 req.xhr.send(req.data); | |
946 }; // Implement progressive backoff for reconnects -- | |
947 // First retry (send === 1) should also be instantaneous | |
948 | |
949 | |
950 if (req.sends > 1) { | |
951 // Using a cube of the retry number creates a nicely | |
952 // expanding retry window | |
953 var backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait), Math.pow(req.sends, 3)) * 1000; | |
954 setTimeout(function () { | |
955 // XXX: setTimeout should be called only with function expressions (23974bc1) | |
956 sendFunc(); | |
957 }, backoff); | |
958 } else { | |
959 sendFunc(); | |
960 } | |
961 | |
962 req.sends++; | |
963 | |
964 if (this._conn.xmlOutput !== Strophe.Connection.prototype.xmlOutput) { | |
965 if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) { | |
966 this._conn.xmlOutput(req.xmlData.childNodes[0]); | |
967 } else { | |
968 this._conn.xmlOutput(req.xmlData); | |
969 } | |
970 } | |
971 | |
972 if (this._conn.rawOutput !== Strophe.Connection.prototype.rawOutput) { | |
973 this._conn.rawOutput(req.data); | |
974 } | |
975 } else { | |
976 Strophe.debug("_processRequest: " + (i === 0 ? "first" : "second") + " request has readyState of " + req.xhr.readyState); | |
977 } | |
978 }, | |
979 | |
980 /** PrivateFunction: _removeRequest | |
981 * _Private_ function to remove a request from the queue. | |
982 * | |
983 * Parameters: | |
984 * (Strophe.Request) req - The request to remove. | |
985 */ | |
986 _removeRequest: function _removeRequest(req) { | |
987 Strophe.debug("removing request"); | |
988 | |
989 for (var i = this._requests.length - 1; i >= 0; i--) { | |
990 if (req === this._requests[i]) { | |
991 this._requests.splice(i, 1); | |
992 } | |
993 } // IE6 fails on setting to null, so set to empty function | |
994 | |
995 | |
996 req.xhr.onreadystatechange = function () {}; | |
997 | |
998 this._throttledRequestHandler(); | |
999 }, | |
1000 | |
1001 /** PrivateFunction: _restartRequest | |
1002 * _Private_ function to restart a request that is presumed dead. | |
1003 * | |
1004 * Parameters: | |
1005 * (Integer) i - The index of the request in the queue. | |
1006 */ | |
1007 _restartRequest: function _restartRequest(i) { | |
1008 var req = this._requests[i]; | |
1009 | |
1010 if (req.dead === null) { | |
1011 req.dead = new Date(); | |
1012 } | |
1013 | |
1014 this._processRequest(i); | |
1015 }, | |
1016 | |
1017 /** PrivateFunction: _reqToData | |
1018 * _Private_ function to get a stanza out of a request. | |
1019 * | |
1020 * Tries to extract a stanza out of a Request Object. | |
1021 * When this fails the current connection will be disconnected. | |
1022 * | |
1023 * Parameters: | |
1024 * (Object) req - The Request. | |
1025 * | |
1026 * Returns: | |
1027 * The stanza that was passed. | |
1028 */ | |
1029 _reqToData: function _reqToData(req) { | |
1030 try { | |
1031 return req.getResponse(); | |
1032 } catch (e) { | |
1033 if (e.message !== "parsererror") { | |
1034 throw e; | |
1035 } | |
1036 | |
1037 this._conn.disconnect("strophe-parsererror"); | |
1038 } | |
1039 }, | |
1040 | |
1041 /** PrivateFunction: _sendTerminate | |
1042 * _Private_ function to send initial disconnect sequence. | |
1043 * | |
1044 * This is the first step in a graceful disconnect. It sends | |
1045 * the BOSH server a terminate body and includes an unavailable | |
1046 * presence if authentication has completed. | |
1047 */ | |
1048 _sendTerminate: function _sendTerminate(pres) { | |
1049 Strophe.info("_sendTerminate was called"); | |
1050 | |
1051 var body = this._buildBody().attrs({ | |
1052 type: "terminate" | |
1053 }); | |
1054 | |
1055 if (pres) { | |
1056 body.cnode(pres.tree()); | |
1057 } | |
1058 | |
1059 var req = new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute("rid")); | |
1060 | |
1061 this._requests.push(req); | |
1062 | |
1063 this._throttledRequestHandler(); | |
1064 }, | |
1065 | |
1066 /** PrivateFunction: _send | |
1067 * _Private_ part of the Connection.send function for BOSH | |
1068 * | |
1069 * Just triggers the RequestHandler to send the messages that are in the queue | |
1070 */ | |
1071 _send: function _send() { | |
1072 var _this2 = this; | |
1073 | |
1074 clearTimeout(this._conn._idleTimeout); | |
1075 | |
1076 this._throttledRequestHandler(); | |
1077 | |
1078 this._conn._idleTimeout = setTimeout(function () { | |
1079 return _this2._conn._onIdle(); | |
1080 }, 100); | |
1081 }, | |
1082 | |
1083 /** PrivateFunction: _sendRestart | |
1084 * | |
1085 * Send an xmpp:restart stanza. | |
1086 */ | |
1087 _sendRestart: function _sendRestart() { | |
1088 this._throttledRequestHandler(); | |
1089 | |
1090 clearTimeout(this._conn._idleTimeout); | |
1091 }, | |
1092 | |
1093 /** PrivateFunction: _throttledRequestHandler | |
1094 * _Private_ function to throttle requests to the connection window. | |
1095 * | |
1096 * This function makes sure we don't send requests so fast that the | |
1097 * request ids overflow the connection window in the case that one | |
1098 * request died. | |
1099 */ | |
1100 _throttledRequestHandler: function _throttledRequestHandler() { | |
1101 if (!this._requests) { | |
1102 Strophe.debug("_throttledRequestHandler called with " + "undefined requests"); | |
1103 } else { | |
1104 Strophe.debug("_throttledRequestHandler called with " + this._requests.length + " requests"); | |
1105 } | |
1106 | |
1107 if (!this._requests || this._requests.length === 0) { | |
1108 return; | |
1109 } | |
1110 | |
1111 if (this._requests.length > 0) { | |
1112 this._processRequest(0); | |
1113 } | |
1114 | |
1115 if (this._requests.length > 1 && Math.abs(this._requests[0].rid - this._requests[1].rid) < this.window) { | |
1116 this._processRequest(1); | |
1117 } | |
1118 } | |
1119 }; | |
1120 | |
1121 /***/ }), | |
1122 | |
1123 /***/ "./src/core.js": | |
1124 /*!*********************!*\ | |
1125 !*** ./src/core.js ***! | |
1126 \*********************/ | |
1127 /*! exports provided: Strophe, $build, $iq, $msg, $pres, SHA1, MD5, default */ | |
1128 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
1129 | |
1130 "use strict"; | |
1131 __webpack_require__.r(__webpack_exports__); | |
1132 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Strophe", function() { return Strophe; }); | |
1133 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$build", function() { return $build; }); | |
1134 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$iq", function() { return $iq; }); | |
1135 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$msg", function() { return $msg; }); | |
1136 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$pres", function() { return $pres; }); | |
1137 /* harmony import */ var md5__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! md5 */ "./src/md5.js"); | |
1138 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MD5", function() { return md5__WEBPACK_IMPORTED_MODULE_0__["default"]; }); | |
1139 | |
1140 /* harmony import */ var sha1__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sha1 */ "./src/sha1.js"); | |
1141 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SHA1", function() { return sha1__WEBPACK_IMPORTED_MODULE_1__["default"]; }); | |
1142 | |
1143 /* harmony import */ var utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! utils */ "./src/utils.js"); | |
1144 function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } | |
1145 | |
1146 function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } | |
1147 | |
1148 function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } | |
1149 | |
1150 function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } | |
1151 | |
1152 function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | |
1153 | |
1154 /* | |
1155 This program is distributed under the terms of the MIT license. | |
1156 Please see the LICENSE file for details. | |
1157 | |
1158 Copyright 2006-2018, OGG, LLC | |
1159 */ | |
1160 | |
1161 /*global define, document, sessionStorage, setTimeout, clearTimeout, ActiveXObject, DOMParser, btoa, atob, module */ | |
1162 | |
1163 | |
1164 | |
1165 /** Function: $build | |
1166 * Create a Strophe.Builder. | |
1167 * This is an alias for 'new Strophe.Builder(name, attrs)'. | |
1168 * | |
1169 * Parameters: | |
1170 * (String) name - The root element name. | |
1171 * (Object) attrs - The attributes for the root element in object notation. | |
1172 * | |
1173 * Returns: | |
1174 * A new Strophe.Builder object. | |
1175 */ | |
1176 | |
1177 function $build(name, attrs) { | |
1178 return new Strophe.Builder(name, attrs); | |
1179 } | |
1180 /** Function: $msg | |
1181 * Create a Strophe.Builder with a <message/> element as the root. | |
1182 * | |
1183 * Parameters: | |
1184 * (Object) attrs - The <message/> element attributes in object notation. | |
1185 * | |
1186 * Returns: | |
1187 * A new Strophe.Builder object. | |
1188 */ | |
1189 | |
1190 | |
1191 function $msg(attrs) { | |
1192 return new Strophe.Builder("message", attrs); | |
1193 } | |
1194 /** Function: $iq | |
1195 * Create a Strophe.Builder with an <iq/> element as the root. | |
1196 * | |
1197 * Parameters: | |
1198 * (Object) attrs - The <iq/> element attributes in object notation. | |
1199 * | |
1200 * Returns: | |
1201 * A new Strophe.Builder object. | |
1202 */ | |
1203 | |
1204 | |
1205 function $iq(attrs) { | |
1206 return new Strophe.Builder("iq", attrs); | |
1207 } | |
1208 /** Function: $pres | |
1209 * Create a Strophe.Builder with a <presence/> element as the root. | |
1210 * | |
1211 * Parameters: | |
1212 * (Object) attrs - The <presence/> element attributes in object notation. | |
1213 * | |
1214 * Returns: | |
1215 * A new Strophe.Builder object. | |
1216 */ | |
1217 | |
1218 | |
1219 function $pres(attrs) { | |
1220 return new Strophe.Builder("presence", attrs); | |
1221 } | |
1222 /** Class: Strophe | |
1223 * An object container for all Strophe library functions. | |
1224 * | |
1225 * This class is just a container for all the objects and constants | |
1226 * used in the library. It is not meant to be instantiated, but to | |
1227 * provide a namespace for library objects, constants, and functions. | |
1228 */ | |
1229 | |
1230 | |
1231 var Strophe = { | |
1232 /** Constant: VERSION */ | |
1233 VERSION: "1.3.1", | |
1234 | |
1235 /** Constants: XMPP Namespace Constants | |
1236 * Common namespace constants from the XMPP RFCs and XEPs. | |
1237 * | |
1238 * NS.HTTPBIND - HTTP BIND namespace from XEP 124. | |
1239 * NS.BOSH - BOSH namespace from XEP 206. | |
1240 * NS.CLIENT - Main XMPP client namespace. | |
1241 * NS.AUTH - Legacy authentication namespace. | |
1242 * NS.ROSTER - Roster operations namespace. | |
1243 * NS.PROFILE - Profile namespace. | |
1244 * NS.DISCO_INFO - Service discovery info namespace from XEP 30. | |
1245 * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30. | |
1246 * NS.MUC - Multi-User Chat namespace from XEP 45. | |
1247 * NS.SASL - XMPP SASL namespace from RFC 3920. | |
1248 * NS.STREAM - XMPP Streams namespace from RFC 3920. | |
1249 * NS.BIND - XMPP Binding namespace from RFC 3920. | |
1250 * NS.SESSION - XMPP Session namespace from RFC 3920. | |
1251 * NS.XHTML_IM - XHTML-IM namespace from XEP 71. | |
1252 * NS.XHTML - XHTML body namespace from XEP 71. | |
1253 */ | |
1254 NS: { | |
1255 HTTPBIND: "http://jabber.org/protocol/httpbind", | |
1256 BOSH: "urn:xmpp:xbosh", | |
1257 CLIENT: "jabber:client", | |
1258 AUTH: "jabber:iq:auth", | |
1259 ROSTER: "jabber:iq:roster", | |
1260 PROFILE: "jabber:iq:profile", | |
1261 DISCO_INFO: "http://jabber.org/protocol/disco#info", | |
1262 DISCO_ITEMS: "http://jabber.org/protocol/disco#items", | |
1263 MUC: "http://jabber.org/protocol/muc", | |
1264 SASL: "urn:ietf:params:xml:ns:xmpp-sasl", | |
1265 STREAM: "http://etherx.jabber.org/streams", | |
1266 FRAMING: "urn:ietf:params:xml:ns:xmpp-framing", | |
1267 BIND: "urn:ietf:params:xml:ns:xmpp-bind", | |
1268 SESSION: "urn:ietf:params:xml:ns:xmpp-session", | |
1269 VERSION: "jabber:iq:version", | |
1270 STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas", | |
1271 XHTML_IM: "http://jabber.org/protocol/xhtml-im", | |
1272 XHTML: "http://www.w3.org/1999/xhtml" | |
1273 }, | |
1274 | |
1275 /** Constants: XHTML_IM Namespace | |
1276 * contains allowed tags, tag attributes, and css properties. | |
1277 * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset. | |
1278 * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended | |
1279 * allowed tags and their attributes. | |
1280 */ | |
1281 XHTML: { | |
1282 tags: ['a', 'blockquote', 'br', 'cite', 'em', 'img', 'li', 'ol', 'p', 'span', 'strong', 'ul', 'body'], | |
1283 attributes: { | |
1284 'a': ['href'], | |
1285 'blockquote': ['style'], | |
1286 'br': [], | |
1287 'cite': ['style'], | |
1288 'em': [], | |
1289 'img': ['src', 'alt', 'style', 'height', 'width'], | |
1290 'li': ['style'], | |
1291 'ol': ['style'], | |
1292 'p': ['style'], | |
1293 'span': ['style'], | |
1294 'strong': [], | |
1295 'ul': ['style'], | |
1296 'body': [] | |
1297 }, | |
1298 css: ['background-color', 'color', 'font-family', 'font-size', 'font-style', 'font-weight', 'margin-left', 'margin-right', 'text-align', 'text-decoration'], | |
1299 | |
1300 /** Function: XHTML.validTag | |
1301 * | |
1302 * Utility method to determine whether a tag is allowed | |
1303 * in the XHTML_IM namespace. | |
1304 * | |
1305 * XHTML tag names are case sensitive and must be lower case. | |
1306 */ | |
1307 validTag: function validTag(tag) { | |
1308 for (var i = 0; i < Strophe.XHTML.tags.length; i++) { | |
1309 if (tag === Strophe.XHTML.tags[i]) { | |
1310 return true; | |
1311 } | |
1312 } | |
1313 | |
1314 return false; | |
1315 }, | |
1316 | |
1317 /** Function: XHTML.validAttribute | |
1318 * | |
1319 * Utility method to determine whether an attribute is allowed | |
1320 * as recommended per XEP-0071 | |
1321 * | |
1322 * XHTML attribute names are case sensitive and must be lower case. | |
1323 */ | |
1324 validAttribute: function validAttribute(tag, attribute) { | |
1325 if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) { | |
1326 for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) { | |
1327 if (attribute === Strophe.XHTML.attributes[tag][i]) { | |
1328 return true; | |
1329 } | |
1330 } | |
1331 } | |
1332 | |
1333 return false; | |
1334 }, | |
1335 validCSS: function validCSS(style) { | |
1336 for (var i = 0; i < Strophe.XHTML.css.length; i++) { | |
1337 if (style === Strophe.XHTML.css[i]) { | |
1338 return true; | |
1339 } | |
1340 } | |
1341 | |
1342 return false; | |
1343 } | |
1344 }, | |
1345 | |
1346 /** Constants: Connection Status Constants | |
1347 * Connection status constants for use by the connection handler | |
1348 * callback. | |
1349 * | |
1350 * Status.ERROR - An error has occurred | |
1351 * Status.CONNECTING - The connection is currently being made | |
1352 * Status.CONNFAIL - The connection attempt failed | |
1353 * Status.AUTHENTICATING - The connection is authenticating | |
1354 * Status.AUTHFAIL - The authentication attempt failed | |
1355 * Status.CONNECTED - The connection has succeeded | |
1356 * Status.DISCONNECTED - The connection has been terminated | |
1357 * Status.DISCONNECTING - The connection is currently being terminated | |
1358 * Status.ATTACHED - The connection has been attached | |
1359 * Status.REDIRECT - The connection has been redirected | |
1360 * Status.CONNTIMEOUT - The connection has timed out | |
1361 */ | |
1362 Status: { | |
1363 ERROR: 0, | |
1364 CONNECTING: 1, | |
1365 CONNFAIL: 2, | |
1366 AUTHENTICATING: 3, | |
1367 AUTHFAIL: 4, | |
1368 CONNECTED: 5, | |
1369 DISCONNECTED: 6, | |
1370 DISCONNECTING: 7, | |
1371 ATTACHED: 8, | |
1372 REDIRECT: 9, | |
1373 CONNTIMEOUT: 10 | |
1374 }, | |
1375 ErrorCondition: { | |
1376 BAD_FORMAT: "bad-format", | |
1377 CONFLICT: "conflict", | |
1378 MISSING_JID_NODE: "x-strophe-bad-non-anon-jid", | |
1379 NO_AUTH_MECH: "no-auth-mech", | |
1380 UNKNOWN_REASON: "unknown" | |
1381 }, | |
1382 | |
1383 /** Constants: Log Level Constants | |
1384 * Logging level indicators. | |
1385 * | |
1386 * LogLevel.DEBUG - Debug output | |
1387 * LogLevel.INFO - Informational output | |
1388 * LogLevel.WARN - Warnings | |
1389 * LogLevel.ERROR - Errors | |
1390 * LogLevel.FATAL - Fatal errors | |
1391 */ | |
1392 LogLevel: { | |
1393 DEBUG: 0, | |
1394 INFO: 1, | |
1395 WARN: 2, | |
1396 ERROR: 3, | |
1397 FATAL: 4 | |
1398 }, | |
1399 | |
1400 /** PrivateConstants: DOM Element Type Constants | |
1401 * DOM element types. | |
1402 * | |
1403 * ElementType.NORMAL - Normal element. | |
1404 * ElementType.TEXT - Text data element. | |
1405 * ElementType.FRAGMENT - XHTML fragment element. | |
1406 */ | |
1407 ElementType: { | |
1408 NORMAL: 1, | |
1409 TEXT: 3, | |
1410 CDATA: 4, | |
1411 FRAGMENT: 11 | |
1412 }, | |
1413 | |
1414 /** PrivateConstants: Timeout Values | |
1415 * Timeout values for error states. These values are in seconds. | |
1416 * These should not be changed unless you know exactly what you are | |
1417 * doing. | |
1418 * | |
1419 * TIMEOUT - Timeout multiplier. A waiting request will be considered | |
1420 * failed after Math.floor(TIMEOUT * wait) seconds have elapsed. | |
1421 * This defaults to 1.1, and with default wait, 66 seconds. | |
1422 * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where | |
1423 * Strophe can detect early failure, it will consider the request | |
1424 * failed if it doesn't return after | |
1425 * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed. | |
1426 * This defaults to 0.1, and with default wait, 6 seconds. | |
1427 */ | |
1428 TIMEOUT: 1.1, | |
1429 SECONDARY_TIMEOUT: 0.1, | |
1430 | |
1431 /** Function: addNamespace | |
1432 * This function is used to extend the current namespaces in | |
1433 * Strophe.NS. It takes a key and a value with the key being the | |
1434 * name of the new namespace, with its actual value. | |
1435 * For example: | |
1436 * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub"); | |
1437 * | |
1438 * Parameters: | |
1439 * (String) name - The name under which the namespace will be | |
1440 * referenced under Strophe.NS | |
1441 * (String) value - The actual namespace. | |
1442 */ | |
1443 addNamespace: function addNamespace(name, value) { | |
1444 Strophe.NS[name] = value; | |
1445 }, | |
1446 | |
1447 /** Function: forEachChild | |
1448 * Map a function over some or all child elements of a given element. | |
1449 * | |
1450 * This is a small convenience function for mapping a function over | |
1451 * some or all of the children of an element. If elemName is null, all | |
1452 * children will be passed to the function, otherwise only children | |
1453 * whose tag names match elemName will be passed. | |
1454 * | |
1455 * Parameters: | |
1456 * (XMLElement) elem - The element to operate on. | |
1457 * (String) elemName - The child element tag name filter. | |
1458 * (Function) func - The function to apply to each child. This | |
1459 * function should take a single argument, a DOM element. | |
1460 */ | |
1461 forEachChild: function forEachChild(elem, elemName, func) { | |
1462 for (var i = 0; i < elem.childNodes.length; i++) { | |
1463 var childNode = elem.childNodes[i]; | |
1464 | |
1465 if (childNode.nodeType === Strophe.ElementType.NORMAL && (!elemName || this.isTagEqual(childNode, elemName))) { | |
1466 func(childNode); | |
1467 } | |
1468 } | |
1469 }, | |
1470 | |
1471 /** Function: isTagEqual | |
1472 * Compare an element's tag name with a string. | |
1473 * | |
1474 * This function is case sensitive. | |
1475 * | |
1476 * Parameters: | |
1477 * (XMLElement) el - A DOM element. | |
1478 * (String) name - The element name. | |
1479 * | |
1480 * Returns: | |
1481 * true if the element's tag name matches _el_, and false | |
1482 * otherwise. | |
1483 */ | |
1484 isTagEqual: function isTagEqual(el, name) { | |
1485 return el.tagName === name; | |
1486 }, | |
1487 | |
1488 /** PrivateVariable: _xmlGenerator | |
1489 * _Private_ variable that caches a DOM document to | |
1490 * generate elements. | |
1491 */ | |
1492 _xmlGenerator: null, | |
1493 | |
1494 /** PrivateFunction: _makeGenerator | |
1495 * _Private_ function that creates a dummy XML DOM document to serve as | |
1496 * an element and text node generator. | |
1497 */ | |
1498 _makeGenerator: function _makeGenerator() { | |
1499 var doc; // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload. | |
1500 // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be | |
1501 // less than 10 in the case of IE9 and below. | |
1502 | |
1503 if (document.implementation.createDocument === undefined || document.implementation.createDocument && document.documentMode && document.documentMode < 10) { | |
1504 doc = this._getIEXmlDom(); | |
1505 doc.appendChild(doc.createElement('strophe')); | |
1506 } else { | |
1507 doc = document.implementation.createDocument('jabber:client', 'strophe', null); | |
1508 } | |
1509 | |
1510 return doc; | |
1511 }, | |
1512 | |
1513 /** Function: xmlGenerator | |
1514 * Get the DOM document to generate elements. | |
1515 * | |
1516 * Returns: | |
1517 * The currently used DOM document. | |
1518 */ | |
1519 xmlGenerator: function xmlGenerator() { | |
1520 if (!Strophe._xmlGenerator) { | |
1521 Strophe._xmlGenerator = Strophe._makeGenerator(); | |
1522 } | |
1523 | |
1524 return Strophe._xmlGenerator; | |
1525 }, | |
1526 | |
1527 /** PrivateFunction: _getIEXmlDom | |
1528 * Gets IE xml doc object | |
1529 * | |
1530 * Returns: | |
1531 * A Microsoft XML DOM Object | |
1532 * See Also: | |
1533 * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx | |
1534 */ | |
1535 _getIEXmlDom: function _getIEXmlDom() { | |
1536 var doc = null; | |
1537 var docStrings = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]; | |
1538 | |
1539 for (var d = 0; d < docStrings.length; d++) { | |
1540 if (doc === null) { | |
1541 try { | |
1542 doc = new ActiveXObject(docStrings[d]); | |
1543 } catch (e) { | |
1544 doc = null; | |
1545 } | |
1546 } else { | |
1547 break; | |
1548 } | |
1549 } | |
1550 | |
1551 return doc; | |
1552 }, | |
1553 | |
1554 /** Function: xmlElement | |
1555 * Create an XML DOM element. | |
1556 * | |
1557 * This function creates an XML DOM element correctly across all | |
1558 * implementations. Note that these are not HTML DOM elements, which | |
1559 * aren't appropriate for XMPP stanzas. | |
1560 * | |
1561 * Parameters: | |
1562 * (String) name - The name for the element. | |
1563 * (Array|Object) attrs - An optional array or object containing | |
1564 * key/value pairs to use as element attributes. The object should | |
1565 * be in the format {'key': 'value'} or {key: 'value'}. The array | |
1566 * should have the format [['key1', 'value1'], ['key2', 'value2']]. | |
1567 * (String) text - The text child data for the element. | |
1568 * | |
1569 * Returns: | |
1570 * A new XML DOM element. | |
1571 */ | |
1572 xmlElement: function xmlElement(name) { | |
1573 if (!name) { | |
1574 return null; | |
1575 } | |
1576 | |
1577 var node = Strophe.xmlGenerator().createElement(name); // FIXME: this should throw errors if args are the wrong type or | |
1578 // there are more than two optional args | |
1579 | |
1580 for (var a = 1; a < arguments.length; a++) { | |
1581 var arg = arguments[a]; | |
1582 | |
1583 if (!arg) { | |
1584 continue; | |
1585 } | |
1586 | |
1587 if (typeof arg === "string" || typeof arg === "number") { | |
1588 node.appendChild(Strophe.xmlTextNode(arg)); | |
1589 } else if (_typeof(arg) === "object" && typeof arg.sort === "function") { | |
1590 for (var i = 0; i < arg.length; i++) { | |
1591 var attr = arg[i]; | |
1592 | |
1593 if (_typeof(attr) === "object" && typeof attr.sort === "function" && attr[1] !== undefined && attr[1] !== null) { | |
1594 node.setAttribute(attr[0], attr[1]); | |
1595 } | |
1596 } | |
1597 } else if (_typeof(arg) === "object") { | |
1598 for (var k in arg) { | |
1599 if (Object.prototype.hasOwnProperty.call(arg, k) && arg[k] !== undefined && arg[k] !== null) { | |
1600 node.setAttribute(k, arg[k]); | |
1601 } | |
1602 } | |
1603 } | |
1604 } | |
1605 | |
1606 return node; | |
1607 }, | |
1608 | |
1609 /* Function: xmlescape | |
1610 * Excapes invalid xml characters. | |
1611 * | |
1612 * Parameters: | |
1613 * (String) text - text to escape. | |
1614 * | |
1615 * Returns: | |
1616 * Escaped text. | |
1617 */ | |
1618 xmlescape: function xmlescape(text) { | |
1619 text = text.replace(/\&/g, "&"); | |
1620 text = text.replace(/</g, "<"); | |
1621 text = text.replace(/>/g, ">"); | |
1622 text = text.replace(/'/g, "'"); | |
1623 text = text.replace(/"/g, """); | |
1624 return text; | |
1625 }, | |
1626 | |
1627 /* Function: xmlunescape | |
1628 * Unexcapes invalid xml characters. | |
1629 * | |
1630 * Parameters: | |
1631 * (String) text - text to unescape. | |
1632 * | |
1633 * Returns: | |
1634 * Unescaped text. | |
1635 */ | |
1636 xmlunescape: function xmlunescape(text) { | |
1637 text = text.replace(/\&/g, "&"); | |
1638 text = text.replace(/</g, "<"); | |
1639 text = text.replace(/>/g, ">"); | |
1640 text = text.replace(/'/g, "'"); | |
1641 text = text.replace(/"/g, "\""); | |
1642 return text; | |
1643 }, | |
1644 | |
1645 /** Function: xmlTextNode | |
1646 * Creates an XML DOM text node. | |
1647 * | |
1648 * Provides a cross implementation version of document.createTextNode. | |
1649 * | |
1650 * Parameters: | |
1651 * (String) text - The content of the text node. | |
1652 * | |
1653 * Returns: | |
1654 * A new XML DOM text node. | |
1655 */ | |
1656 xmlTextNode: function xmlTextNode(text) { | |
1657 return Strophe.xmlGenerator().createTextNode(text); | |
1658 }, | |
1659 | |
1660 /** Function: xmlHtmlNode | |
1661 * Creates an XML DOM html node. | |
1662 * | |
1663 * Parameters: | |
1664 * (String) html - The content of the html node. | |
1665 * | |
1666 * Returns: | |
1667 * A new XML DOM text node. | |
1668 */ | |
1669 xmlHtmlNode: function xmlHtmlNode(html) { | |
1670 var node; //ensure text is escaped | |
1671 | |
1672 if (DOMParser) { | |
1673 var parser = new DOMParser(); | |
1674 node = parser.parseFromString(html, "text/xml"); | |
1675 } else { | |
1676 node = new ActiveXObject("Microsoft.XMLDOM"); | |
1677 node.async = "false"; | |
1678 node.loadXML(html); | |
1679 } | |
1680 | |
1681 return node; | |
1682 }, | |
1683 | |
1684 /** Function: getText | |
1685 * Get the concatenation of all text children of an element. | |
1686 * | |
1687 * Parameters: | |
1688 * (XMLElement) elem - A DOM element. | |
1689 * | |
1690 * Returns: | |
1691 * A String with the concatenated text of all text element children. | |
1692 */ | |
1693 getText: function getText(elem) { | |
1694 if (!elem) { | |
1695 return null; | |
1696 } | |
1697 | |
1698 var str = ""; | |
1699 | |
1700 if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) { | |
1701 str += elem.nodeValue; | |
1702 } | |
1703 | |
1704 for (var i = 0; i < elem.childNodes.length; i++) { | |
1705 if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) { | |
1706 str += elem.childNodes[i].nodeValue; | |
1707 } | |
1708 } | |
1709 | |
1710 return Strophe.xmlescape(str); | |
1711 }, | |
1712 | |
1713 /** Function: copyElement | |
1714 * Copy an XML DOM element. | |
1715 * | |
1716 * This function copies a DOM element and all its descendants and returns | |
1717 * the new copy. | |
1718 * | |
1719 * Parameters: | |
1720 * (XMLElement) elem - A DOM element. | |
1721 * | |
1722 * Returns: | |
1723 * A new, copied DOM element tree. | |
1724 */ | |
1725 copyElement: function copyElement(elem) { | |
1726 var el; | |
1727 | |
1728 if (elem.nodeType === Strophe.ElementType.NORMAL) { | |
1729 el = Strophe.xmlElement(elem.tagName); | |
1730 | |
1731 for (var i = 0; i < elem.attributes.length; i++) { | |
1732 el.setAttribute(elem.attributes[i].nodeName, elem.attributes[i].value); | |
1733 } | |
1734 | |
1735 for (var _i = 0; _i < elem.childNodes.length; _i++) { | |
1736 el.appendChild(Strophe.copyElement(elem.childNodes[_i])); | |
1737 } | |
1738 } else if (elem.nodeType === Strophe.ElementType.TEXT) { | |
1739 el = Strophe.xmlGenerator().createTextNode(elem.nodeValue); | |
1740 } | |
1741 | |
1742 return el; | |
1743 }, | |
1744 | |
1745 /** Function: createHtml | |
1746 * Copy an HTML DOM element into an XML DOM. | |
1747 * | |
1748 * This function copies a DOM element and all its descendants and returns | |
1749 * the new copy. | |
1750 * | |
1751 * Parameters: | |
1752 * (HTMLElement) elem - A DOM element. | |
1753 * | |
1754 * Returns: | |
1755 * A new, copied DOM element tree. | |
1756 */ | |
1757 createHtml: function createHtml(elem) { | |
1758 var el; | |
1759 | |
1760 if (elem.nodeType === Strophe.ElementType.NORMAL) { | |
1761 var tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case. | |
1762 | |
1763 if (Strophe.XHTML.validTag(tag)) { | |
1764 try { | |
1765 el = Strophe.xmlElement(tag); | |
1766 | |
1767 for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) { | |
1768 var attribute = Strophe.XHTML.attributes[tag][i]; | |
1769 var value = elem.getAttribute(attribute); | |
1770 | |
1771 if (typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) { | |
1772 continue; | |
1773 } | |
1774 | |
1775 if (attribute === 'style' && _typeof(value) === 'object' && typeof value.cssText !== 'undefined') { | |
1776 value = value.cssText; // we're dealing with IE, need to get CSS out | |
1777 } // filter out invalid css styles | |
1778 | |
1779 | |
1780 if (attribute === 'style') { | |
1781 var css = []; | |
1782 var cssAttrs = value.split(';'); | |
1783 | |
1784 for (var j = 0; j < cssAttrs.length; j++) { | |
1785 var attr = cssAttrs[j].split(':'); | |
1786 var cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase(); | |
1787 | |
1788 if (Strophe.XHTML.validCSS(cssName)) { | |
1789 var cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, ""); | |
1790 css.push(cssName + ': ' + cssValue); | |
1791 } | |
1792 } | |
1793 | |
1794 if (css.length > 0) { | |
1795 value = css.join('; '); | |
1796 el.setAttribute(attribute, value); | |
1797 } | |
1798 } else { | |
1799 el.setAttribute(attribute, value); | |
1800 } | |
1801 } | |
1802 | |
1803 for (var _i2 = 0; _i2 < elem.childNodes.length; _i2++) { | |
1804 el.appendChild(Strophe.createHtml(elem.childNodes[_i2])); | |
1805 } | |
1806 } catch (e) { | |
1807 // invalid elements | |
1808 el = Strophe.xmlTextNode(''); | |
1809 } | |
1810 } else { | |
1811 el = Strophe.xmlGenerator().createDocumentFragment(); | |
1812 | |
1813 for (var _i3 = 0; _i3 < elem.childNodes.length; _i3++) { | |
1814 el.appendChild(Strophe.createHtml(elem.childNodes[_i3])); | |
1815 } | |
1816 } | |
1817 } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) { | |
1818 el = Strophe.xmlGenerator().createDocumentFragment(); | |
1819 | |
1820 for (var _i4 = 0; _i4 < elem.childNodes.length; _i4++) { | |
1821 el.appendChild(Strophe.createHtml(elem.childNodes[_i4])); | |
1822 } | |
1823 } else if (elem.nodeType === Strophe.ElementType.TEXT) { | |
1824 el = Strophe.xmlTextNode(elem.nodeValue); | |
1825 } | |
1826 | |
1827 return el; | |
1828 }, | |
1829 | |
1830 /** Function: escapeNode | |
1831 * Escape the node part (also called local part) of a JID. | |
1832 * | |
1833 * Parameters: | |
1834 * (String) node - A node (or local part). | |
1835 * | |
1836 * Returns: | |
1837 * An escaped node (or local part). | |
1838 */ | |
1839 escapeNode: function escapeNode(node) { | |
1840 if (typeof node !== "string") { | |
1841 return node; | |
1842 } | |
1843 | |
1844 return node.replace(/^\s+|\s+$/g, '').replace(/\\/g, "\\5c").replace(/ /g, "\\20").replace(/\"/g, "\\22").replace(/\&/g, "\\26").replace(/\'/g, "\\27").replace(/\//g, "\\2f").replace(/:/g, "\\3a").replace(/</g, "\\3c").replace(/>/g, "\\3e").replace(/@/g, "\\40"); | |
1845 }, | |
1846 | |
1847 /** Function: unescapeNode | |
1848 * Unescape a node part (also called local part) of a JID. | |
1849 * | |
1850 * Parameters: | |
1851 * (String) node - A node (or local part). | |
1852 * | |
1853 * Returns: | |
1854 * An unescaped node (or local part). | |
1855 */ | |
1856 unescapeNode: function unescapeNode(node) { | |
1857 if (typeof node !== "string") { | |
1858 return node; | |
1859 } | |
1860 | |
1861 return node.replace(/\\20/g, " ").replace(/\\22/g, '"').replace(/\\26/g, "&").replace(/\\27/g, "'").replace(/\\2f/g, "/").replace(/\\3a/g, ":").replace(/\\3c/g, "<").replace(/\\3e/g, ">").replace(/\\40/g, "@").replace(/\\5c/g, "\\"); | |
1862 }, | |
1863 | |
1864 /** Function: getNodeFromJid | |
1865 * Get the node portion of a JID String. | |
1866 * | |
1867 * Parameters: | |
1868 * (String) jid - A JID. | |
1869 * | |
1870 * Returns: | |
1871 * A String containing the node. | |
1872 */ | |
1873 getNodeFromJid: function getNodeFromJid(jid) { | |
1874 if (jid.indexOf("@") < 0) { | |
1875 return null; | |
1876 } | |
1877 | |
1878 return jid.split("@")[0]; | |
1879 }, | |
1880 | |
1881 /** Function: getDomainFromJid | |
1882 * Get the domain portion of a JID String. | |
1883 * | |
1884 * Parameters: | |
1885 * (String) jid - A JID. | |
1886 * | |
1887 * Returns: | |
1888 * A String containing the domain. | |
1889 */ | |
1890 getDomainFromJid: function getDomainFromJid(jid) { | |
1891 var bare = Strophe.getBareJidFromJid(jid); | |
1892 | |
1893 if (bare.indexOf("@") < 0) { | |
1894 return bare; | |
1895 } else { | |
1896 var parts = bare.split("@"); | |
1897 parts.splice(0, 1); | |
1898 return parts.join('@'); | |
1899 } | |
1900 }, | |
1901 | |
1902 /** Function: getResourceFromJid | |
1903 * Get the resource portion of a JID String. | |
1904 * | |
1905 * Parameters: | |
1906 * (String) jid - A JID. | |
1907 * | |
1908 * Returns: | |
1909 * A String containing the resource. | |
1910 */ | |
1911 getResourceFromJid: function getResourceFromJid(jid) { | |
1912 var s = jid.split("/"); | |
1913 | |
1914 if (s.length < 2) { | |
1915 return null; | |
1916 } | |
1917 | |
1918 s.splice(0, 1); | |
1919 return s.join('/'); | |
1920 }, | |
1921 | |
1922 /** Function: getBareJidFromJid | |
1923 * Get the bare JID from a JID String. | |
1924 * | |
1925 * Parameters: | |
1926 * (String) jid - A JID. | |
1927 * | |
1928 * Returns: | |
1929 * A String containing the bare JID. | |
1930 */ | |
1931 getBareJidFromJid: function getBareJidFromJid(jid) { | |
1932 return jid ? jid.split("/")[0] : null; | |
1933 }, | |
1934 | |
1935 /** PrivateFunction: _handleError | |
1936 * _Private_ function that properly logs an error to the console | |
1937 */ | |
1938 _handleError: function _handleError(e) { | |
1939 if (typeof e.stack !== "undefined") { | |
1940 Strophe.fatal(e.stack); | |
1941 } | |
1942 | |
1943 if (e.sourceURL) { | |
1944 Strophe.fatal("error: " + this.handler + " " + e.sourceURL + ":" + e.line + " - " + e.name + ": " + e.message); | |
1945 } else if (e.fileName) { | |
1946 Strophe.fatal("error: " + this.handler + " " + e.fileName + ":" + e.lineNumber + " - " + e.name + ": " + e.message); | |
1947 } else { | |
1948 Strophe.fatal("error: " + e.message); | |
1949 } | |
1950 }, | |
1951 | |
1952 /** Function: log | |
1953 * User overrideable logging function. | |
1954 * | |
1955 * This function is called whenever the Strophe library calls any | |
1956 * of the logging functions. The default implementation of this | |
1957 * function logs only fatal errors. If client code wishes to handle the logging | |
1958 * messages, it should override this with | |
1959 * > Strophe.log = function (level, msg) { | |
1960 * > (user code here) | |
1961 * > }; | |
1962 * | |
1963 * Please note that data sent and received over the wire is logged | |
1964 * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput(). | |
1965 * | |
1966 * The different levels and their meanings are | |
1967 * | |
1968 * DEBUG - Messages useful for debugging purposes. | |
1969 * INFO - Informational messages. This is mostly information like | |
1970 * 'disconnect was called' or 'SASL auth succeeded'. | |
1971 * WARN - Warnings about potential problems. This is mostly used | |
1972 * to report transient connection errors like request timeouts. | |
1973 * ERROR - Some error occurred. | |
1974 * FATAL - A non-recoverable fatal error occurred. | |
1975 * | |
1976 * Parameters: | |
1977 * (Integer) level - The log level of the log message. This will | |
1978 * be one of the values in Strophe.LogLevel. | |
1979 * (String) msg - The log message. | |
1980 */ | |
1981 log: function log(level, msg) { | |
1982 if (level === this.LogLevel.FATAL && _typeof(window.console) === 'object' && typeof window.console.error === 'function') { | |
1983 window.console.error(msg); | |
1984 } | |
1985 }, | |
1986 | |
1987 /** Function: debug | |
1988 * Log a message at the Strophe.LogLevel.DEBUG level. | |
1989 * | |
1990 * Parameters: | |
1991 * (String) msg - The log message. | |
1992 */ | |
1993 debug: function debug(msg) { | |
1994 this.log(this.LogLevel.DEBUG, msg); | |
1995 }, | |
1996 | |
1997 /** Function: info | |
1998 * Log a message at the Strophe.LogLevel.INFO level. | |
1999 * | |
2000 * Parameters: | |
2001 * (String) msg - The log message. | |
2002 */ | |
2003 info: function info(msg) { | |
2004 this.log(this.LogLevel.INFO, msg); | |
2005 }, | |
2006 | |
2007 /** Function: warn | |
2008 * Log a message at the Strophe.LogLevel.WARN level. | |
2009 * | |
2010 * Parameters: | |
2011 * (String) msg - The log message. | |
2012 */ | |
2013 warn: function warn(msg) { | |
2014 this.log(this.LogLevel.WARN, msg); | |
2015 }, | |
2016 | |
2017 /** Function: error | |
2018 * Log a message at the Strophe.LogLevel.ERROR level. | |
2019 * | |
2020 * Parameters: | |
2021 * (String) msg - The log message. | |
2022 */ | |
2023 error: function error(msg) { | |
2024 this.log(this.LogLevel.ERROR, msg); | |
2025 }, | |
2026 | |
2027 /** Function: fatal | |
2028 * Log a message at the Strophe.LogLevel.FATAL level. | |
2029 * | |
2030 * Parameters: | |
2031 * (String) msg - The log message. | |
2032 */ | |
2033 fatal: function fatal(msg) { | |
2034 this.log(this.LogLevel.FATAL, msg); | |
2035 }, | |
2036 | |
2037 /** Function: serialize | |
2038 * Render a DOM element and all descendants to a String. | |
2039 * | |
2040 * Parameters: | |
2041 * (XMLElement) elem - A DOM element. | |
2042 * | |
2043 * Returns: | |
2044 * The serialized element tree as a String. | |
2045 */ | |
2046 serialize: function serialize(elem) { | |
2047 if (!elem) { | |
2048 return null; | |
2049 } | |
2050 | |
2051 if (typeof elem.tree === "function") { | |
2052 elem = elem.tree(); | |
2053 } | |
2054 | |
2055 var names = _toConsumableArray(Array(elem.attributes.length).keys()).map(function (i) { | |
2056 return elem.attributes[i].nodeName; | |
2057 }); | |
2058 | |
2059 names.sort(); | |
2060 var result = names.reduce(function (a, n) { | |
2061 return "".concat(a, " ").concat(n, "=\"").concat(Strophe.xmlescape(elem.attributes.getNamedItem(n).value), "\""); | |
2062 }, "<".concat(elem.nodeName)); | |
2063 | |
2064 if (elem.childNodes.length > 0) { | |
2065 result += ">"; | |
2066 | |
2067 for (var i = 0; i < elem.childNodes.length; i++) { | |
2068 var child = elem.childNodes[i]; | |
2069 | |
2070 switch (child.nodeType) { | |
2071 case Strophe.ElementType.NORMAL: | |
2072 // normal element, so recurse | |
2073 result += Strophe.serialize(child); | |
2074 break; | |
2075 | |
2076 case Strophe.ElementType.TEXT: | |
2077 // text element to escape values | |
2078 result += Strophe.xmlescape(child.nodeValue); | |
2079 break; | |
2080 | |
2081 case Strophe.ElementType.CDATA: | |
2082 // cdata section so don't escape values | |
2083 result += "<![CDATA[" + child.nodeValue + "]]>"; | |
2084 } | |
2085 } | |
2086 | |
2087 result += "</" + elem.nodeName + ">"; | |
2088 } else { | |
2089 result += "/>"; | |
2090 } | |
2091 | |
2092 return result; | |
2093 }, | |
2094 | |
2095 /** PrivateVariable: _requestId | |
2096 * _Private_ variable that keeps track of the request ids for | |
2097 * connections. | |
2098 */ | |
2099 _requestId: 0, | |
2100 | |
2101 /** PrivateVariable: Strophe.connectionPlugins | |
2102 * _Private_ variable Used to store plugin names that need | |
2103 * initialization on Strophe.Connection construction. | |
2104 */ | |
2105 _connectionPlugins: {}, | |
2106 | |
2107 /** Function: addConnectionPlugin | |
2108 * Extends the Strophe.Connection object with the given plugin. | |
2109 * | |
2110 * Parameters: | |
2111 * (String) name - The name of the extension. | |
2112 * (Object) ptype - The plugin's prototype. | |
2113 */ | |
2114 addConnectionPlugin: function addConnectionPlugin(name, ptype) { | |
2115 Strophe._connectionPlugins[name] = ptype; | |
2116 } | |
2117 }; | |
2118 /** Class: Strophe.Builder | |
2119 * XML DOM builder. | |
2120 * | |
2121 * This object provides an interface similar to JQuery but for building | |
2122 * DOM elements easily and rapidly. All the functions except for toString() | |
2123 * and tree() return the object, so calls can be chained. Here's an | |
2124 * example using the $iq() builder helper. | |
2125 * > $iq({to: 'you', from: 'me', type: 'get', id: '1'}) | |
2126 * > .c('query', {xmlns: 'strophe:example'}) | |
2127 * > .c('example') | |
2128 * > .toString() | |
2129 * | |
2130 * The above generates this XML fragment | |
2131 * > <iq to='you' from='me' type='get' id='1'> | |
2132 * > <query xmlns='strophe:example'> | |
2133 * > <example/> | |
2134 * > </query> | |
2135 * > </iq> | |
2136 * The corresponding DOM manipulations to get a similar fragment would be | |
2137 * a lot more tedious and probably involve several helper variables. | |
2138 * | |
2139 * Since adding children makes new operations operate on the child, up() | |
2140 * is provided to traverse up the tree. To add two children, do | |
2141 * > builder.c('child1', ...).up().c('child2', ...) | |
2142 * The next operation on the Builder will be relative to the second child. | |
2143 */ | |
2144 | |
2145 /** Constructor: Strophe.Builder | |
2146 * Create a Strophe.Builder object. | |
2147 * | |
2148 * The attributes should be passed in object notation. For example | |
2149 * > let b = new Builder('message', {to: 'you', from: 'me'}); | |
2150 * or | |
2151 * > let b = new Builder('messsage', {'xml:lang': 'en'}); | |
2152 * | |
2153 * Parameters: | |
2154 * (String) name - The name of the root element. | |
2155 * (Object) attrs - The attributes for the root element in object notation. | |
2156 * | |
2157 * Returns: | |
2158 * A new Strophe.Builder. | |
2159 */ | |
2160 | |
2161 Strophe.Builder = function (name, attrs) { | |
2162 // Set correct namespace for jabber:client elements | |
2163 if (name === "presence" || name === "message" || name === "iq") { | |
2164 if (attrs && !attrs.xmlns) { | |
2165 attrs.xmlns = Strophe.NS.CLIENT; | |
2166 } else if (!attrs) { | |
2167 attrs = { | |
2168 xmlns: Strophe.NS.CLIENT | |
2169 }; | |
2170 } | |
2171 } // Holds the tree being built. | |
2172 | |
2173 | |
2174 this.nodeTree = Strophe.xmlElement(name, attrs); // Points to the current operation node. | |
2175 | |
2176 this.node = this.nodeTree; | |
2177 }; | |
2178 | |
2179 Strophe.Builder.prototype = { | |
2180 /** Function: tree | |
2181 * Return the DOM tree. | |
2182 * | |
2183 * This function returns the current DOM tree as an element object. This | |
2184 * is suitable for passing to functions like Strophe.Connection.send(). | |
2185 * | |
2186 * Returns: | |
2187 * The DOM tree as a element object. | |
2188 */ | |
2189 tree: function tree() { | |
2190 return this.nodeTree; | |
2191 }, | |
2192 | |
2193 /** Function: toString | |
2194 * Serialize the DOM tree to a String. | |
2195 * | |
2196 * This function returns a string serialization of the current DOM | |
2197 * tree. It is often used internally to pass data to a | |
2198 * Strophe.Request object. | |
2199 * | |
2200 * Returns: | |
2201 * The serialized DOM tree in a String. | |
2202 */ | |
2203 toString: function toString() { | |
2204 return Strophe.serialize(this.nodeTree); | |
2205 }, | |
2206 | |
2207 /** Function: up | |
2208 * Make the current parent element the new current element. | |
2209 * | |
2210 * This function is often used after c() to traverse back up the tree. | |
2211 * For example, to add two children to the same element | |
2212 * > builder.c('child1', {}).up().c('child2', {}); | |
2213 * | |
2214 * Returns: | |
2215 * The Stophe.Builder object. | |
2216 */ | |
2217 up: function up() { | |
2218 this.node = this.node.parentNode; | |
2219 return this; | |
2220 }, | |
2221 | |
2222 /** Function: root | |
2223 * Make the root element the new current element. | |
2224 * | |
2225 * When at a deeply nested element in the tree, this function can be used | |
2226 * to jump back to the root of the tree, instead of having to repeatedly | |
2227 * call up(). | |
2228 * | |
2229 * Returns: | |
2230 * The Stophe.Builder object. | |
2231 */ | |
2232 root: function root() { | |
2233 this.node = this.nodeTree; | |
2234 return this; | |
2235 }, | |
2236 | |
2237 /** Function: attrs | |
2238 * Add or modify attributes of the current element. | |
2239 * | |
2240 * The attributes should be passed in object notation. This function | |
2241 * does not move the current element pointer. | |
2242 * | |
2243 * Parameters: | |
2244 * (Object) moreattrs - The attributes to add/modify in object notation. | |
2245 * | |
2246 * Returns: | |
2247 * The Strophe.Builder object. | |
2248 */ | |
2249 attrs: function attrs(moreattrs) { | |
2250 for (var k in moreattrs) { | |
2251 if (Object.prototype.hasOwnProperty.call(moreattrs, k)) { | |
2252 if (moreattrs[k] === undefined) { | |
2253 this.node.removeAttribute(k); | |
2254 } else { | |
2255 this.node.setAttribute(k, moreattrs[k]); | |
2256 } | |
2257 } | |
2258 } | |
2259 | |
2260 return this; | |
2261 }, | |
2262 | |
2263 /** Function: c | |
2264 * Add a child to the current element and make it the new current | |
2265 * element. | |
2266 * | |
2267 * This function moves the current element pointer to the child, | |
2268 * unless text is provided. If you need to add another child, it | |
2269 * is necessary to use up() to go back to the parent in the tree. | |
2270 * | |
2271 * Parameters: | |
2272 * (String) name - The name of the child. | |
2273 * (Object) attrs - The attributes of the child in object notation. | |
2274 * (String) text - The text to add to the child. | |
2275 * | |
2276 * Returns: | |
2277 * The Strophe.Builder object. | |
2278 */ | |
2279 c: function c(name, attrs, text) { | |
2280 var child = Strophe.xmlElement(name, attrs, text); | |
2281 this.node.appendChild(child); | |
2282 | |
2283 if (typeof text !== "string" && typeof text !== "number") { | |
2284 this.node = child; | |
2285 } | |
2286 | |
2287 return this; | |
2288 }, | |
2289 | |
2290 /** Function: cnode | |
2291 * Add a child to the current element and make it the new current | |
2292 * element. | |
2293 * | |
2294 * This function is the same as c() except that instead of using a | |
2295 * name and an attributes object to create the child it uses an | |
2296 * existing DOM element object. | |
2297 * | |
2298 * Parameters: | |
2299 * (XMLElement) elem - A DOM element. | |
2300 * | |
2301 * Returns: | |
2302 * The Strophe.Builder object. | |
2303 */ | |
2304 cnode: function cnode(elem) { | |
2305 var impNode; | |
2306 var xmlGen = Strophe.xmlGenerator(); | |
2307 | |
2308 try { | |
2309 impNode = xmlGen.importNode !== undefined; | |
2310 } catch (e) { | |
2311 impNode = false; | |
2312 } | |
2313 | |
2314 var newElem = impNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem); | |
2315 this.node.appendChild(newElem); | |
2316 this.node = newElem; | |
2317 return this; | |
2318 }, | |
2319 | |
2320 /** Function: t | |
2321 * Add a child text element. | |
2322 * | |
2323 * This *does not* make the child the new current element since there | |
2324 * are no children of text elements. | |
2325 * | |
2326 * Parameters: | |
2327 * (String) text - The text data to append to the current element. | |
2328 * | |
2329 * Returns: | |
2330 * The Strophe.Builder object. | |
2331 */ | |
2332 t: function t(text) { | |
2333 var child = Strophe.xmlTextNode(text); | |
2334 this.node.appendChild(child); | |
2335 return this; | |
2336 }, | |
2337 | |
2338 /** Function: h | |
2339 * Replace current element contents with the HTML passed in. | |
2340 * | |
2341 * This *does not* make the child the new current element | |
2342 * | |
2343 * Parameters: | |
2344 * (String) html - The html to insert as contents of current element. | |
2345 * | |
2346 * Returns: | |
2347 * The Strophe.Builder object. | |
2348 */ | |
2349 h: function h(html) { | |
2350 var fragment = document.createElement('body'); // force the browser to try and fix any invalid HTML tags | |
2351 | |
2352 fragment.innerHTML = html; // copy cleaned html into an xml dom | |
2353 | |
2354 var xhtml = Strophe.createHtml(fragment); | |
2355 | |
2356 while (xhtml.childNodes.length > 0) { | |
2357 this.node.appendChild(xhtml.childNodes[0]); | |
2358 } | |
2359 | |
2360 return this; | |
2361 } | |
2362 }; | |
2363 /** PrivateClass: Strophe.Handler | |
2364 * _Private_ helper class for managing stanza handlers. | |
2365 * | |
2366 * A Strophe.Handler encapsulates a user provided callback function to be | |
2367 * executed when matching stanzas are received by the connection. | |
2368 * Handlers can be either one-off or persistant depending on their | |
2369 * return value. Returning true will cause a Handler to remain active, and | |
2370 * returning false will remove the Handler. | |
2371 * | |
2372 * Users will not use Strophe.Handler objects directly, but instead they | |
2373 * will use Strophe.Connection.addHandler() and | |
2374 * Strophe.Connection.deleteHandler(). | |
2375 */ | |
2376 | |
2377 /** PrivateConstructor: Strophe.Handler | |
2378 * Create and initialize a new Strophe.Handler. | |
2379 * | |
2380 * Parameters: | |
2381 * (Function) handler - A function to be executed when the handler is run. | |
2382 * (String) ns - The namespace to match. | |
2383 * (String) name - The element name to match. | |
2384 * (String) type - The element type to match. | |
2385 * (String) id - The element id attribute to match. | |
2386 * (String) from - The element from attribute to match. | |
2387 * (Object) options - Handler options | |
2388 * | |
2389 * Returns: | |
2390 * A new Strophe.Handler object. | |
2391 */ | |
2392 | |
2393 Strophe.Handler = function (handler, ns, name, type, id, from, options) { | |
2394 this.handler = handler; | |
2395 this.ns = ns; | |
2396 this.name = name; | |
2397 this.type = type; | |
2398 this.id = id; | |
2399 this.options = options || { | |
2400 'matchBareFromJid': false, | |
2401 'ignoreNamespaceFragment': false | |
2402 }; // BBB: Maintain backward compatibility with old `matchBare` option | |
2403 | |
2404 if (this.options.matchBare) { | |
2405 Strophe.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.'); | |
2406 this.options.matchBareFromJid = this.options.matchBare; | |
2407 delete this.options.matchBare; | |
2408 } | |
2409 | |
2410 if (this.options.matchBareFromJid) { | |
2411 this.from = from ? Strophe.getBareJidFromJid(from) : null; | |
2412 } else { | |
2413 this.from = from; | |
2414 } // whether the handler is a user handler or a system handler | |
2415 | |
2416 | |
2417 this.user = true; | |
2418 }; | |
2419 | |
2420 Strophe.Handler.prototype = { | |
2421 /** PrivateFunction: getNamespace | |
2422 * Returns the XML namespace attribute on an element. | |
2423 * If `ignoreNamespaceFragment` was passed in for this handler, then the | |
2424 * URL fragment will be stripped. | |
2425 * | |
2426 * Parameters: | |
2427 * (XMLElement) elem - The XML element with the namespace. | |
2428 * | |
2429 * Returns: | |
2430 * The namespace, with optionally the fragment stripped. | |
2431 */ | |
2432 getNamespace: function getNamespace(elem) { | |
2433 var elNamespace = elem.getAttribute("xmlns"); | |
2434 | |
2435 if (elNamespace && this.options.ignoreNamespaceFragment) { | |
2436 elNamespace = elNamespace.split('#')[0]; | |
2437 } | |
2438 | |
2439 return elNamespace; | |
2440 }, | |
2441 | |
2442 /** PrivateFunction: namespaceMatch | |
2443 * Tests if a stanza matches the namespace set for this Strophe.Handler. | |
2444 * | |
2445 * Parameters: | |
2446 * (XMLElement) elem - The XML element to test. | |
2447 * | |
2448 * Returns: | |
2449 * true if the stanza matches and false otherwise. | |
2450 */ | |
2451 namespaceMatch: function namespaceMatch(elem) { | |
2452 var _this = this; | |
2453 | |
2454 var nsMatch = false; | |
2455 | |
2456 if (!this.ns) { | |
2457 return true; | |
2458 } else { | |
2459 Strophe.forEachChild(elem, null, function (elem) { | |
2460 if (_this.getNamespace(elem) === _this.ns) { | |
2461 nsMatch = true; | |
2462 } | |
2463 }); | |
2464 return nsMatch || this.getNamespace(elem) === this.ns; | |
2465 } | |
2466 }, | |
2467 | |
2468 /** PrivateFunction: isMatch | |
2469 * Tests if a stanza matches the Strophe.Handler. | |
2470 * | |
2471 * Parameters: | |
2472 * (XMLElement) elem - The XML element to test. | |
2473 * | |
2474 * Returns: | |
2475 * true if the stanza matches and false otherwise. | |
2476 */ | |
2477 isMatch: function isMatch(elem) { | |
2478 var from = elem.getAttribute('from'); | |
2479 | |
2480 if (this.options.matchBareFromJid) { | |
2481 from = Strophe.getBareJidFromJid(from); | |
2482 } | |
2483 | |
2484 var elem_type = elem.getAttribute("type"); | |
2485 | |
2486 if (this.namespaceMatch(elem) && (!this.name || Strophe.isTagEqual(elem, this.name)) && (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) && (!this.id || elem.getAttribute("id") === this.id) && (!this.from || from === this.from)) { | |
2487 return true; | |
2488 } | |
2489 | |
2490 return false; | |
2491 }, | |
2492 | |
2493 /** PrivateFunction: run | |
2494 * Run the callback on a matching stanza. | |
2495 * | |
2496 * Parameters: | |
2497 * (XMLElement) elem - The DOM element that triggered the | |
2498 * Strophe.Handler. | |
2499 * | |
2500 * Returns: | |
2501 * A boolean indicating if the handler should remain active. | |
2502 */ | |
2503 run: function run(elem) { | |
2504 var result = null; | |
2505 | |
2506 try { | |
2507 result = this.handler(elem); | |
2508 } catch (e) { | |
2509 Strophe._handleError(e); | |
2510 | |
2511 throw e; | |
2512 } | |
2513 | |
2514 return result; | |
2515 }, | |
2516 | |
2517 /** PrivateFunction: toString | |
2518 * Get a String representation of the Strophe.Handler object. | |
2519 * | |
2520 * Returns: | |
2521 * A String. | |
2522 */ | |
2523 toString: function toString() { | |
2524 return "{Handler: " + this.handler + "(" + this.name + "," + this.id + "," + this.ns + ")}"; | |
2525 } | |
2526 }; | |
2527 /** PrivateClass: Strophe.TimedHandler | |
2528 * _Private_ helper class for managing timed handlers. | |
2529 * | |
2530 * A Strophe.TimedHandler encapsulates a user provided callback that | |
2531 * should be called after a certain period of time or at regular | |
2532 * intervals. The return value of the callback determines whether the | |
2533 * Strophe.TimedHandler will continue to fire. | |
2534 * | |
2535 * Users will not use Strophe.TimedHandler objects directly, but instead | |
2536 * they will use Strophe.Connection.addTimedHandler() and | |
2537 * Strophe.Connection.deleteTimedHandler(). | |
2538 */ | |
2539 | |
2540 /** PrivateConstructor: Strophe.TimedHandler | |
2541 * Create and initialize a new Strophe.TimedHandler object. | |
2542 * | |
2543 * Parameters: | |
2544 * (Integer) period - The number of milliseconds to wait before the | |
2545 * handler is called. | |
2546 * (Function) handler - The callback to run when the handler fires. This | |
2547 * function should take no arguments. | |
2548 * | |
2549 * Returns: | |
2550 * A new Strophe.TimedHandler object. | |
2551 */ | |
2552 | |
2553 Strophe.TimedHandler = function (period, handler) { | |
2554 this.period = period; | |
2555 this.handler = handler; | |
2556 this.lastCalled = new Date().getTime(); | |
2557 this.user = true; | |
2558 }; | |
2559 | |
2560 Strophe.TimedHandler.prototype = { | |
2561 /** PrivateFunction: run | |
2562 * Run the callback for the Strophe.TimedHandler. | |
2563 * | |
2564 * Returns: | |
2565 * true if the Strophe.TimedHandler should be called again, and false | |
2566 * otherwise. | |
2567 */ | |
2568 run: function run() { | |
2569 this.lastCalled = new Date().getTime(); | |
2570 return this.handler(); | |
2571 }, | |
2572 | |
2573 /** PrivateFunction: reset | |
2574 * Reset the last called time for the Strophe.TimedHandler. | |
2575 */ | |
2576 reset: function reset() { | |
2577 this.lastCalled = new Date().getTime(); | |
2578 }, | |
2579 | |
2580 /** PrivateFunction: toString | |
2581 * Get a string representation of the Strophe.TimedHandler object. | |
2582 * | |
2583 * Returns: | |
2584 * The string representation. | |
2585 */ | |
2586 toString: function toString() { | |
2587 return "{TimedHandler: " + this.handler + "(" + this.period + ")}"; | |
2588 } | |
2589 }; | |
2590 /** Class: Strophe.Connection | |
2591 * XMPP Connection manager. | |
2592 * | |
2593 * This class is the main part of Strophe. It manages a BOSH or websocket | |
2594 * connection to an XMPP server and dispatches events to the user callbacks | |
2595 * as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1 | |
2596 * and legacy authentication. | |
2597 * | |
2598 * After creating a Strophe.Connection object, the user will typically | |
2599 * call connect() with a user supplied callback to handle connection level | |
2600 * events like authentication failure, disconnection, or connection | |
2601 * complete. | |
2602 * | |
2603 * The user will also have several event handlers defined by using | |
2604 * addHandler() and addTimedHandler(). These will allow the user code to | |
2605 * respond to interesting stanzas or do something periodically with the | |
2606 * connection. These handlers will be active once authentication is | |
2607 * finished. | |
2608 * | |
2609 * To send data to the connection, use send(). | |
2610 */ | |
2611 | |
2612 /** Constructor: Strophe.Connection | |
2613 * Create and initialize a Strophe.Connection object. | |
2614 * | |
2615 * The transport-protocol for this connection will be chosen automatically | |
2616 * based on the given service parameter. URLs starting with "ws://" or | |
2617 * "wss://" will use WebSockets, URLs starting with "http://", "https://" | |
2618 * or without a protocol will use BOSH. | |
2619 * | |
2620 * To make Strophe connect to the current host you can leave out the protocol | |
2621 * and host part and just pass the path, e.g. | |
2622 * | |
2623 * > let conn = new Strophe.Connection("/http-bind/"); | |
2624 * | |
2625 * Options common to both Websocket and BOSH: | |
2626 * ------------------------------------------ | |
2627 * | |
2628 * cookies: | |
2629 * | |
2630 * The *cookies* option allows you to pass in cookies to be added to the | |
2631 * document. These cookies will then be included in the BOSH XMLHttpRequest | |
2632 * or in the websocket connection. | |
2633 * | |
2634 * The passed in value must be a map of cookie names and string values. | |
2635 * | |
2636 * > { "myCookie": { | |
2637 * > "value": "1234", | |
2638 * > "domain": ".example.org", | |
2639 * > "path": "/", | |
2640 * > "expires": expirationDate | |
2641 * > } | |
2642 * > } | |
2643 * | |
2644 * Note that cookies can't be set in this way for other domains (i.e. cross-domain). | |
2645 * Those cookies need to be set under those domains, for example they can be | |
2646 * set server-side by making a XHR call to that domain to ask it to set any | |
2647 * necessary cookies. | |
2648 * | |
2649 * mechanisms: | |
2650 * | |
2651 * The *mechanisms* option allows you to specify the SASL mechanisms that this | |
2652 * instance of Strophe.Connection (and therefore your XMPP client) will | |
2653 * support. | |
2654 * | |
2655 * The value must be an array of objects with Strophe.SASLMechanism | |
2656 * prototypes. | |
2657 * | |
2658 * If nothing is specified, then the following mechanisms (and their | |
2659 * priorities) are registered: | |
2660 * | |
2661 * SCRAM-SHA1 - 70 | |
2662 * DIGEST-MD5 - 60 | |
2663 * PLAIN - 50 | |
2664 * OAUTH-BEARER - 40 | |
2665 * OAUTH-2 - 30 | |
2666 * ANONYMOUS - 20 | |
2667 * EXTERNAL - 10 | |
2668 * | |
2669 * WebSocket options: | |
2670 * ------------------ | |
2671 * | |
2672 * If you want to connect to the current host with a WebSocket connection you | |
2673 * can tell Strophe to use WebSockets through a "protocol" attribute in the | |
2674 * optional options parameter. Valid values are "ws" for WebSocket and "wss" | |
2675 * for Secure WebSocket. | |
2676 * So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call | |
2677 * | |
2678 * > let conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"}); | |
2679 * | |
2680 * Note that relative URLs _NOT_ starting with a "/" will also include the path | |
2681 * of the current site. | |
2682 * | |
2683 * Also because downgrading security is not permitted by browsers, when using | |
2684 * relative URLs both BOSH and WebSocket connections will use their secure | |
2685 * variants if the current connection to the site is also secure (https). | |
2686 * | |
2687 * BOSH options: | |
2688 * ------------- | |
2689 * | |
2690 * By adding "sync" to the options, you can control if requests will | |
2691 * be made synchronously or not. The default behaviour is asynchronous. | |
2692 * If you want to make requests synchronous, make "sync" evaluate to true. | |
2693 * > let conn = new Strophe.Connection("/http-bind/", {sync: true}); | |
2694 * | |
2695 * You can also toggle this on an already established connection. | |
2696 * > conn.options.sync = true; | |
2697 * | |
2698 * The *customHeaders* option can be used to provide custom HTTP headers to be | |
2699 * included in the XMLHttpRequests made. | |
2700 * | |
2701 * The *keepalive* option can be used to instruct Strophe to maintain the | |
2702 * current BOSH session across interruptions such as webpage reloads. | |
2703 * | |
2704 * It will do this by caching the sessions tokens in sessionStorage, and when | |
2705 * "restore" is called it will check whether there are cached tokens with | |
2706 * which it can resume an existing session. | |
2707 * | |
2708 * The *withCredentials* option should receive a Boolean value and is used to | |
2709 * indicate wether cookies should be included in ajax requests (by default | |
2710 * they're not). | |
2711 * Set this value to true if you are connecting to a BOSH service | |
2712 * and for some reason need to send cookies to it. | |
2713 * In order for this to work cross-domain, the server must also enable | |
2714 * credentials by setting the Access-Control-Allow-Credentials response header | |
2715 * to "true". For most usecases however this setting should be false (which | |
2716 * is the default). | |
2717 * Additionally, when using Access-Control-Allow-Credentials, the | |
2718 * Access-Control-Allow-Origin header can't be set to the wildcard "*", but | |
2719 * instead must be restricted to actual domains. | |
2720 * | |
2721 * The *contentType* option can be set to change the default Content-Type | |
2722 * of "text/xml; charset=utf-8", which can be useful to reduce the amount of | |
2723 * CORS preflight requests that are sent to the server. | |
2724 * | |
2725 * Parameters: | |
2726 * (String) service - The BOSH or WebSocket service URL. | |
2727 * (Object) options - A hash of configuration options | |
2728 * | |
2729 * Returns: | |
2730 * A new Strophe.Connection object. | |
2731 */ | |
2732 | |
2733 Strophe.Connection = function (service, options) { | |
2734 var _this2 = this; | |
2735 | |
2736 // The service URL | |
2737 this.service = service; // Configuration options | |
2738 | |
2739 this.options = options || {}; | |
2740 var proto = this.options.protocol || ""; // Select protocal based on service or options | |
2741 | |
2742 if (service.indexOf("ws:") === 0 || service.indexOf("wss:") === 0 || proto.indexOf("ws") === 0) { | |
2743 this._proto = new Strophe.Websocket(this); | |
2744 } else { | |
2745 this._proto = new Strophe.Bosh(this); | |
2746 } | |
2747 /* The connected JID. */ | |
2748 | |
2749 | |
2750 this.jid = ""; | |
2751 /* the JIDs domain */ | |
2752 | |
2753 this.domain = null; | |
2754 /* stream:features */ | |
2755 | |
2756 this.features = null; // SASL | |
2757 | |
2758 this._sasl_data = {}; | |
2759 this.do_session = false; | |
2760 this.do_bind = false; // handler lists | |
2761 | |
2762 this.timedHandlers = []; | |
2763 this.handlers = []; | |
2764 this.removeTimeds = []; | |
2765 this.removeHandlers = []; | |
2766 this.addTimeds = []; | |
2767 this.addHandlers = []; | |
2768 this.protocolErrorHandlers = { | |
2769 'HTTP': {}, | |
2770 'websocket': {} | |
2771 }; | |
2772 this._idleTimeout = null; | |
2773 this._disconnectTimeout = null; | |
2774 this.authenticated = false; | |
2775 this.connected = false; | |
2776 this.disconnecting = false; | |
2777 this.do_authentication = true; | |
2778 this.paused = false; | |
2779 this.restored = false; | |
2780 this._data = []; | |
2781 this._uniqueId = 0; | |
2782 this._sasl_success_handler = null; | |
2783 this._sasl_failure_handler = null; | |
2784 this._sasl_challenge_handler = null; // Max retries before disconnecting | |
2785 | |
2786 this.maxRetries = 5; // Call onIdle callback every 1/10th of a second | |
2787 | |
2788 this._idleTimeout = setTimeout(function () { | |
2789 return _this2._onIdle(); | |
2790 }, 100); | |
2791 utils__WEBPACK_IMPORTED_MODULE_2__["default"].addCookies(this.options.cookies); | |
2792 this.registerSASLMechanisms(this.options.mechanisms); // initialize plugins | |
2793 | |
2794 for (var k in Strophe._connectionPlugins) { | |
2795 if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) { | |
2796 var F = function F() {}; | |
2797 | |
2798 F.prototype = Strophe._connectionPlugins[k]; | |
2799 this[k] = new F(); | |
2800 this[k].init(this); | |
2801 } | |
2802 } | |
2803 }; | |
2804 | |
2805 Strophe.Connection.prototype = { | |
2806 /** Function: reset | |
2807 * Reset the connection. | |
2808 * | |
2809 * This function should be called after a connection is disconnected | |
2810 * before that connection is reused. | |
2811 */ | |
2812 reset: function reset() { | |
2813 this._proto._reset(); // SASL | |
2814 | |
2815 | |
2816 this.do_session = false; | |
2817 this.do_bind = false; // handler lists | |
2818 | |
2819 this.timedHandlers = []; | |
2820 this.handlers = []; | |
2821 this.removeTimeds = []; | |
2822 this.removeHandlers = []; | |
2823 this.addTimeds = []; | |
2824 this.addHandlers = []; | |
2825 this.authenticated = false; | |
2826 this.connected = false; | |
2827 this.disconnecting = false; | |
2828 this.restored = false; | |
2829 this._data = []; | |
2830 this._requests = []; | |
2831 this._uniqueId = 0; | |
2832 }, | |
2833 | |
2834 /** Function: pause | |
2835 * Pause the request manager. | |
2836 * | |
2837 * This will prevent Strophe from sending any more requests to the | |
2838 * server. This is very useful for temporarily pausing | |
2839 * BOSH-Connections while a lot of send() calls are happening quickly. | |
2840 * This causes Strophe to send the data in a single request, saving | |
2841 * many request trips. | |
2842 */ | |
2843 pause: function pause() { | |
2844 this.paused = true; | |
2845 }, | |
2846 | |
2847 /** Function: resume | |
2848 * Resume the request manager. | |
2849 * | |
2850 * This resumes after pause() has been called. | |
2851 */ | |
2852 resume: function resume() { | |
2853 this.paused = false; | |
2854 }, | |
2855 | |
2856 /** Function: getUniqueId | |
2857 * Generate a unique ID for use in <iq/> elements. | |
2858 * | |
2859 * All <iq/> stanzas are required to have unique id attributes. This | |
2860 * function makes creating these easy. Each connection instance has | |
2861 * a counter which starts from zero, and the value of this counter | |
2862 * plus a colon followed by the suffix becomes the unique id. If no | |
2863 * suffix is supplied, the counter is used as the unique id. | |
2864 * | |
2865 * Suffixes are used to make debugging easier when reading the stream | |
2866 * data, and their use is recommended. The counter resets to 0 for | |
2867 * every new connection for the same reason. For connections to the | |
2868 * same server that authenticate the same way, all the ids should be | |
2869 * the same, which makes it easy to see changes. This is useful for | |
2870 * automated testing as well. | |
2871 * | |
2872 * Parameters: | |
2873 * (String) suffix - A optional suffix to append to the id. | |
2874 * | |
2875 * Returns: | |
2876 * A unique string to be used for the id attribute. | |
2877 */ | |
2878 getUniqueId: function getUniqueId(suffix) { | |
2879 var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | |
2880 var r = Math.random() * 16 | 0, | |
2881 v = c === 'x' ? r : r & 0x3 | 0x8; | |
2882 return v.toString(16); | |
2883 }); | |
2884 | |
2885 if (typeof suffix === "string" || typeof suffix === "number") { | |
2886 return uuid + ":" + suffix; | |
2887 } else { | |
2888 return uuid + ""; | |
2889 } | |
2890 }, | |
2891 | |
2892 /** Function: addProtocolErrorHandler | |
2893 * Register a handler function for when a protocol (websocker or HTTP) | |
2894 * error occurs. | |
2895 * | |
2896 * NOTE: Currently only HTTP errors for BOSH requests are handled. | |
2897 * Patches that handle websocket errors would be very welcome. | |
2898 * | |
2899 * Parameters: | |
2900 * (String) protocol - 'HTTP' or 'websocket' | |
2901 * (Integer) status_code - Error status code (e.g 500, 400 or 404) | |
2902 * (Function) callback - Function that will fire on Http error | |
2903 * | |
2904 * Example: | |
2905 * function onError(err_code){ | |
2906 * //do stuff | |
2907 * } | |
2908 * | |
2909 * let conn = Strophe.connect('http://example.com/http-bind'); | |
2910 * conn.addProtocolErrorHandler('HTTP', 500, onError); | |
2911 * // Triggers HTTP 500 error and onError handler will be called | |
2912 * conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect); | |
2913 */ | |
2914 addProtocolErrorHandler: function addProtocolErrorHandler(protocol, status_code, callback) { | |
2915 this.protocolErrorHandlers[protocol][status_code] = callback; | |
2916 }, | |
2917 | |
2918 /** Function: connect | |
2919 * Starts the connection process. | |
2920 * | |
2921 * As the connection process proceeds, the user supplied callback will | |
2922 * be triggered multiple times with status updates. The callback | |
2923 * should take two arguments - the status code and the error condition. | |
2924 * | |
2925 * The status code will be one of the values in the Strophe.Status | |
2926 * constants. The error condition will be one of the conditions | |
2927 * defined in RFC 3920 or the condition 'strophe-parsererror'. | |
2928 * | |
2929 * The Parameters _wait_, _hold_ and _route_ are optional and only relevant | |
2930 * for BOSH connections. Please see XEP 124 for a more detailed explanation | |
2931 * of the optional parameters. | |
2932 * | |
2933 * Parameters: | |
2934 * (String) jid - The user's JID. This may be a bare JID, | |
2935 * or a full JID. If a node is not supplied, SASL OAUTHBEARER or | |
2936 * SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will | |
2937 * process the provided password value as an access token). | |
2938 * (String) pass - The user's password. | |
2939 * (Function) callback - The connect callback function. | |
2940 * (Integer) wait - The optional HTTPBIND wait value. This is the | |
2941 * time the server will wait before returning an empty result for | |
2942 * a request. The default setting of 60 seconds is recommended. | |
2943 * (Integer) hold - The optional HTTPBIND hold value. This is the | |
2944 * number of connections the server will hold at one time. This | |
2945 * should almost always be set to 1 (the default). | |
2946 * (String) route - The optional route value. | |
2947 * (String) authcid - The optional alternative authentication identity | |
2948 * (username) if intending to impersonate another user. | |
2949 * When using the SASL-EXTERNAL authentication mechanism, for example | |
2950 * with client certificates, then the authcid value is used to | |
2951 * determine whether an authorization JID (authzid) should be sent to | |
2952 * the server. The authzid should not be sent to the server if the | |
2953 * authzid and authcid are the same. So to prevent it from being sent | |
2954 * (for example when the JID is already contained in the client | |
2955 * certificate), set authcid to that same JID. See XEP-178 for more | |
2956 * details. | |
2957 */ | |
2958 connect: function connect(jid, pass, callback, wait, hold, route, authcid) { | |
2959 this.jid = jid; | |
2960 /** Variable: authzid | |
2961 * Authorization identity. | |
2962 */ | |
2963 | |
2964 this.authzid = Strophe.getBareJidFromJid(this.jid); | |
2965 /** Variable: authcid | |
2966 * Authentication identity (User name). | |
2967 */ | |
2968 | |
2969 this.authcid = authcid || Strophe.getNodeFromJid(this.jid); | |
2970 /** Variable: pass | |
2971 * Authentication identity (User password). | |
2972 */ | |
2973 | |
2974 this.pass = pass; | |
2975 /** Variable: servtype | |
2976 * Digest MD5 compatibility. | |
2977 */ | |
2978 | |
2979 this.servtype = "xmpp"; | |
2980 this.connect_callback = callback; | |
2981 this.disconnecting = false; | |
2982 this.connected = false; | |
2983 this.authenticated = false; | |
2984 this.restored = false; // parse jid for domain | |
2985 | |
2986 this.domain = Strophe.getDomainFromJid(this.jid); | |
2987 | |
2988 this._changeConnectStatus(Strophe.Status.CONNECTING, null); | |
2989 | |
2990 this._proto._connect(wait, hold, route); | |
2991 }, | |
2992 | |
2993 /** Function: attach | |
2994 * Attach to an already created and authenticated BOSH session. | |
2995 * | |
2996 * This function is provided to allow Strophe to attach to BOSH | |
2997 * sessions which have been created externally, perhaps by a Web | |
2998 * application. This is often used to support auto-login type features | |
2999 * without putting user credentials into the page. | |
3000 * | |
3001 * Parameters: | |
3002 * (String) jid - The full JID that is bound by the session. | |
3003 * (String) sid - The SID of the BOSH session. | |
3004 * (String) rid - The current RID of the BOSH session. This RID | |
3005 * will be used by the next request. | |
3006 * (Function) callback The connect callback function. | |
3007 * (Integer) wait - The optional HTTPBIND wait value. This is the | |
3008 * time the server will wait before returning an empty result for | |
3009 * a request. The default setting of 60 seconds is recommended. | |
3010 * Other settings will require tweaks to the Strophe.TIMEOUT value. | |
3011 * (Integer) hold - The optional HTTPBIND hold value. This is the | |
3012 * number of connections the server will hold at one time. This | |
3013 * should almost always be set to 1 (the default). | |
3014 * (Integer) wind - The optional HTTBIND window value. This is the | |
3015 * allowed range of request ids that are valid. The default is 5. | |
3016 */ | |
3017 attach: function attach(jid, sid, rid, callback, wait, hold, wind) { | |
3018 if (this._proto instanceof Strophe.Bosh) { | |
3019 this._proto._attach(jid, sid, rid, callback, wait, hold, wind); | |
3020 } else { | |
3021 var error = new Error('The "attach" method can only be used with a BOSH connection.'); | |
3022 error.name = 'StropheSessionError'; | |
3023 throw error; | |
3024 } | |
3025 }, | |
3026 | |
3027 /** Function: restore | |
3028 * Attempt to restore a cached BOSH session. | |
3029 * | |
3030 * This function is only useful in conjunction with providing the | |
3031 * "keepalive":true option when instantiating a new Strophe.Connection. | |
3032 * | |
3033 * When "keepalive" is set to true, Strophe will cache the BOSH tokens | |
3034 * RID (Request ID) and SID (Session ID) and then when this function is | |
3035 * called, it will attempt to restore the session from those cached | |
3036 * tokens. | |
3037 * | |
3038 * This function must therefore be called instead of connect or attach. | |
3039 * | |
3040 * For an example on how to use it, please see examples/restore.js | |
3041 * | |
3042 * Parameters: | |
3043 * (String) jid - The user's JID. This may be a bare JID or a full JID. | |
3044 * (Function) callback - The connect callback function. | |
3045 * (Integer) wait - The optional HTTPBIND wait value. This is the | |
3046 * time the server will wait before returning an empty result for | |
3047 * a request. The default setting of 60 seconds is recommended. | |
3048 * (Integer) hold - The optional HTTPBIND hold value. This is the | |
3049 * number of connections the server will hold at one time. This | |
3050 * should almost always be set to 1 (the default). | |
3051 * (Integer) wind - The optional HTTBIND window value. This is the | |
3052 * allowed range of request ids that are valid. The default is 5. | |
3053 */ | |
3054 restore: function restore(jid, callback, wait, hold, wind) { | |
3055 if (this._sessionCachingSupported()) { | |
3056 this._proto._restore(jid, callback, wait, hold, wind); | |
3057 } else { | |
3058 var error = new Error('The "restore" method can only be used with a BOSH connection.'); | |
3059 error.name = 'StropheSessionError'; | |
3060 throw error; | |
3061 } | |
3062 }, | |
3063 | |
3064 /** PrivateFunction: _sessionCachingSupported | |
3065 * Checks whether sessionStorage and JSON are supported and whether we're | |
3066 * using BOSH. | |
3067 */ | |
3068 _sessionCachingSupported: function _sessionCachingSupported() { | |
3069 if (this._proto instanceof Strophe.Bosh) { | |
3070 if (!JSON) { | |
3071 return false; | |
3072 } | |
3073 | |
3074 try { | |
3075 sessionStorage.setItem('_strophe_', '_strophe_'); | |
3076 sessionStorage.removeItem('_strophe_'); | |
3077 } catch (e) { | |
3078 return false; | |
3079 } | |
3080 | |
3081 return true; | |
3082 } | |
3083 | |
3084 return false; | |
3085 }, | |
3086 | |
3087 /** Function: xmlInput | |
3088 * User overrideable function that receives XML data coming into the | |
3089 * connection. | |
3090 * | |
3091 * The default function does nothing. User code can override this with | |
3092 * > Strophe.Connection.xmlInput = function (elem) { | |
3093 * > (user code) | |
3094 * > }; | |
3095 * | |
3096 * Due to limitations of current Browsers' XML-Parsers the opening and closing | |
3097 * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here. | |
3098 * | |
3099 * BOSH-Connections will have all stanzas wrapped in a <body> tag. See | |
3100 * <Strophe.Bosh.strip> if you want to strip this tag. | |
3101 * | |
3102 * Parameters: | |
3103 * (XMLElement) elem - The XML data received by the connection. | |
3104 */ | |
3105 | |
3106 /* jshint unused:false */ | |
3107 xmlInput: function xmlInput(elem) { | |
3108 return; | |
3109 }, | |
3110 | |
3111 /* jshint unused:true */ | |
3112 | |
3113 /** Function: xmlOutput | |
3114 * User overrideable function that receives XML data sent to the | |
3115 * connection. | |
3116 * | |
3117 * The default function does nothing. User code can override this with | |
3118 * > Strophe.Connection.xmlOutput = function (elem) { | |
3119 * > (user code) | |
3120 * > }; | |
3121 * | |
3122 * Due to limitations of current Browsers' XML-Parsers the opening and closing | |
3123 * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here. | |
3124 * | |
3125 * BOSH-Connections will have all stanzas wrapped in a <body> tag. See | |
3126 * <Strophe.Bosh.strip> if you want to strip this tag. | |
3127 * | |
3128 * Parameters: | |
3129 * (XMLElement) elem - The XMLdata sent by the connection. | |
3130 */ | |
3131 | |
3132 /* jshint unused:false */ | |
3133 xmlOutput: function xmlOutput(elem) { | |
3134 return; | |
3135 }, | |
3136 | |
3137 /* jshint unused:true */ | |
3138 | |
3139 /** Function: rawInput | |
3140 * User overrideable function that receives raw data coming into the | |
3141 * connection. | |
3142 * | |
3143 * The default function does nothing. User code can override this with | |
3144 * > Strophe.Connection.rawInput = function (data) { | |
3145 * > (user code) | |
3146 * > }; | |
3147 * | |
3148 * Parameters: | |
3149 * (String) data - The data received by the connection. | |
3150 */ | |
3151 | |
3152 /* jshint unused:false */ | |
3153 rawInput: function rawInput(data) { | |
3154 return; | |
3155 }, | |
3156 | |
3157 /* jshint unused:true */ | |
3158 | |
3159 /** Function: rawOutput | |
3160 * User overrideable function that receives raw data sent to the | |
3161 * connection. | |
3162 * | |
3163 * The default function does nothing. User code can override this with | |
3164 * > Strophe.Connection.rawOutput = function (data) { | |
3165 * > (user code) | |
3166 * > }; | |
3167 * | |
3168 * Parameters: | |
3169 * (String) data - The data sent by the connection. | |
3170 */ | |
3171 | |
3172 /* jshint unused:false */ | |
3173 rawOutput: function rawOutput(data) { | |
3174 return; | |
3175 }, | |
3176 | |
3177 /* jshint unused:true */ | |
3178 | |
3179 /** Function: nextValidRid | |
3180 * User overrideable function that receives the new valid rid. | |
3181 * | |
3182 * The default function does nothing. User code can override this with | |
3183 * > Strophe.Connection.nextValidRid = function (rid) { | |
3184 * > (user code) | |
3185 * > }; | |
3186 * | |
3187 * Parameters: | |
3188 * (Number) rid - The next valid rid | |
3189 */ | |
3190 | |
3191 /* jshint unused:false */ | |
3192 nextValidRid: function nextValidRid(rid) { | |
3193 return; | |
3194 }, | |
3195 | |
3196 /* jshint unused:true */ | |
3197 | |
3198 /** Function: send | |
3199 * Send a stanza. | |
3200 * | |
3201 * This function is called to push data onto the send queue to | |
3202 * go out over the wire. Whenever a request is sent to the BOSH | |
3203 * server, all pending data is sent and the queue is flushed. | |
3204 * | |
3205 * Parameters: | |
3206 * (XMLElement | | |
3207 * [XMLElement] | | |
3208 * Strophe.Builder) elem - The stanza to send. | |
3209 */ | |
3210 send: function send(elem) { | |
3211 if (elem === null) { | |
3212 return; | |
3213 } | |
3214 | |
3215 if (typeof elem.sort === "function") { | |
3216 for (var i = 0; i < elem.length; i++) { | |
3217 this._queueData(elem[i]); | |
3218 } | |
3219 } else if (typeof elem.tree === "function") { | |
3220 this._queueData(elem.tree()); | |
3221 } else { | |
3222 this._queueData(elem); | |
3223 } | |
3224 | |
3225 this._proto._send(); | |
3226 }, | |
3227 | |
3228 /** Function: flush | |
3229 * Immediately send any pending outgoing data. | |
3230 * | |
3231 * Normally send() queues outgoing data until the next idle period | |
3232 * (100ms), which optimizes network use in the common cases when | |
3233 * several send()s are called in succession. flush() can be used to | |
3234 * immediately send all pending data. | |
3235 */ | |
3236 flush: function flush() { | |
3237 // cancel the pending idle period and run the idle function | |
3238 // immediately | |
3239 clearTimeout(this._idleTimeout); | |
3240 | |
3241 this._onIdle(); | |
3242 }, | |
3243 | |
3244 /** Function: sendPresence | |
3245 * Helper function to send presence stanzas. The main benefit is for | |
3246 * sending presence stanzas for which you expect a responding presence | |
3247 * stanza with the same id (for example when leaving a chat room). | |
3248 * | |
3249 * Parameters: | |
3250 * (XMLElement) elem - The stanza to send. | |
3251 * (Function) callback - The callback function for a successful request. | |
3252 * (Function) errback - The callback function for a failed or timed | |
3253 * out request. On timeout, the stanza will be null. | |
3254 * (Integer) timeout - The time specified in milliseconds for a | |
3255 * timeout to occur. | |
3256 * | |
3257 * Returns: | |
3258 * The id used to send the presence. | |
3259 */ | |
3260 sendPresence: function sendPresence(elem, callback, errback, timeout) { | |
3261 var _this3 = this; | |
3262 | |
3263 var timeoutHandler = null; | |
3264 | |
3265 if (typeof elem.tree === "function") { | |
3266 elem = elem.tree(); | |
3267 } | |
3268 | |
3269 var id = elem.getAttribute('id'); | |
3270 | |
3271 if (!id) { | |
3272 // inject id if not found | |
3273 id = this.getUniqueId("sendPresence"); | |
3274 elem.setAttribute("id", id); | |
3275 } | |
3276 | |
3277 if (typeof callback === "function" || typeof errback === "function") { | |
3278 var handler = this.addHandler(function (stanza) { | |
3279 // remove timeout handler if there is one | |
3280 if (timeoutHandler) { | |
3281 _this3.deleteTimedHandler(timeoutHandler); | |
3282 } | |
3283 | |
3284 if (stanza.getAttribute('type') === 'error') { | |
3285 if (errback) { | |
3286 errback(stanza); | |
3287 } | |
3288 } else if (callback) { | |
3289 callback(stanza); | |
3290 } | |
3291 }, null, 'presence', null, id); // if timeout specified, set up a timeout handler. | |
3292 | |
3293 if (timeout) { | |
3294 timeoutHandler = this.addTimedHandler(timeout, function () { | |
3295 // get rid of normal handler | |
3296 _this3.deleteHandler(handler); // call errback on timeout with null stanza | |
3297 | |
3298 | |
3299 if (errback) { | |
3300 errback(null); | |
3301 } | |
3302 | |
3303 return false; | |
3304 }); | |
3305 } | |
3306 } | |
3307 | |
3308 this.send(elem); | |
3309 return id; | |
3310 }, | |
3311 | |
3312 /** Function: sendIQ | |
3313 * Helper function to send IQ stanzas. | |
3314 * | |
3315 * Parameters: | |
3316 * (XMLElement) elem - The stanza to send. | |
3317 * (Function) callback - The callback function for a successful request. | |
3318 * (Function) errback - The callback function for a failed or timed | |
3319 * out request. On timeout, the stanza will be null. | |
3320 * (Integer) timeout - The time specified in milliseconds for a | |
3321 * timeout to occur. | |
3322 * | |
3323 * Returns: | |
3324 * The id used to send the IQ. | |
3325 */ | |
3326 sendIQ: function sendIQ(elem, callback, errback, timeout) { | |
3327 var _this4 = this; | |
3328 | |
3329 var timeoutHandler = null; | |
3330 | |
3331 if (typeof elem.tree === "function") { | |
3332 elem = elem.tree(); | |
3333 } | |
3334 | |
3335 var id = elem.getAttribute('id'); | |
3336 | |
3337 if (!id) { | |
3338 // inject id if not found | |
3339 id = this.getUniqueId("sendIQ"); | |
3340 elem.setAttribute("id", id); | |
3341 } | |
3342 | |
3343 if (typeof callback === "function" || typeof errback === "function") { | |
3344 var handler = this.addHandler(function (stanza) { | |
3345 // remove timeout handler if there is one | |
3346 if (timeoutHandler) { | |
3347 _this4.deleteTimedHandler(timeoutHandler); | |
3348 } | |
3349 | |
3350 var iqtype = stanza.getAttribute('type'); | |
3351 | |
3352 if (iqtype === 'result') { | |
3353 if (callback) { | |
3354 callback(stanza); | |
3355 } | |
3356 } else if (iqtype === 'error') { | |
3357 if (errback) { | |
3358 errback(stanza); | |
3359 } | |
3360 } else { | |
3361 var error = new Error("Got bad IQ type of ".concat(iqtype)); | |
3362 error.name = "StropheError"; | |
3363 throw error; | |
3364 } | |
3365 }, null, 'iq', ['error', 'result'], id); // if timeout specified, set up a timeout handler. | |
3366 | |
3367 if (timeout) { | |
3368 timeoutHandler = this.addTimedHandler(timeout, function () { | |
3369 // get rid of normal handler | |
3370 _this4.deleteHandler(handler); // call errback on timeout with null stanza | |
3371 | |
3372 | |
3373 if (errback) { | |
3374 errback(null); | |
3375 } | |
3376 | |
3377 return false; | |
3378 }); | |
3379 } | |
3380 } | |
3381 | |
3382 this.send(elem); | |
3383 return id; | |
3384 }, | |
3385 | |
3386 /** PrivateFunction: _queueData | |
3387 * Queue outgoing data for later sending. Also ensures that the data | |
3388 * is a DOMElement. | |
3389 */ | |
3390 _queueData: function _queueData(element) { | |
3391 if (element === null || !element.tagName || !element.childNodes) { | |
3392 var error = new Error("Cannot queue non-DOMElement."); | |
3393 error.name = "StropheError"; | |
3394 throw error; | |
3395 } | |
3396 | |
3397 this._data.push(element); | |
3398 }, | |
3399 | |
3400 /** PrivateFunction: _sendRestart | |
3401 * Send an xmpp:restart stanza. | |
3402 */ | |
3403 _sendRestart: function _sendRestart() { | |
3404 var _this5 = this; | |
3405 | |
3406 this._data.push("restart"); | |
3407 | |
3408 this._proto._sendRestart(); | |
3409 | |
3410 this._idleTimeout = setTimeout(function () { | |
3411 return _this5._onIdle(); | |
3412 }, 100); | |
3413 }, | |
3414 | |
3415 /** Function: addTimedHandler | |
3416 * Add a timed handler to the connection. | |
3417 * | |
3418 * This function adds a timed handler. The provided handler will | |
3419 * be called every period milliseconds until it returns false, | |
3420 * the connection is terminated, or the handler is removed. Handlers | |
3421 * that wish to continue being invoked should return true. | |
3422 * | |
3423 * Because of method binding it is necessary to save the result of | |
3424 * this function if you wish to remove a handler with | |
3425 * deleteTimedHandler(). | |
3426 * | |
3427 * Note that user handlers are not active until authentication is | |
3428 * successful. | |
3429 * | |
3430 * Parameters: | |
3431 * (Integer) period - The period of the handler. | |
3432 * (Function) handler - The callback function. | |
3433 * | |
3434 * Returns: | |
3435 * A reference to the handler that can be used to remove it. | |
3436 */ | |
3437 addTimedHandler: function addTimedHandler(period, handler) { | |
3438 var thand = new Strophe.TimedHandler(period, handler); | |
3439 this.addTimeds.push(thand); | |
3440 return thand; | |
3441 }, | |
3442 | |
3443 /** Function: deleteTimedHandler | |
3444 * Delete a timed handler for a connection. | |
3445 * | |
3446 * This function removes a timed handler from the connection. The | |
3447 * handRef parameter is *not* the function passed to addTimedHandler(), | |
3448 * but is the reference returned from addTimedHandler(). | |
3449 * | |
3450 * Parameters: | |
3451 * (Strophe.TimedHandler) handRef - The handler reference. | |
3452 */ | |
3453 deleteTimedHandler: function deleteTimedHandler(handRef) { | |
3454 // this must be done in the Idle loop so that we don't change | |
3455 // the handlers during iteration | |
3456 this.removeTimeds.push(handRef); | |
3457 }, | |
3458 | |
3459 /** Function: addHandler | |
3460 * Add a stanza handler for the connection. | |
3461 * | |
3462 * This function adds a stanza handler to the connection. The | |
3463 * handler callback will be called for any stanza that matches | |
3464 * the parameters. Note that if multiple parameters are supplied, | |
3465 * they must all match for the handler to be invoked. | |
3466 * | |
3467 * The handler will receive the stanza that triggered it as its argument. | |
3468 * *The handler should return true if it is to be invoked again; | |
3469 * returning false will remove the handler after it returns.* | |
3470 * | |
3471 * As a convenience, the ns parameters applies to the top level element | |
3472 * and also any of its immediate children. This is primarily to make | |
3473 * matching /iq/query elements easy. | |
3474 * | |
3475 * Options | |
3476 * ~~~~~~~ | |
3477 * With the options argument, you can specify boolean flags that affect how | |
3478 * matches are being done. | |
3479 * | |
3480 * Currently two flags exist: | |
3481 * | |
3482 * - matchBareFromJid: | |
3483 * When set to true, the from parameter and the | |
3484 * from attribute on the stanza will be matched as bare JIDs instead | |
3485 * of full JIDs. To use this, pass {matchBareFromJid: true} as the | |
3486 * value of options. The default value for matchBareFromJid is false. | |
3487 * | |
3488 * - ignoreNamespaceFragment: | |
3489 * When set to true, a fragment specified on the stanza's namespace | |
3490 * URL will be ignored when it's matched with the one configured for | |
3491 * the handler. | |
3492 * | |
3493 * This means that if you register like this: | |
3494 * > connection.addHandler( | |
3495 * > handler, | |
3496 * > 'http://jabber.org/protocol/muc', | |
3497 * > null, null, null, null, | |
3498 * > {'ignoreNamespaceFragment': true} | |
3499 * > ); | |
3500 * | |
3501 * Then a stanza with XML namespace of | |
3502 * 'http://jabber.org/protocol/muc#user' will also be matched. If | |
3503 * 'ignoreNamespaceFragment' is false, then only stanzas with | |
3504 * 'http://jabber.org/protocol/muc' will be matched. | |
3505 * | |
3506 * Deleting the handler | |
3507 * ~~~~~~~~~~~~~~~~~~~~ | |
3508 * The return value should be saved if you wish to remove the handler | |
3509 * with deleteHandler(). | |
3510 * | |
3511 * Parameters: | |
3512 * (Function) handler - The user callback. | |
3513 * (String) ns - The namespace to match. | |
3514 * (String) name - The stanza name to match. | |
3515 * (String|Array) type - The stanza type (or types if an array) to match. | |
3516 * (String) id - The stanza id attribute to match. | |
3517 * (String) from - The stanza from attribute to match. | |
3518 * (String) options - The handler options | |
3519 * | |
3520 * Returns: | |
3521 * A reference to the handler that can be used to remove it. | |
3522 */ | |
3523 addHandler: function addHandler(handler, ns, name, type, id, from, options) { | |
3524 var hand = new Strophe.Handler(handler, ns, name, type, id, from, options); | |
3525 this.addHandlers.push(hand); | |
3526 return hand; | |
3527 }, | |
3528 | |
3529 /** Function: deleteHandler | |
3530 * Delete a stanza handler for a connection. | |
3531 * | |
3532 * This function removes a stanza handler from the connection. The | |
3533 * handRef parameter is *not* the function passed to addHandler(), | |
3534 * but is the reference returned from addHandler(). | |
3535 * | |
3536 * Parameters: | |
3537 * (Strophe.Handler) handRef - The handler reference. | |
3538 */ | |
3539 deleteHandler: function deleteHandler(handRef) { | |
3540 // this must be done in the Idle loop so that we don't change | |
3541 // the handlers during iteration | |
3542 this.removeHandlers.push(handRef); // If a handler is being deleted while it is being added, | |
3543 // prevent it from getting added | |
3544 | |
3545 var i = this.addHandlers.indexOf(handRef); | |
3546 | |
3547 if (i >= 0) { | |
3548 this.addHandlers.splice(i, 1); | |
3549 } | |
3550 }, | |
3551 | |
3552 /** Function: registerSASLMechanisms | |
3553 * | |
3554 * Register the SASL mechanisms which will be supported by this instance of | |
3555 * Strophe.Connection (i.e. which this XMPP client will support). | |
3556 * | |
3557 * Parameters: | |
3558 * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes | |
3559 * | |
3560 */ | |
3561 registerSASLMechanisms: function registerSASLMechanisms(mechanisms) { | |
3562 this.mechanisms = {}; | |
3563 mechanisms = mechanisms || [Strophe.SASLAnonymous, Strophe.SASLExternal, Strophe.SASLMD5, Strophe.SASLOAuthBearer, Strophe.SASLXOAuth2, Strophe.SASLPlain, Strophe.SASLSHA1]; | |
3564 mechanisms.forEach(this.registerSASLMechanism.bind(this)); | |
3565 }, | |
3566 | |
3567 /** Function: registerSASLMechanism | |
3568 * | |
3569 * Register a single SASL mechanism, to be supported by this client. | |
3570 * | |
3571 * Parameters: | |
3572 * (Object) mechanism - Object with a Strophe.SASLMechanism prototype | |
3573 * | |
3574 */ | |
3575 registerSASLMechanism: function registerSASLMechanism(mechanism) { | |
3576 this.mechanisms[mechanism.prototype.name] = mechanism; | |
3577 }, | |
3578 | |
3579 /** Function: disconnect | |
3580 * Start the graceful disconnection process. | |
3581 * | |
3582 * This function starts the disconnection process. This process starts | |
3583 * by sending unavailable presence and sending BOSH body of type | |
3584 * terminate. A timeout handler makes sure that disconnection happens | |
3585 * even if the BOSH server does not respond. | |
3586 * If the Connection object isn't connected, at least tries to abort all pending requests | |
3587 * so the connection object won't generate successful requests (which were already opened). | |
3588 * | |
3589 * The user supplied connection callback will be notified of the | |
3590 * progress as this process happens. | |
3591 * | |
3592 * Parameters: | |
3593 * (String) reason - The reason the disconnect is occuring. | |
3594 */ | |
3595 disconnect: function disconnect(reason) { | |
3596 this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason); | |
3597 | |
3598 Strophe.info("Disconnect was called because: " + reason); | |
3599 | |
3600 if (this.connected) { | |
3601 var pres = false; | |
3602 this.disconnecting = true; | |
3603 | |
3604 if (this.authenticated) { | |
3605 pres = $pres({ | |
3606 'xmlns': Strophe.NS.CLIENT, | |
3607 'type': 'unavailable' | |
3608 }); | |
3609 } // setup timeout handler | |
3610 | |
3611 | |
3612 this._disconnectTimeout = this._addSysTimedHandler(3000, this._onDisconnectTimeout.bind(this)); | |
3613 | |
3614 this._proto._disconnect(pres); | |
3615 } else { | |
3616 Strophe.info("Disconnect was called before Strophe connected to the server"); | |
3617 | |
3618 this._proto._abortAllRequests(); | |
3619 | |
3620 this._doDisconnect(); | |
3621 } | |
3622 }, | |
3623 | |
3624 /** PrivateFunction: _changeConnectStatus | |
3625 * _Private_ helper function that makes sure plugins and the user's | |
3626 * callback are notified of connection status changes. | |
3627 * | |
3628 * Parameters: | |
3629 * (Integer) status - the new connection status, one of the values | |
3630 * in Strophe.Status | |
3631 * (String) condition - the error condition or null | |
3632 * (XMLElement) elem - The triggering stanza. | |
3633 */ | |
3634 _changeConnectStatus: function _changeConnectStatus(status, condition, elem) { | |
3635 // notify all plugins listening for status changes | |
3636 for (var k in Strophe._connectionPlugins) { | |
3637 if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) { | |
3638 var plugin = this[k]; | |
3639 | |
3640 if (plugin.statusChanged) { | |
3641 try { | |
3642 plugin.statusChanged(status, condition); | |
3643 } catch (err) { | |
3644 Strophe.error("".concat(k, " plugin caused an exception changing status: ").concat(err)); | |
3645 } | |
3646 } | |
3647 } | |
3648 } // notify the user's callback | |
3649 | |
3650 | |
3651 if (this.connect_callback) { | |
3652 try { | |
3653 this.connect_callback(status, condition, elem); | |
3654 } catch (e) { | |
3655 Strophe._handleError(e); | |
3656 | |
3657 Strophe.error("User connection callback caused an exception: ".concat(e)); | |
3658 } | |
3659 } | |
3660 }, | |
3661 | |
3662 /** PrivateFunction: _doDisconnect | |
3663 * _Private_ function to disconnect. | |
3664 * | |
3665 * This is the last piece of the disconnection logic. This resets the | |
3666 * connection and alerts the user's connection callback. | |
3667 */ | |
3668 _doDisconnect: function _doDisconnect(condition) { | |
3669 if (typeof this._idleTimeout === "number") { | |
3670 clearTimeout(this._idleTimeout); | |
3671 } // Cancel Disconnect Timeout | |
3672 | |
3673 | |
3674 if (this._disconnectTimeout !== null) { | |
3675 this.deleteTimedHandler(this._disconnectTimeout); | |
3676 this._disconnectTimeout = null; | |
3677 } | |
3678 | |
3679 Strophe.info("_doDisconnect was called"); | |
3680 | |
3681 this._proto._doDisconnect(); | |
3682 | |
3683 this.authenticated = false; | |
3684 this.disconnecting = false; | |
3685 this.restored = false; // delete handlers | |
3686 | |
3687 this.handlers = []; | |
3688 this.timedHandlers = []; | |
3689 this.removeTimeds = []; | |
3690 this.removeHandlers = []; | |
3691 this.addTimeds = []; | |
3692 this.addHandlers = []; // tell the parent we disconnected | |
3693 | |
3694 this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition); | |
3695 | |
3696 this.connected = false; | |
3697 }, | |
3698 | |
3699 /** PrivateFunction: _dataRecv | |
3700 * _Private_ handler to processes incoming data from the the connection. | |
3701 * | |
3702 * Except for _connect_cb handling the initial connection request, | |
3703 * this function handles the incoming data for all requests. This | |
3704 * function also fires stanza handlers that match each incoming | |
3705 * stanza. | |
3706 * | |
3707 * Parameters: | |
3708 * (Strophe.Request) req - The request that has data ready. | |
3709 * (string) req - The stanza a raw string (optiona). | |
3710 */ | |
3711 _dataRecv: function _dataRecv(req, raw) { | |
3712 var _this6 = this; | |
3713 | |
3714 Strophe.info("_dataRecv called"); | |
3715 | |
3716 var elem = this._proto._reqToData(req); | |
3717 | |
3718 if (elem === null) { | |
3719 return; | |
3720 } | |
3721 | |
3722 if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) { | |
3723 if (elem.nodeName === this._proto.strip && elem.childNodes.length) { | |
3724 this.xmlInput(elem.childNodes[0]); | |
3725 } else { | |
3726 this.xmlInput(elem); | |
3727 } | |
3728 } | |
3729 | |
3730 if (this.rawInput !== Strophe.Connection.prototype.rawInput) { | |
3731 if (raw) { | |
3732 this.rawInput(raw); | |
3733 } else { | |
3734 this.rawInput(Strophe.serialize(elem)); | |
3735 } | |
3736 } // remove handlers scheduled for deletion | |
3737 | |
3738 | |
3739 while (this.removeHandlers.length > 0) { | |
3740 var hand = this.removeHandlers.pop(); | |
3741 var i = this.handlers.indexOf(hand); | |
3742 | |
3743 if (i >= 0) { | |
3744 this.handlers.splice(i, 1); | |
3745 } | |
3746 } // add handlers scheduled for addition | |
3747 | |
3748 | |
3749 while (this.addHandlers.length > 0) { | |
3750 this.handlers.push(this.addHandlers.pop()); | |
3751 } // handle graceful disconnect | |
3752 | |
3753 | |
3754 if (this.disconnecting && this._proto._emptyQueue()) { | |
3755 this._doDisconnect(); | |
3756 | |
3757 return; | |
3758 } | |
3759 | |
3760 var type = elem.getAttribute("type"); | |
3761 | |
3762 if (type !== null && type === "terminate") { | |
3763 // Don't process stanzas that come in after disconnect | |
3764 if (this.disconnecting) { | |
3765 return; | |
3766 } // an error occurred | |
3767 | |
3768 | |
3769 var cond = elem.getAttribute("condition"); | |
3770 var conflict = elem.getElementsByTagName("conflict"); | |
3771 | |
3772 if (cond !== null) { | |
3773 if (cond === "remote-stream-error" && conflict.length > 0) { | |
3774 cond = "conflict"; | |
3775 } | |
3776 | |
3777 this._changeConnectStatus(Strophe.Status.CONNFAIL, cond); | |
3778 } else { | |
3779 this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.UNKOWN_REASON); | |
3780 } | |
3781 | |
3782 this._doDisconnect(cond); | |
3783 | |
3784 return; | |
3785 } // send each incoming stanza through the handler chain | |
3786 | |
3787 | |
3788 Strophe.forEachChild(elem, null, function (child) { | |
3789 // process handlers | |
3790 var newList = _this6.handlers; | |
3791 _this6.handlers = []; | |
3792 | |
3793 for (var _i5 = 0; _i5 < newList.length; _i5++) { | |
3794 var _hand = newList[_i5]; // encapsulate 'handler.run' not to lose the whole handler list if | |
3795 // one of the handlers throws an exception | |
3796 | |
3797 try { | |
3798 if (_hand.isMatch(child) && (_this6.authenticated || !_hand.user)) { | |
3799 if (_hand.run(child)) { | |
3800 _this6.handlers.push(_hand); | |
3801 } | |
3802 } else { | |
3803 _this6.handlers.push(_hand); | |
3804 } | |
3805 } catch (e) { | |
3806 // if the handler throws an exception, we consider it as false | |
3807 Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message); | |
3808 } | |
3809 } | |
3810 }); | |
3811 }, | |
3812 | |
3813 /** Attribute: mechanisms | |
3814 * SASL Mechanisms available for Connection. | |
3815 */ | |
3816 mechanisms: {}, | |
3817 | |
3818 /** PrivateFunction: _connect_cb | |
3819 * _Private_ handler for initial connection request. | |
3820 * | |
3821 * This handler is used to process the initial connection request | |
3822 * response from the BOSH server. It is used to set up authentication | |
3823 * handlers and start the authentication process. | |
3824 * | |
3825 * SASL authentication will be attempted if available, otherwise | |
3826 * the code will fall back to legacy authentication. | |
3827 * | |
3828 * Parameters: | |
3829 * (Strophe.Request) req - The current request. | |
3830 * (Function) _callback - low level (xmpp) connect callback function. | |
3831 * Useful for plugins with their own xmpp connect callback (when they | |
3832 * want to do something special). | |
3833 */ | |
3834 _connect_cb: function _connect_cb(req, _callback, raw) { | |
3835 Strophe.info("_connect_cb was called"); | |
3836 this.connected = true; | |
3837 var bodyWrap; | |
3838 | |
3839 try { | |
3840 bodyWrap = this._proto._reqToData(req); | |
3841 } catch (e) { | |
3842 if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) { | |
3843 throw e; | |
3844 } | |
3845 | |
3846 this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.BAD_FORMAT); | |
3847 | |
3848 this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT); | |
3849 } | |
3850 | |
3851 if (!bodyWrap) { | |
3852 return; | |
3853 } | |
3854 | |
3855 if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) { | |
3856 if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) { | |
3857 this.xmlInput(bodyWrap.childNodes[0]); | |
3858 } else { | |
3859 this.xmlInput(bodyWrap); | |
3860 } | |
3861 } | |
3862 | |
3863 if (this.rawInput !== Strophe.Connection.prototype.rawInput) { | |
3864 if (raw) { | |
3865 this.rawInput(raw); | |
3866 } else { | |
3867 this.rawInput(Strophe.serialize(bodyWrap)); | |
3868 } | |
3869 } | |
3870 | |
3871 var conncheck = this._proto._connect_cb(bodyWrap); | |
3872 | |
3873 if (conncheck === Strophe.Status.CONNFAIL) { | |
3874 return; | |
3875 } // Check for the stream:features tag | |
3876 | |
3877 | |
3878 var hasFeatures; | |
3879 | |
3880 if (bodyWrap.getElementsByTagNameNS) { | |
3881 hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0; | |
3882 } else { | |
3883 hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 || bodyWrap.getElementsByTagName("features").length > 0; | |
3884 } | |
3885 | |
3886 if (!hasFeatures) { | |
3887 this._proto._no_auth_received(_callback); | |
3888 | |
3889 return; | |
3890 } | |
3891 | |
3892 var matched = []; | |
3893 var mechanisms = bodyWrap.getElementsByTagName("mechanism"); | |
3894 | |
3895 if (mechanisms.length > 0) { | |
3896 for (var i = 0; i < mechanisms.length; i++) { | |
3897 var mech = Strophe.getText(mechanisms[i]); | |
3898 if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]); | |
3899 } | |
3900 } | |
3901 | |
3902 if (matched.length === 0) { | |
3903 if (bodyWrap.getElementsByTagName("auth").length === 0) { | |
3904 // There are no matching SASL mechanisms and also no legacy | |
3905 // auth available. | |
3906 this._proto._no_auth_received(_callback); | |
3907 | |
3908 return; | |
3909 } | |
3910 } | |
3911 | |
3912 if (this.do_authentication !== false) { | |
3913 this.authenticate(matched); | |
3914 } | |
3915 }, | |
3916 | |
3917 /** Function: sortMechanismsByPriority | |
3918 * | |
3919 * Sorts an array of objects with prototype SASLMechanism according to | |
3920 * their priorities. | |
3921 * | |
3922 * Parameters: | |
3923 * (Array) mechanisms - Array of SASL mechanisms. | |
3924 * | |
3925 */ | |
3926 sortMechanismsByPriority: function sortMechanismsByPriority(mechanisms) { | |
3927 // Sorting mechanisms according to priority. | |
3928 for (var i = 0; i < mechanisms.length - 1; ++i) { | |
3929 var higher = i; | |
3930 | |
3931 for (var j = i + 1; j < mechanisms.length; ++j) { | |
3932 if (mechanisms[j].prototype.priority > mechanisms[higher].prototype.priority) { | |
3933 higher = j; | |
3934 } | |
3935 } | |
3936 | |
3937 if (higher !== i) { | |
3938 var swap = mechanisms[i]; | |
3939 mechanisms[i] = mechanisms[higher]; | |
3940 mechanisms[higher] = swap; | |
3941 } | |
3942 } | |
3943 | |
3944 return mechanisms; | |
3945 }, | |
3946 | |
3947 /** PrivateFunction: _attemptSASLAuth | |
3948 * | |
3949 * Iterate through an array of SASL mechanisms and attempt authentication | |
3950 * with the highest priority (enabled) mechanism. | |
3951 * | |
3952 * Parameters: | |
3953 * (Array) mechanisms - Array of SASL mechanisms. | |
3954 * | |
3955 * Returns: | |
3956 * (Boolean) mechanism_found - true or false, depending on whether a | |
3957 * valid SASL mechanism was found with which authentication could be | |
3958 * started. | |
3959 */ | |
3960 _attemptSASLAuth: function _attemptSASLAuth(mechanisms) { | |
3961 mechanisms = this.sortMechanismsByPriority(mechanisms || []); | |
3962 var mechanism_found = false; | |
3963 | |
3964 for (var i = 0; i < mechanisms.length; ++i) { | |
3965 if (!mechanisms[i].prototype.test(this)) { | |
3966 continue; | |
3967 } | |
3968 | |
3969 this._sasl_success_handler = this._addSysHandler(this._sasl_success_cb.bind(this), null, "success", null, null); | |
3970 this._sasl_failure_handler = this._addSysHandler(this._sasl_failure_cb.bind(this), null, "failure", null, null); | |
3971 this._sasl_challenge_handler = this._addSysHandler(this._sasl_challenge_cb.bind(this), null, "challenge", null, null); | |
3972 this._sasl_mechanism = new mechanisms[i](); | |
3973 | |
3974 this._sasl_mechanism.onStart(this); | |
3975 | |
3976 var request_auth_exchange = $build("auth", { | |
3977 'xmlns': Strophe.NS.SASL, | |
3978 'mechanism': this._sasl_mechanism.name | |
3979 }); | |
3980 | |
3981 if (this._sasl_mechanism.isClientFirst) { | |
3982 var response = this._sasl_mechanism.onChallenge(this, null); | |
3983 | |
3984 request_auth_exchange.t(btoa(response)); | |
3985 } | |
3986 | |
3987 this.send(request_auth_exchange.tree()); | |
3988 mechanism_found = true; | |
3989 break; | |
3990 } | |
3991 | |
3992 return mechanism_found; | |
3993 }, | |
3994 | |
3995 /** PrivateFunction: _attemptLegacyAuth | |
3996 * | |
3997 * Attempt legacy (i.e. non-SASL) authentication. | |
3998 * | |
3999 */ | |
4000 _attemptLegacyAuth: function _attemptLegacyAuth() { | |
4001 if (Strophe.getNodeFromJid(this.jid) === null) { | |
4002 // we don't have a node, which is required for non-anonymous | |
4003 // client connections | |
4004 this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.MISSING_JID_NODE); | |
4005 | |
4006 this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE); | |
4007 } else { | |
4008 // Fall back to legacy authentication | |
4009 this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null); | |
4010 | |
4011 this._addSysHandler(this._auth1_cb.bind(this), null, null, null, "_auth_1"); | |
4012 | |
4013 this.send($iq({ | |
4014 'type': "get", | |
4015 'to': this.domain, | |
4016 'id': "_auth_1" | |
4017 }).c("query", { | |
4018 xmlns: Strophe.NS.AUTH | |
4019 }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree()); | |
4020 } | |
4021 }, | |
4022 | |
4023 /** Function: authenticate | |
4024 * Set up authentication | |
4025 * | |
4026 * Continues the initial connection request by setting up authentication | |
4027 * handlers and starting the authentication process. | |
4028 * | |
4029 * SASL authentication will be attempted if available, otherwise | |
4030 * the code will fall back to legacy authentication. | |
4031 * | |
4032 * Parameters: | |
4033 * (Array) matched - Array of SASL mechanisms supported. | |
4034 * | |
4035 */ | |
4036 authenticate: function authenticate(matched) { | |
4037 if (!this._attemptSASLAuth(matched)) { | |
4038 this._attemptLegacyAuth(); | |
4039 } | |
4040 }, | |
4041 | |
4042 /** PrivateFunction: _sasl_challenge_cb | |
4043 * _Private_ handler for the SASL challenge | |
4044 * | |
4045 */ | |
4046 _sasl_challenge_cb: function _sasl_challenge_cb(elem) { | |
4047 var challenge = atob(Strophe.getText(elem)); | |
4048 | |
4049 var response = this._sasl_mechanism.onChallenge(this, challenge); | |
4050 | |
4051 var stanza = $build('response', { | |
4052 'xmlns': Strophe.NS.SASL | |
4053 }); | |
4054 | |
4055 if (response !== "") { | |
4056 stanza.t(btoa(response)); | |
4057 } | |
4058 | |
4059 this.send(stanza.tree()); | |
4060 return true; | |
4061 }, | |
4062 | |
4063 /** PrivateFunction: _auth1_cb | |
4064 * _Private_ handler for legacy authentication. | |
4065 * | |
4066 * This handler is called in response to the initial <iq type='get'/> | |
4067 * for legacy authentication. It builds an authentication <iq/> and | |
4068 * sends it, creating a handler (calling back to _auth2_cb()) to | |
4069 * handle the result | |
4070 * | |
4071 * Parameters: | |
4072 * (XMLElement) elem - The stanza that triggered the callback. | |
4073 * | |
4074 * Returns: | |
4075 * false to remove the handler. | |
4076 */ | |
4077 | |
4078 /* jshint unused:false */ | |
4079 _auth1_cb: function _auth1_cb(elem) { | |
4080 // build plaintext auth iq | |
4081 var iq = $iq({ | |
4082 type: "set", | |
4083 id: "_auth_2" | |
4084 }).c('query', { | |
4085 xmlns: Strophe.NS.AUTH | |
4086 }).c('username', {}).t(Strophe.getNodeFromJid(this.jid)).up().c('password').t(this.pass); | |
4087 | |
4088 if (!Strophe.getResourceFromJid(this.jid)) { | |
4089 // since the user has not supplied a resource, we pick | |
4090 // a default one here. unlike other auth methods, the server | |
4091 // cannot do this for us. | |
4092 this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe'; | |
4093 } | |
4094 | |
4095 iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid)); | |
4096 | |
4097 this._addSysHandler(this._auth2_cb.bind(this), null, null, null, "_auth_2"); | |
4098 | |
4099 this.send(iq.tree()); | |
4100 return false; | |
4101 }, | |
4102 | |
4103 /* jshint unused:true */ | |
4104 | |
4105 /** PrivateFunction: _sasl_success_cb | |
4106 * _Private_ handler for succesful SASL authentication. | |
4107 * | |
4108 * Parameters: | |
4109 * (XMLElement) elem - The matching stanza. | |
4110 * | |
4111 * Returns: | |
4112 * false to remove the handler. | |
4113 */ | |
4114 _sasl_success_cb: function _sasl_success_cb(elem) { | |
4115 var _this7 = this; | |
4116 | |
4117 if (this._sasl_data["server-signature"]) { | |
4118 var serverSignature; | |
4119 var success = atob(Strophe.getText(elem)); | |
4120 var attribMatch = /([a-z]+)=([^,]+)(,|$)/; | |
4121 var matches = success.match(attribMatch); | |
4122 | |
4123 if (matches[1] === "v") { | |
4124 serverSignature = matches[2]; | |
4125 } | |
4126 | |
4127 if (serverSignature !== this._sasl_data["server-signature"]) { | |
4128 // remove old handlers | |
4129 this.deleteHandler(this._sasl_failure_handler); | |
4130 this._sasl_failure_handler = null; | |
4131 | |
4132 if (this._sasl_challenge_handler) { | |
4133 this.deleteHandler(this._sasl_challenge_handler); | |
4134 this._sasl_challenge_handler = null; | |
4135 } | |
4136 | |
4137 this._sasl_data = {}; | |
4138 return this._sasl_failure_cb(null); | |
4139 } | |
4140 } | |
4141 | |
4142 Strophe.info("SASL authentication succeeded."); | |
4143 | |
4144 if (this._sasl_mechanism) { | |
4145 this._sasl_mechanism.onSuccess(); | |
4146 } // remove old handlers | |
4147 | |
4148 | |
4149 this.deleteHandler(this._sasl_failure_handler); | |
4150 this._sasl_failure_handler = null; | |
4151 | |
4152 if (this._sasl_challenge_handler) { | |
4153 this.deleteHandler(this._sasl_challenge_handler); | |
4154 this._sasl_challenge_handler = null; | |
4155 } | |
4156 | |
4157 var streamfeature_handlers = []; | |
4158 | |
4159 var wrapper = function wrapper(handlers, elem) { | |
4160 while (handlers.length) { | |
4161 _this7.deleteHandler(handlers.pop()); | |
4162 } | |
4163 | |
4164 _this7._sasl_auth1_cb(elem); | |
4165 | |
4166 return false; | |
4167 }; | |
4168 | |
4169 streamfeature_handlers.push(this._addSysHandler(function (elem) { | |
4170 return wrapper(streamfeature_handlers, elem); | |
4171 }, null, "stream:features", null, null)); | |
4172 streamfeature_handlers.push(this._addSysHandler(function (elem) { | |
4173 return wrapper(streamfeature_handlers, elem); | |
4174 }, Strophe.NS.STREAM, "features", null, null)); // we must send an xmpp:restart now | |
4175 | |
4176 this._sendRestart(); | |
4177 | |
4178 return false; | |
4179 }, | |
4180 | |
4181 /** PrivateFunction: _sasl_auth1_cb | |
4182 * _Private_ handler to start stream binding. | |
4183 * | |
4184 * Parameters: | |
4185 * (XMLElement) elem - The matching stanza. | |
4186 * | |
4187 * Returns: | |
4188 * false to remove the handler. | |
4189 */ | |
4190 _sasl_auth1_cb: function _sasl_auth1_cb(elem) { | |
4191 // save stream:features for future usage | |
4192 this.features = elem; | |
4193 | |
4194 for (var i = 0; i < elem.childNodes.length; i++) { | |
4195 var child = elem.childNodes[i]; | |
4196 | |
4197 if (child.nodeName === 'bind') { | |
4198 this.do_bind = true; | |
4199 } | |
4200 | |
4201 if (child.nodeName === 'session') { | |
4202 this.do_session = true; | |
4203 } | |
4204 } | |
4205 | |
4206 if (!this.do_bind) { | |
4207 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null); | |
4208 | |
4209 return false; | |
4210 } else { | |
4211 this._addSysHandler(this._sasl_bind_cb.bind(this), null, null, null, "_bind_auth_2"); | |
4212 | |
4213 var resource = Strophe.getResourceFromJid(this.jid); | |
4214 | |
4215 if (resource) { | |
4216 this.send($iq({ | |
4217 type: "set", | |
4218 id: "_bind_auth_2" | |
4219 }).c('bind', { | |
4220 xmlns: Strophe.NS.BIND | |
4221 }).c('resource', {}).t(resource).tree()); | |
4222 } else { | |
4223 this.send($iq({ | |
4224 type: "set", | |
4225 id: "_bind_auth_2" | |
4226 }).c('bind', { | |
4227 xmlns: Strophe.NS.BIND | |
4228 }).tree()); | |
4229 } | |
4230 } | |
4231 | |
4232 return false; | |
4233 }, | |
4234 | |
4235 /** PrivateFunction: _sasl_bind_cb | |
4236 * _Private_ handler for binding result and session start. | |
4237 * | |
4238 * Parameters: | |
4239 * (XMLElement) elem - The matching stanza. | |
4240 * | |
4241 * Returns: | |
4242 * false to remove the handler. | |
4243 */ | |
4244 _sasl_bind_cb: function _sasl_bind_cb(elem) { | |
4245 if (elem.getAttribute("type") === "error") { | |
4246 Strophe.info("SASL binding failed."); | |
4247 var conflict = elem.getElementsByTagName("conflict"); | |
4248 var condition; | |
4249 | |
4250 if (conflict.length > 0) { | |
4251 condition = Strophe.ErrorCondition.CONFLICT; | |
4252 } | |
4253 | |
4254 this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem); | |
4255 | |
4256 return false; | |
4257 } // TODO - need to grab errors | |
4258 | |
4259 | |
4260 var bind = elem.getElementsByTagName("bind"); | |
4261 | |
4262 if (bind.length > 0) { | |
4263 var jidNode = bind[0].getElementsByTagName("jid"); | |
4264 | |
4265 if (jidNode.length > 0) { | |
4266 this.jid = Strophe.getText(jidNode[0]); | |
4267 | |
4268 if (this.do_session) { | |
4269 this._addSysHandler(this._sasl_session_cb.bind(this), null, null, null, "_session_auth_2"); | |
4270 | |
4271 this.send($iq({ | |
4272 type: "set", | |
4273 id: "_session_auth_2" | |
4274 }).c('session', { | |
4275 xmlns: Strophe.NS.SESSION | |
4276 }).tree()); | |
4277 } else { | |
4278 this.authenticated = true; | |
4279 | |
4280 this._changeConnectStatus(Strophe.Status.CONNECTED, null); | |
4281 } | |
4282 } | |
4283 } else { | |
4284 Strophe.info("SASL binding failed."); | |
4285 | |
4286 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem); | |
4287 | |
4288 return false; | |
4289 } | |
4290 }, | |
4291 | |
4292 /** PrivateFunction: _sasl_session_cb | |
4293 * _Private_ handler to finish successful SASL connection. | |
4294 * | |
4295 * This sets Connection.authenticated to true on success, which | |
4296 * starts the processing of user handlers. | |
4297 * | |
4298 * Parameters: | |
4299 * (XMLElement) elem - The matching stanza. | |
4300 * | |
4301 * Returns: | |
4302 * false to remove the handler. | |
4303 */ | |
4304 _sasl_session_cb: function _sasl_session_cb(elem) { | |
4305 if (elem.getAttribute("type") === "result") { | |
4306 this.authenticated = true; | |
4307 | |
4308 this._changeConnectStatus(Strophe.Status.CONNECTED, null); | |
4309 } else if (elem.getAttribute("type") === "error") { | |
4310 Strophe.info("Session creation failed."); | |
4311 | |
4312 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem); | |
4313 | |
4314 return false; | |
4315 } | |
4316 | |
4317 return false; | |
4318 }, | |
4319 | |
4320 /** PrivateFunction: _sasl_failure_cb | |
4321 * _Private_ handler for SASL authentication failure. | |
4322 * | |
4323 * Parameters: | |
4324 * (XMLElement) elem - The matching stanza. | |
4325 * | |
4326 * Returns: | |
4327 * false to remove the handler. | |
4328 */ | |
4329 | |
4330 /* jshint unused:false */ | |
4331 _sasl_failure_cb: function _sasl_failure_cb(elem) { | |
4332 // delete unneeded handlers | |
4333 if (this._sasl_success_handler) { | |
4334 this.deleteHandler(this._sasl_success_handler); | |
4335 this._sasl_success_handler = null; | |
4336 } | |
4337 | |
4338 if (this._sasl_challenge_handler) { | |
4339 this.deleteHandler(this._sasl_challenge_handler); | |
4340 this._sasl_challenge_handler = null; | |
4341 } | |
4342 | |
4343 if (this._sasl_mechanism) this._sasl_mechanism.onFailure(); | |
4344 | |
4345 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem); | |
4346 | |
4347 return false; | |
4348 }, | |
4349 | |
4350 /* jshint unused:true */ | |
4351 | |
4352 /** PrivateFunction: _auth2_cb | |
4353 * _Private_ handler to finish legacy authentication. | |
4354 * | |
4355 * This handler is called when the result from the jabber:iq:auth | |
4356 * <iq/> stanza is returned. | |
4357 * | |
4358 * Parameters: | |
4359 * (XMLElement) elem - The stanza that triggered the callback. | |
4360 * | |
4361 * Returns: | |
4362 * false to remove the handler. | |
4363 */ | |
4364 _auth2_cb: function _auth2_cb(elem) { | |
4365 if (elem.getAttribute("type") === "result") { | |
4366 this.authenticated = true; | |
4367 | |
4368 this._changeConnectStatus(Strophe.Status.CONNECTED, null); | |
4369 } else if (elem.getAttribute("type") === "error") { | |
4370 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem); | |
4371 | |
4372 this.disconnect('authentication failed'); | |
4373 } | |
4374 | |
4375 return false; | |
4376 }, | |
4377 | |
4378 /** PrivateFunction: _addSysTimedHandler | |
4379 * _Private_ function to add a system level timed handler. | |
4380 * | |
4381 * This function is used to add a Strophe.TimedHandler for the | |
4382 * library code. System timed handlers are allowed to run before | |
4383 * authentication is complete. | |
4384 * | |
4385 * Parameters: | |
4386 * (Integer) period - The period of the handler. | |
4387 * (Function) handler - The callback function. | |
4388 */ | |
4389 _addSysTimedHandler: function _addSysTimedHandler(period, handler) { | |
4390 var thand = new Strophe.TimedHandler(period, handler); | |
4391 thand.user = false; | |
4392 this.addTimeds.push(thand); | |
4393 return thand; | |
4394 }, | |
4395 | |
4396 /** PrivateFunction: _addSysHandler | |
4397 * _Private_ function to add a system level stanza handler. | |
4398 * | |
4399 * This function is used to add a Strophe.Handler for the | |
4400 * library code. System stanza handlers are allowed to run before | |
4401 * authentication is complete. | |
4402 * | |
4403 * Parameters: | |
4404 * (Function) handler - The callback function. | |
4405 * (String) ns - The namespace to match. | |
4406 * (String) name - The stanza name to match. | |
4407 * (String) type - The stanza type attribute to match. | |
4408 * (String) id - The stanza id attribute to match. | |
4409 */ | |
4410 _addSysHandler: function _addSysHandler(handler, ns, name, type, id) { | |
4411 var hand = new Strophe.Handler(handler, ns, name, type, id); | |
4412 hand.user = false; | |
4413 this.addHandlers.push(hand); | |
4414 return hand; | |
4415 }, | |
4416 | |
4417 /** PrivateFunction: _onDisconnectTimeout | |
4418 * _Private_ timeout handler for handling non-graceful disconnection. | |
4419 * | |
4420 * If the graceful disconnect process does not complete within the | |
4421 * time allotted, this handler finishes the disconnect anyway. | |
4422 * | |
4423 * Returns: | |
4424 * false to remove the handler. | |
4425 */ | |
4426 _onDisconnectTimeout: function _onDisconnectTimeout() { | |
4427 Strophe.info("_onDisconnectTimeout was called"); | |
4428 | |
4429 this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null); | |
4430 | |
4431 this._proto._onDisconnectTimeout(); // actually disconnect | |
4432 | |
4433 | |
4434 this._doDisconnect(); | |
4435 | |
4436 return false; | |
4437 }, | |
4438 | |
4439 /** PrivateFunction: _onIdle | |
4440 * _Private_ handler to process events during idle cycle. | |
4441 * | |
4442 * This handler is called every 100ms to fire timed handlers that | |
4443 * are ready and keep poll requests going. | |
4444 */ | |
4445 _onIdle: function _onIdle() { | |
4446 var _this8 = this; | |
4447 | |
4448 // add timed handlers scheduled for addition | |
4449 // NOTE: we add before remove in the case a timed handler is | |
4450 // added and then deleted before the next _onIdle() call. | |
4451 while (this.addTimeds.length > 0) { | |
4452 this.timedHandlers.push(this.addTimeds.pop()); | |
4453 } // remove timed handlers that have been scheduled for deletion | |
4454 | |
4455 | |
4456 while (this.removeTimeds.length > 0) { | |
4457 var thand = this.removeTimeds.pop(); | |
4458 var i = this.timedHandlers.indexOf(thand); | |
4459 | |
4460 if (i >= 0) { | |
4461 this.timedHandlers.splice(i, 1); | |
4462 } | |
4463 } // call ready timed handlers | |
4464 | |
4465 | |
4466 var now = new Date().getTime(); | |
4467 var newList = []; | |
4468 | |
4469 for (var _i6 = 0; _i6 < this.timedHandlers.length; _i6++) { | |
4470 var _thand = this.timedHandlers[_i6]; | |
4471 | |
4472 if (this.authenticated || !_thand.user) { | |
4473 var since = _thand.lastCalled + _thand.period; | |
4474 | |
4475 if (since - now <= 0) { | |
4476 if (_thand.run()) { | |
4477 newList.push(_thand); | |
4478 } | |
4479 } else { | |
4480 newList.push(_thand); | |
4481 } | |
4482 } | |
4483 } | |
4484 | |
4485 this.timedHandlers = newList; | |
4486 clearTimeout(this._idleTimeout); | |
4487 | |
4488 this._proto._onIdle(); // reactivate the timer only if connected | |
4489 | |
4490 | |
4491 if (this.connected) { | |
4492 this._idleTimeout = setTimeout(function () { | |
4493 return _this8._onIdle(); | |
4494 }, 100); | |
4495 } | |
4496 } | |
4497 }; | |
4498 /** Class: Strophe.SASLMechanism | |
4499 * | |
4500 * encapsulates SASL authentication mechanisms. | |
4501 * | |
4502 * User code may override the priority for each mechanism or disable it completely. | |
4503 * See <priority> for information about changing priority and <test> for informatian on | |
4504 * how to disable a mechanism. | |
4505 * | |
4506 * By default, all mechanisms are enabled and the priorities are | |
4507 * | |
4508 * OAUTHBEARER - 60 | |
4509 * SCRAM-SHA1 - 50 | |
4510 * DIGEST-MD5 - 40 | |
4511 * PLAIN - 30 | |
4512 * ANONYMOUS - 20 | |
4513 * EXTERNAL - 10 | |
4514 * | |
4515 * See: Strophe.Connection.addSupportedSASLMechanisms | |
4516 */ | |
4517 | |
4518 /** | |
4519 * PrivateConstructor: Strophe.SASLMechanism | |
4520 * SASL auth mechanism abstraction. | |
4521 * | |
4522 * Parameters: | |
4523 * (String) name - SASL Mechanism name. | |
4524 * (Boolean) isClientFirst - If client should send response first without challenge. | |
4525 * (Number) priority - Priority. | |
4526 * | |
4527 * Returns: | |
4528 * A new Strophe.SASLMechanism object. | |
4529 */ | |
4530 | |
4531 Strophe.SASLMechanism = function (name, isClientFirst, priority) { | |
4532 /** PrivateVariable: name | |
4533 * Mechanism name. | |
4534 */ | |
4535 this.name = name; | |
4536 /** PrivateVariable: isClientFirst | |
4537 * If client sends response without initial server challenge. | |
4538 */ | |
4539 | |
4540 this.isClientFirst = isClientFirst; | |
4541 /** Variable: priority | |
4542 * Determines which <SASLMechanism> is chosen for authentication (Higher is better). | |
4543 * Users may override this to prioritize mechanisms differently. | |
4544 * | |
4545 * In the default configuration the priorities are | |
4546 * | |
4547 * SCRAM-SHA1 - 40 | |
4548 * DIGEST-MD5 - 30 | |
4549 * Plain - 20 | |
4550 * | |
4551 * Example: (This will cause Strophe to choose the mechanism that the server sent first) | |
4552 * | |
4553 * > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority; | |
4554 * | |
4555 * See <SASL mechanisms> for a list of available mechanisms. | |
4556 * | |
4557 */ | |
4558 | |
4559 this.priority = priority; | |
4560 }; | |
4561 | |
4562 Strophe.SASLMechanism.prototype = { | |
4563 /** | |
4564 * Function: test | |
4565 * Checks if mechanism able to run. | |
4566 * To disable a mechanism, make this return false; | |
4567 * | |
4568 * To disable plain authentication run | |
4569 * > Strophe.SASLPlain.test = function() { | |
4570 * > return false; | |
4571 * > } | |
4572 * | |
4573 * See <SASL mechanisms> for a list of available mechanisms. | |
4574 * | |
4575 * Parameters: | |
4576 * (Strophe.Connection) connection - Target Connection. | |
4577 * | |
4578 * Returns: | |
4579 * (Boolean) If mechanism was able to run. | |
4580 */ | |
4581 | |
4582 /* jshint unused:false */ | |
4583 test: function test(connection) { | |
4584 return true; | |
4585 }, | |
4586 | |
4587 /* jshint unused:true */ | |
4588 | |
4589 /** PrivateFunction: onStart | |
4590 * Called before starting mechanism on some connection. | |
4591 * | |
4592 * Parameters: | |
4593 * (Strophe.Connection) connection - Target Connection. | |
4594 */ | |
4595 onStart: function onStart(connection) { | |
4596 this._connection = connection; | |
4597 }, | |
4598 | |
4599 /** PrivateFunction: onChallenge | |
4600 * Called by protocol implementation on incoming challenge. If client is | |
4601 * first (isClientFirst === true) challenge will be null on the first call. | |
4602 * | |
4603 * Parameters: | |
4604 * (Strophe.Connection) connection - Target Connection. | |
4605 * (String) challenge - current challenge to handle. | |
4606 * | |
4607 * Returns: | |
4608 * (String) Mechanism response. | |
4609 */ | |
4610 | |
4611 /* jshint unused:false */ | |
4612 onChallenge: function onChallenge(connection, challenge) { | |
4613 throw new Error("You should implement challenge handling!"); | |
4614 }, | |
4615 | |
4616 /* jshint unused:true */ | |
4617 | |
4618 /** PrivateFunction: onFailure | |
4619 * Protocol informs mechanism implementation about SASL failure. | |
4620 */ | |
4621 onFailure: function onFailure() { | |
4622 this._connection = null; | |
4623 }, | |
4624 | |
4625 /** PrivateFunction: onSuccess | |
4626 * Protocol informs mechanism implementation about SASL success. | |
4627 */ | |
4628 onSuccess: function onSuccess() { | |
4629 this._connection = null; | |
4630 } | |
4631 }; | |
4632 /** Constants: SASL mechanisms | |
4633 * Available authentication mechanisms | |
4634 * | |
4635 * Strophe.SASLAnonymous - SASL ANONYMOUS authentication. | |
4636 * Strophe.SASLPlain - SASL PLAIN authentication. | |
4637 * Strophe.SASLMD5 - SASL DIGEST-MD5 authentication | |
4638 * Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication | |
4639 * Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication | |
4640 * Strophe.SASLExternal - SASL EXTERNAL authentication | |
4641 * Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication | |
4642 */ | |
4643 // Building SASL callbacks | |
4644 | |
4645 /** PrivateConstructor: SASLAnonymous | |
4646 * SASL ANONYMOUS authentication. | |
4647 */ | |
4648 | |
4649 Strophe.SASLAnonymous = function () {}; | |
4650 | |
4651 Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism("ANONYMOUS", false, 20); | |
4652 | |
4653 Strophe.SASLAnonymous.prototype.test = function (connection) { | |
4654 return connection.authcid === null; | |
4655 }; | |
4656 /** PrivateConstructor: SASLPlain | |
4657 * SASL PLAIN authentication. | |
4658 */ | |
4659 | |
4660 | |
4661 Strophe.SASLPlain = function () {}; | |
4662 | |
4663 Strophe.SASLPlain.prototype = new Strophe.SASLMechanism("PLAIN", true, 50); | |
4664 | |
4665 Strophe.SASLPlain.prototype.test = function (connection) { | |
4666 return connection.authcid !== null; | |
4667 }; | |
4668 | |
4669 Strophe.SASLPlain.prototype.onChallenge = function (connection) { | |
4670 var auth_str = connection.authzid; | |
4671 auth_str = auth_str + "\0"; | |
4672 auth_str = auth_str + connection.authcid; | |
4673 auth_str = auth_str + "\0"; | |
4674 auth_str = auth_str + connection.pass; | |
4675 return utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(auth_str); | |
4676 }; | |
4677 /** PrivateConstructor: SASLSHA1 | |
4678 * SASL SCRAM SHA 1 authentication. | |
4679 */ | |
4680 | |
4681 | |
4682 Strophe.SASLSHA1 = function () {}; | |
4683 | |
4684 Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism("SCRAM-SHA-1", true, 70); | |
4685 | |
4686 Strophe.SASLSHA1.prototype.test = function (connection) { | |
4687 return connection.authcid !== null; | |
4688 }; | |
4689 | |
4690 Strophe.SASLSHA1.prototype.onChallenge = function (connection, challenge, test_cnonce) { | |
4691 var cnonce = test_cnonce || md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(Math.random() * 1234567890); | |
4692 var auth_str = "n=" + utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.authcid); | |
4693 auth_str += ",r="; | |
4694 auth_str += cnonce; | |
4695 connection._sasl_data.cnonce = cnonce; | |
4696 connection._sasl_data["client-first-message-bare"] = auth_str; | |
4697 auth_str = "n,," + auth_str; | |
4698 | |
4699 this.onChallenge = function (connection, challenge) { | |
4700 var nonce, salt, iter, Hi, U, U_old, i, k; | |
4701 var responseText = "c=biws,"; | |
4702 var authMessage = "".concat(connection._sasl_data["client-first-message-bare"], ",").concat(challenge, ","); | |
4703 var cnonce = connection._sasl_data.cnonce; | |
4704 var attribMatch = /([a-z]+)=([^,]+)(,|$)/; | |
4705 | |
4706 while (challenge.match(attribMatch)) { | |
4707 var matches = challenge.match(attribMatch); | |
4708 challenge = challenge.replace(matches[0], ""); | |
4709 | |
4710 switch (matches[1]) { | |
4711 case "r": | |
4712 nonce = matches[2]; | |
4713 break; | |
4714 | |
4715 case "s": | |
4716 salt = matches[2]; | |
4717 break; | |
4718 | |
4719 case "i": | |
4720 iter = matches[2]; | |
4721 break; | |
4722 } | |
4723 } | |
4724 | |
4725 if (nonce.substr(0, cnonce.length) !== cnonce) { | |
4726 connection._sasl_data = {}; | |
4727 return connection._sasl_failure_cb(); | |
4728 } | |
4729 | |
4730 responseText += "r=" + nonce; | |
4731 authMessage += responseText; | |
4732 salt = atob(salt); | |
4733 salt += "\x00\x00\x00\x01"; | |
4734 var pass = utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.pass); | |
4735 Hi = U_old = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(pass, salt); | |
4736 | |
4737 for (i = 1; i < iter; i++) { | |
4738 U = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(pass, sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(U_old)); | |
4739 | |
4740 for (k = 0; k < 5; k++) { | |
4741 Hi[k] ^= U[k]; | |
4742 } | |
4743 | |
4744 U_old = U; | |
4745 } | |
4746 | |
4747 Hi = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(Hi); | |
4748 var clientKey = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(Hi, "Client Key"); | |
4749 var serverKey = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_hmac_sha1(Hi, "Server Key"); | |
4750 var clientSignature = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_sha1(sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(clientKey)), authMessage); | |
4751 connection._sasl_data["server-signature"] = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].b64_hmac_sha1(serverKey, authMessage); | |
4752 | |
4753 for (k = 0; k < 5; k++) { | |
4754 clientKey[k] ^= clientSignature[k]; | |
4755 } | |
4756 | |
4757 responseText += ",p=" + btoa(sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(clientKey)); | |
4758 return responseText; | |
4759 }; | |
4760 | |
4761 return auth_str; | |
4762 }; | |
4763 /** PrivateConstructor: SASLMD5 | |
4764 * SASL DIGEST MD5 authentication. | |
4765 */ | |
4766 | |
4767 | |
4768 Strophe.SASLMD5 = function () {}; | |
4769 | |
4770 Strophe.SASLMD5.prototype = new Strophe.SASLMechanism("DIGEST-MD5", false, 60); | |
4771 | |
4772 Strophe.SASLMD5.prototype.test = function (connection) { | |
4773 return connection.authcid !== null; | |
4774 }; | |
4775 /** PrivateFunction: _quote | |
4776 * _Private_ utility function to backslash escape and quote strings. | |
4777 * | |
4778 * Parameters: | |
4779 * (String) str - The string to be quoted. | |
4780 * | |
4781 * Returns: | |
4782 * quoted string | |
4783 */ | |
4784 | |
4785 | |
4786 Strophe.SASLMD5.prototype._quote = function (str) { | |
4787 return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"'; //" end string workaround for emacs | |
4788 }; | |
4789 | |
4790 Strophe.SASLMD5.prototype.onChallenge = function (connection, challenge, test_cnonce) { | |
4791 var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/; | |
4792 var cnonce = test_cnonce || md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest("" + Math.random() * 1234567890); | |
4793 var realm = ""; | |
4794 var host = null; | |
4795 var nonce = ""; | |
4796 var qop = ""; | |
4797 | |
4798 while (challenge.match(attribMatch)) { | |
4799 var matches = challenge.match(attribMatch); | |
4800 challenge = challenge.replace(matches[0], ""); | |
4801 matches[2] = matches[2].replace(/^"(.+)"$/, "$1"); | |
4802 | |
4803 switch (matches[1]) { | |
4804 case "realm": | |
4805 realm = matches[2]; | |
4806 break; | |
4807 | |
4808 case "nonce": | |
4809 nonce = matches[2]; | |
4810 break; | |
4811 | |
4812 case "qop": | |
4813 qop = matches[2]; | |
4814 break; | |
4815 | |
4816 case "host": | |
4817 host = matches[2]; | |
4818 break; | |
4819 } | |
4820 } | |
4821 | |
4822 var digest_uri = connection.servtype + "/" + connection.domain; | |
4823 | |
4824 if (host !== null) { | |
4825 digest_uri = digest_uri + "/" + host; | |
4826 } | |
4827 | |
4828 var cred = utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.authcid + ":" + realm + ":" + this._connection.pass); | |
4829 var A1 = md5__WEBPACK_IMPORTED_MODULE_0__["default"].hash(cred) + ":" + nonce + ":" + cnonce; | |
4830 var A2 = 'AUTHENTICATE:' + digest_uri; | |
4831 var responseText = ""; | |
4832 responseText += 'charset=utf-8,'; | |
4833 responseText += 'username=' + this._quote(utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.authcid)) + ','; | |
4834 responseText += 'realm=' + this._quote(realm) + ','; | |
4835 responseText += 'nonce=' + this._quote(nonce) + ','; | |
4836 responseText += 'nc=00000001,'; | |
4837 responseText += 'cnonce=' + this._quote(cnonce) + ','; | |
4838 responseText += 'digest-uri=' + this._quote(digest_uri) + ','; | |
4839 responseText += 'response=' + md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(A1) + ":" + nonce + ":00000001:" + cnonce + ":auth:" + md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(A2)) + ","; | |
4840 responseText += 'qop=auth'; | |
4841 | |
4842 this.onChallenge = function () { | |
4843 return ""; | |
4844 }; | |
4845 | |
4846 return responseText; | |
4847 }; | |
4848 /** PrivateConstructor: SASLOAuthBearer | |
4849 * SASL OAuth Bearer authentication. | |
4850 */ | |
4851 | |
4852 | |
4853 Strophe.SASLOAuthBearer = function () {}; | |
4854 | |
4855 Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism("OAUTHBEARER", true, 40); | |
4856 | |
4857 Strophe.SASLOAuthBearer.prototype.test = function (connection) { | |
4858 return connection.pass !== null; | |
4859 }; | |
4860 | |
4861 Strophe.SASLOAuthBearer.prototype.onChallenge = function (connection) { | |
4862 var auth_str = 'n,'; | |
4863 | |
4864 if (connection.authcid !== null) { | |
4865 auth_str = auth_str + 'a=' + connection.authzid; | |
4866 } | |
4867 | |
4868 auth_str = auth_str + ','; | |
4869 auth_str = auth_str + "\x01"; | |
4870 auth_str = auth_str + 'auth=Bearer '; | |
4871 auth_str = auth_str + connection.pass; | |
4872 auth_str = auth_str + "\x01"; | |
4873 auth_str = auth_str + "\x01"; | |
4874 return utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(auth_str); | |
4875 }; | |
4876 /** PrivateConstructor: SASLExternal | |
4877 * SASL EXTERNAL authentication. | |
4878 * | |
4879 * The EXTERNAL mechanism allows a client to request the server to use | |
4880 * credentials established by means external to the mechanism to | |
4881 * authenticate the client. The external means may be, for instance, | |
4882 * TLS services. | |
4883 */ | |
4884 | |
4885 | |
4886 Strophe.SASLExternal = function () {}; | |
4887 | |
4888 Strophe.SASLExternal.prototype = new Strophe.SASLMechanism("EXTERNAL", true, 10); | |
4889 | |
4890 Strophe.SASLExternal.prototype.onChallenge = function (connection) { | |
4891 /** According to XEP-178, an authzid SHOULD NOT be presented when the | |
4892 * authcid contained or implied in the client certificate is the JID (i.e. | |
4893 * authzid) with which the user wants to log in as. | |
4894 * | |
4895 * To NOT send the authzid, the user should therefore set the authcid equal | |
4896 * to the JID when instantiating a new Strophe.Connection object. | |
4897 */ | |
4898 return connection.authcid === connection.authzid ? '' : connection.authzid; | |
4899 }; | |
4900 /** PrivateConstructor: SASLXOAuth2 | |
4901 * SASL X-OAuth2 authentication. | |
4902 */ | |
4903 | |
4904 | |
4905 Strophe.SASLXOAuth2 = function () {}; | |
4906 | |
4907 Strophe.SASLXOAuth2.prototype = new Strophe.SASLMechanism("X-OAUTH2", true, 30); | |
4908 | |
4909 Strophe.SASLXOAuth2.prototype.test = function (connection) { | |
4910 return connection.pass !== null; | |
4911 }; | |
4912 | |
4913 Strophe.SASLXOAuth2.prototype.onChallenge = function (connection) { | |
4914 var auth_str = "\0"; | |
4915 | |
4916 if (connection.authcid !== null) { | |
4917 auth_str = auth_str + connection.authzid; | |
4918 } | |
4919 | |
4920 auth_str = auth_str + "\0"; | |
4921 auth_str = auth_str + connection.pass; | |
4922 return utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(auth_str); | |
4923 }; | |
4924 | |
4925 | |
4926 /* harmony default export */ __webpack_exports__["default"] = ({ | |
4927 'Strophe': Strophe, | |
4928 '$build': $build, | |
4929 '$iq': $iq, | |
4930 '$msg': $msg, | |
4931 '$pres': $pres, | |
4932 'SHA1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"], | |
4933 'MD5': md5__WEBPACK_IMPORTED_MODULE_0__["default"], | |
4934 'b64_hmac_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].b64_hmac_sha1, | |
4935 'b64_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].b64_sha1, | |
4936 'str_hmac_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_hmac_sha1, | |
4937 'str_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_sha1 | |
4938 }); | |
4939 | |
4940 /***/ }), | |
4941 | |
4942 /***/ "./src/md5.js": | |
4943 /*!********************!*\ | |
4944 !*** ./src/md5.js ***! | |
4945 \********************/ | |
4946 /*! exports provided: default */ | |
4947 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
4948 | |
4949 "use strict"; | |
4950 __webpack_require__.r(__webpack_exports__); | |
4951 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return MD5; }); | |
4952 /* | |
4953 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message | |
4954 * Digest Algorithm, as defined in RFC 1321. | |
4955 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. | |
4956 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet | |
4957 * Distributed under the BSD License | |
4958 * See http://pajhome.org.uk/crypt/md5 for more info. | |
4959 */ | |
4960 | |
4961 /* | |
4962 * Everything that isn't used by Strophe has been stripped here! | |
4963 */ | |
4964 | |
4965 /* | |
4966 * Add integers, wrapping at 2^32. This uses 16-bit operations internally | |
4967 * to work around bugs in some JS interpreters. | |
4968 */ | |
4969 var safe_add = function safe_add(x, y) { | |
4970 var lsw = (x & 0xFFFF) + (y & 0xFFFF); | |
4971 var msw = (x >> 16) + (y >> 16) + (lsw >> 16); | |
4972 return msw << 16 | lsw & 0xFFFF; | |
4973 }; | |
4974 /* | |
4975 * Bitwise rotate a 32-bit number to the left. | |
4976 */ | |
4977 | |
4978 | |
4979 var bit_rol = function bit_rol(num, cnt) { | |
4980 return num << cnt | num >>> 32 - cnt; | |
4981 }; | |
4982 /* | |
4983 * Convert a string to an array of little-endian words | |
4984 */ | |
4985 | |
4986 | |
4987 var str2binl = function str2binl(str) { | |
4988 var bin = []; | |
4989 | |
4990 for (var i = 0; i < str.length * 8; i += 8) { | |
4991 bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32; | |
4992 } | |
4993 | |
4994 return bin; | |
4995 }; | |
4996 /* | |
4997 * Convert an array of little-endian words to a string | |
4998 */ | |
4999 | |
5000 | |
5001 var binl2str = function binl2str(bin) { | |
5002 var str = ""; | |
5003 | |
5004 for (var i = 0; i < bin.length * 32; i += 8) { | |
5005 str += String.fromCharCode(bin[i >> 5] >>> i % 32 & 255); | |
5006 } | |
5007 | |
5008 return str; | |
5009 }; | |
5010 /* | |
5011 * Convert an array of little-endian words to a hex string. | |
5012 */ | |
5013 | |
5014 | |
5015 var binl2hex = function binl2hex(binarray) { | |
5016 var hex_tab = "0123456789abcdef"; | |
5017 var str = ""; | |
5018 | |
5019 for (var i = 0; i < binarray.length * 4; i++) { | |
5020 str += hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 + 4 & 0xF) + hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 & 0xF); | |
5021 } | |
5022 | |
5023 return str; | |
5024 }; | |
5025 /* | |
5026 * These functions implement the four basic operations the algorithm uses. | |
5027 */ | |
5028 | |
5029 | |
5030 var md5_cmn = function md5_cmn(q, a, b, x, s, t) { | |
5031 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); | |
5032 }; | |
5033 | |
5034 var md5_ff = function md5_ff(a, b, c, d, x, s, t) { | |
5035 return md5_cmn(b & c | ~b & d, a, b, x, s, t); | |
5036 }; | |
5037 | |
5038 var md5_gg = function md5_gg(a, b, c, d, x, s, t) { | |
5039 return md5_cmn(b & d | c & ~d, a, b, x, s, t); | |
5040 }; | |
5041 | |
5042 var md5_hh = function md5_hh(a, b, c, d, x, s, t) { | |
5043 return md5_cmn(b ^ c ^ d, a, b, x, s, t); | |
5044 }; | |
5045 | |
5046 var md5_ii = function md5_ii(a, b, c, d, x, s, t) { | |
5047 return md5_cmn(c ^ (b | ~d), a, b, x, s, t); | |
5048 }; | |
5049 /* | |
5050 * Calculate the MD5 of an array of little-endian words, and a bit length | |
5051 */ | |
5052 | |
5053 | |
5054 var core_md5 = function core_md5(x, len) { | |
5055 /* append padding */ | |
5056 x[len >> 5] |= 0x80 << len % 32; | |
5057 x[(len + 64 >>> 9 << 4) + 14] = len; | |
5058 var a = 1732584193; | |
5059 var b = -271733879; | |
5060 var c = -1732584194; | |
5061 var d = 271733878; | |
5062 var olda, oldb, oldc, oldd; | |
5063 | |
5064 for (var i = 0; i < x.length; i += 16) { | |
5065 olda = a; | |
5066 oldb = b; | |
5067 oldc = c; | |
5068 oldd = d; | |
5069 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936); | |
5070 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); | |
5071 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); | |
5072 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); | |
5073 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); | |
5074 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); | |
5075 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); | |
5076 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); | |
5077 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); | |
5078 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); | |
5079 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); | |
5080 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); | |
5081 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); | |
5082 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); | |
5083 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); | |
5084 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); | |
5085 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); | |
5086 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); | |
5087 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); | |
5088 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302); | |
5089 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); | |
5090 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); | |
5091 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); | |
5092 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); | |
5093 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); | |
5094 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); | |
5095 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); | |
5096 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); | |
5097 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); | |
5098 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); | |
5099 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); | |
5100 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); | |
5101 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); | |
5102 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); | |
5103 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); | |
5104 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); | |
5105 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); | |
5106 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); | |
5107 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); | |
5108 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); | |
5109 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); | |
5110 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222); | |
5111 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); | |
5112 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); | |
5113 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); | |
5114 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); | |
5115 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); | |
5116 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); | |
5117 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844); | |
5118 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); | |
5119 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); | |
5120 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); | |
5121 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); | |
5122 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); | |
5123 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); | |
5124 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); | |
5125 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); | |
5126 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); | |
5127 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); | |
5128 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); | |
5129 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); | |
5130 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); | |
5131 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); | |
5132 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); | |
5133 a = safe_add(a, olda); | |
5134 b = safe_add(b, oldb); | |
5135 c = safe_add(c, oldc); | |
5136 d = safe_add(d, oldd); | |
5137 } | |
5138 | |
5139 return [a, b, c, d]; | |
5140 }; | |
5141 /* | |
5142 * These are the functions you'll usually want to call. | |
5143 * They take string arguments and return either hex or base-64 encoded | |
5144 * strings. | |
5145 */ | |
5146 | |
5147 | |
5148 var MD5 = { | |
5149 hexdigest: function hexdigest(s) { | |
5150 return binl2hex(core_md5(str2binl(s), s.length * 8)); | |
5151 }, | |
5152 hash: function hash(s) { | |
5153 return binl2str(core_md5(str2binl(s), s.length * 8)); | |
5154 } | |
5155 }; | |
5156 | |
5157 | |
5158 /***/ }), | |
5159 | |
5160 /***/ "./src/sha1.js": | |
5161 /*!*********************!*\ | |
5162 !*** ./src/sha1.js ***! | |
5163 \*********************/ | |
5164 /*! exports provided: default */ | |
5165 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
5166 | |
5167 "use strict"; | |
5168 __webpack_require__.r(__webpack_exports__); | |
5169 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return SHA1; }); | |
5170 /* | |
5171 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined | |
5172 * in FIPS PUB 180-1 | |
5173 * Version 2.1a Copyright Paul Johnston 2000 - 2002. | |
5174 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet | |
5175 * Distributed under the BSD License | |
5176 * See http://pajhome.org.uk/crypt/md5 for details. | |
5177 */ | |
5178 | |
5179 /* jshint undef: true, unused: true:, noarg: true, latedef: false */ | |
5180 | |
5181 /* global define */ | |
5182 | |
5183 /* Some functions and variables have been stripped for use with Strophe */ | |
5184 | |
5185 /* | |
5186 * Calculate the SHA-1 of an array of big-endian words, and a bit length | |
5187 */ | |
5188 function core_sha1(x, len) { | |
5189 /* append padding */ | |
5190 x[len >> 5] |= 0x80 << 24 - len % 32; | |
5191 x[(len + 64 >> 9 << 4) + 15] = len; | |
5192 var w = new Array(80); | |
5193 var a = 1732584193; | |
5194 var b = -271733879; | |
5195 var c = -1732584194; | |
5196 var d = 271733878; | |
5197 var e = -1009589776; | |
5198 var i, j, t, olda, oldb, oldc, oldd, olde; | |
5199 | |
5200 for (i = 0; i < x.length; i += 16) { | |
5201 olda = a; | |
5202 oldb = b; | |
5203 oldc = c; | |
5204 oldd = d; | |
5205 olde = e; | |
5206 | |
5207 for (j = 0; j < 80; j++) { | |
5208 if (j < 16) { | |
5209 w[j] = x[i + j]; | |
5210 } else { | |
5211 w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); | |
5212 } | |
5213 | |
5214 t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); | |
5215 e = d; | |
5216 d = c; | |
5217 c = rol(b, 30); | |
5218 b = a; | |
5219 a = t; | |
5220 } | |
5221 | |
5222 a = safe_add(a, olda); | |
5223 b = safe_add(b, oldb); | |
5224 c = safe_add(c, oldc); | |
5225 d = safe_add(d, oldd); | |
5226 e = safe_add(e, olde); | |
5227 } | |
5228 | |
5229 return [a, b, c, d, e]; | |
5230 } | |
5231 /* | |
5232 * Perform the appropriate triplet combination function for the current | |
5233 * iteration | |
5234 */ | |
5235 | |
5236 | |
5237 function sha1_ft(t, b, c, d) { | |
5238 if (t < 20) { | |
5239 return b & c | ~b & d; | |
5240 } | |
5241 | |
5242 if (t < 40) { | |
5243 return b ^ c ^ d; | |
5244 } | |
5245 | |
5246 if (t < 60) { | |
5247 return b & c | b & d | c & d; | |
5248 } | |
5249 | |
5250 return b ^ c ^ d; | |
5251 } | |
5252 /* | |
5253 * Determine the appropriate additive constant for the current iteration | |
5254 */ | |
5255 | |
5256 | |
5257 function sha1_kt(t) { | |
5258 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514; | |
5259 } | |
5260 /* | |
5261 * Calculate the HMAC-SHA1 of a key and some data | |
5262 */ | |
5263 | |
5264 | |
5265 function core_hmac_sha1(key, data) { | |
5266 var bkey = str2binb(key); | |
5267 | |
5268 if (bkey.length > 16) { | |
5269 bkey = core_sha1(bkey, key.length * 8); | |
5270 } | |
5271 | |
5272 var ipad = new Array(16), | |
5273 opad = new Array(16); | |
5274 | |
5275 for (var i = 0; i < 16; i++) { | |
5276 ipad[i] = bkey[i] ^ 0x36363636; | |
5277 opad[i] = bkey[i] ^ 0x5C5C5C5C; | |
5278 } | |
5279 | |
5280 var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8); | |
5281 return core_sha1(opad.concat(hash), 512 + 160); | |
5282 } | |
5283 /* | |
5284 * Add integers, wrapping at 2^32. This uses 16-bit operations internally | |
5285 * to work around bugs in some JS interpreters. | |
5286 */ | |
5287 | |
5288 | |
5289 function safe_add(x, y) { | |
5290 var lsw = (x & 0xFFFF) + (y & 0xFFFF); | |
5291 var msw = (x >> 16) + (y >> 16) + (lsw >> 16); | |
5292 return msw << 16 | lsw & 0xFFFF; | |
5293 } | |
5294 /* | |
5295 * Bitwise rotate a 32-bit number to the left. | |
5296 */ | |
5297 | |
5298 | |
5299 function rol(num, cnt) { | |
5300 return num << cnt | num >>> 32 - cnt; | |
5301 } | |
5302 /* | |
5303 * Convert an 8-bit or 16-bit string to an array of big-endian words | |
5304 * In 8-bit function, characters >255 have their hi-byte silently ignored. | |
5305 */ | |
5306 | |
5307 | |
5308 function str2binb(str) { | |
5309 var bin = []; | |
5310 var mask = 255; | |
5311 | |
5312 for (var i = 0; i < str.length * 8; i += 8) { | |
5313 bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << 24 - i % 32; | |
5314 } | |
5315 | |
5316 return bin; | |
5317 } | |
5318 /* | |
5319 * Convert an array of big-endian words to a base-64 string | |
5320 */ | |
5321 | |
5322 | |
5323 function binb2b64(binarray) { | |
5324 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
5325 var str = ""; | |
5326 var triplet, j; | |
5327 | |
5328 for (var i = 0; i < binarray.length * 4; i += 3) { | |
5329 triplet = (binarray[i >> 2] >> 8 * (3 - i % 4) & 0xFF) << 16 | (binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4) & 0xFF) << 8 | binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4) & 0xFF; | |
5330 | |
5331 for (j = 0; j < 4; j++) { | |
5332 if (i * 8 + j * 6 > binarray.length * 32) { | |
5333 str += "="; | |
5334 } else { | |
5335 str += tab.charAt(triplet >> 6 * (3 - j) & 0x3F); | |
5336 } | |
5337 } | |
5338 } | |
5339 | |
5340 return str; | |
5341 } | |
5342 /* | |
5343 * Convert an array of big-endian words to a string | |
5344 */ | |
5345 | |
5346 | |
5347 function binb2str(bin) { | |
5348 var str = ""; | |
5349 var mask = 255; | |
5350 | |
5351 for (var i = 0; i < bin.length * 32; i += 8) { | |
5352 str += String.fromCharCode(bin[i >> 5] >>> 24 - i % 32 & mask); | |
5353 } | |
5354 | |
5355 return str; | |
5356 } | |
5357 /* | |
5358 * These are the functions you'll usually want to call | |
5359 * They take string arguments and return either hex or base-64 encoded strings | |
5360 */ | |
5361 | |
5362 | |
5363 var SHA1 = { | |
5364 b64_hmac_sha1: function b64_hmac_sha1(key, data) { | |
5365 return binb2b64(core_hmac_sha1(key, data)); | |
5366 }, | |
5367 b64_sha1: function b64_sha1(s) { | |
5368 return binb2b64(core_sha1(str2binb(s), s.length * 8)); | |
5369 }, | |
5370 binb2str: binb2str, | |
5371 core_hmac_sha1: core_hmac_sha1, | |
5372 str_hmac_sha1: function str_hmac_sha1(key, data) { | |
5373 return binb2str(core_hmac_sha1(key, data)); | |
5374 }, | |
5375 str_sha1: function str_sha1(s) { | |
5376 return binb2str(core_sha1(str2binb(s), s.length * 8)); | |
5377 } | |
5378 }; | |
5379 | |
5380 | |
5381 /***/ }), | |
5382 | |
5383 /***/ "./src/strophe.js": | |
5384 /*!************************!*\ | |
5385 !*** ./src/strophe.js ***! | |
5386 \************************/ | |
5387 /*! exports provided: default */ | |
5388 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
5389 | |
5390 "use strict"; | |
5391 __webpack_require__.r(__webpack_exports__); | |
5392 /* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core */ "./src/core.js"); | |
5393 /* harmony import */ var bosh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! bosh */ "./src/bosh.js"); | |
5394 /* harmony import */ var websocket__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! websocket */ "./src/websocket.js"); | |
5395 /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "default", function() { return core__WEBPACK_IMPORTED_MODULE_0__["default"]; }); | |
5396 | |
5397 /*global global*/ | |
5398 | |
5399 | |
5400 | |
5401 global.Strophe = core__WEBPACK_IMPORTED_MODULE_0__["default"].Strophe; | |
5402 global.$build = core__WEBPACK_IMPORTED_MODULE_0__["default"].$build; | |
5403 global.$iq = core__WEBPACK_IMPORTED_MODULE_0__["default"].$iq; | |
5404 global.$msg = core__WEBPACK_IMPORTED_MODULE_0__["default"].$msg; | |
5405 global.$pres = core__WEBPACK_IMPORTED_MODULE_0__["default"].$pres; | |
5406 | |
5407 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) | |
5408 | |
5409 /***/ }), | |
5410 | |
5411 /***/ "./src/utils.js": | |
5412 /*!**********************!*\ | |
5413 !*** ./src/utils.js ***! | |
5414 \**********************/ | |
5415 /*! exports provided: default */ | |
5416 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
5417 | |
5418 "use strict"; | |
5419 __webpack_require__.r(__webpack_exports__); | |
5420 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return utils; }); | |
5421 function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | |
5422 | |
5423 var utils = { | |
5424 utf16to8: function utf16to8(str) { | |
5425 var i, c; | |
5426 var out = ""; | |
5427 var len = str.length; | |
5428 | |
5429 for (i = 0; i < len; i++) { | |
5430 c = str.charCodeAt(i); | |
5431 | |
5432 if (c >= 0x0000 && c <= 0x007F) { | |
5433 out += str.charAt(i); | |
5434 } else if (c > 0x07FF) { | |
5435 out += String.fromCharCode(0xE0 | c >> 12 & 0x0F); | |
5436 out += String.fromCharCode(0x80 | c >> 6 & 0x3F); | |
5437 out += String.fromCharCode(0x80 | c >> 0 & 0x3F); | |
5438 } else { | |
5439 out += String.fromCharCode(0xC0 | c >> 6 & 0x1F); | |
5440 out += String.fromCharCode(0x80 | c >> 0 & 0x3F); | |
5441 } | |
5442 } | |
5443 | |
5444 return out; | |
5445 }, | |
5446 addCookies: function addCookies(cookies) { | |
5447 /* Parameters: | |
5448 * (Object) cookies - either a map of cookie names | |
5449 * to string values or to maps of cookie values. | |
5450 * | |
5451 * For example: | |
5452 * { "myCookie": "1234" } | |
5453 * | |
5454 * or: | |
5455 * { "myCookie": { | |
5456 * "value": "1234", | |
5457 * "domain": ".example.org", | |
5458 * "path": "/", | |
5459 * "expires": expirationDate | |
5460 * } | |
5461 * } | |
5462 * | |
5463 * These values get passed to Strophe.Connection via | |
5464 * options.cookies | |
5465 */ | |
5466 cookies = cookies || {}; | |
5467 | |
5468 for (var cookieName in cookies) { | |
5469 if (Object.prototype.hasOwnProperty.call(cookies, cookieName)) { | |
5470 var expires = ''; | |
5471 var domain = ''; | |
5472 var path = ''; | |
5473 var cookieObj = cookies[cookieName]; | |
5474 var isObj = _typeof(cookieObj) === "object"; | |
5475 var cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj)); | |
5476 | |
5477 if (isObj) { | |
5478 expires = cookieObj.expires ? ";expires=" + cookieObj.expires : ''; | |
5479 domain = cookieObj.domain ? ";domain=" + cookieObj.domain : ''; | |
5480 path = cookieObj.path ? ";path=" + cookieObj.path : ''; | |
5481 } | |
5482 | |
5483 document.cookie = cookieName + '=' + cookieValue + expires + domain + path; | |
5484 } | |
5485 } | |
5486 } | |
5487 }; | |
5488 | |
5489 | |
5490 /***/ }), | |
5491 | |
5492 /***/ "./src/websocket.js": | |
5493 /*!**************************!*\ | |
5494 !*** ./src/websocket.js ***! | |
5495 \**************************/ | |
5496 /*! no exports provided */ | |
5497 /***/ (function(module, __webpack_exports__, __webpack_require__) { | |
5498 | |
5499 "use strict"; | |
5500 __webpack_require__.r(__webpack_exports__); | |
5501 /* harmony import */ var core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core */ "./src/core.js"); | |
5502 /* | |
5503 This program is distributed under the terms of the MIT license. | |
5504 Please see the LICENSE file for details. | |
5505 | |
5506 Copyright 2006-2008, OGG, LLC | |
5507 */ | |
5508 | |
5509 /* global window, clearTimeout, WebSocket, DOMParser */ | |
5510 | |
5511 var Strophe = core__WEBPACK_IMPORTED_MODULE_0__["default"].Strophe; | |
5512 var $build = core__WEBPACK_IMPORTED_MODULE_0__["default"].$build; | |
5513 /** Class: Strophe.WebSocket | |
5514 * _Private_ helper class that handles WebSocket Connections | |
5515 * | |
5516 * The Strophe.WebSocket class is used internally by Strophe.Connection | |
5517 * to encapsulate WebSocket sessions. It is not meant to be used from user's code. | |
5518 */ | |
5519 | |
5520 /** File: websocket.js | |
5521 * A JavaScript library to enable XMPP over Websocket in Strophejs. | |
5522 * | |
5523 * This file implements XMPP over WebSockets for Strophejs. | |
5524 * If a Connection is established with a Websocket url (ws://...) | |
5525 * Strophe will use WebSockets. | |
5526 * For more information on XMPP-over-WebSocket see RFC 7395: | |
5527 * http://tools.ietf.org/html/rfc7395 | |
5528 * | |
5529 * WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de) | |
5530 */ | |
5531 | |
5532 /** PrivateConstructor: Strophe.Websocket | |
5533 * Create and initialize a Strophe.WebSocket object. | |
5534 * Currently only sets the connection Object. | |
5535 * | |
5536 * Parameters: | |
5537 * (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets. | |
5538 * | |
5539 * Returns: | |
5540 * A new Strophe.WebSocket object. | |
5541 */ | |
5542 | |
5543 Strophe.Websocket = function (connection) { | |
5544 this._conn = connection; | |
5545 this.strip = "wrapper"; | |
5546 var service = connection.service; | |
5547 | |
5548 if (service.indexOf("ws:") !== 0 && service.indexOf("wss:") !== 0) { | |
5549 // If the service is not an absolute URL, assume it is a path and put the absolute | |
5550 // URL together from options, current URL and the path. | |
5551 var new_service = ""; | |
5552 | |
5553 if (connection.options.protocol === "ws" && window.location.protocol !== "https:") { | |
5554 new_service += "ws"; | |
5555 } else { | |
5556 new_service += "wss"; | |
5557 } | |
5558 | |
5559 new_service += "://" + window.location.host; | |
5560 | |
5561 if (service.indexOf("/") !== 0) { | |
5562 new_service += window.location.pathname + service; | |
5563 } else { | |
5564 new_service += service; | |
5565 } | |
5566 | |
5567 connection.service = new_service; | |
5568 } | |
5569 }; | |
5570 | |
5571 Strophe.Websocket.prototype = { | |
5572 /** PrivateFunction: _buildStream | |
5573 * _Private_ helper function to generate the <stream> start tag for WebSockets | |
5574 * | |
5575 * Returns: | |
5576 * A Strophe.Builder with a <stream> element. | |
5577 */ | |
5578 _buildStream: function _buildStream() { | |
5579 return $build("open", { | |
5580 "xmlns": Strophe.NS.FRAMING, | |
5581 "to": this._conn.domain, | |
5582 "version": '1.0' | |
5583 }); | |
5584 }, | |
5585 | |
5586 /** PrivateFunction: _check_streamerror | |
5587 * _Private_ checks a message for stream:error | |
5588 * | |
5589 * Parameters: | |
5590 * (Strophe.Request) bodyWrap - The received stanza. | |
5591 * connectstatus - The ConnectStatus that will be set on error. | |
5592 * Returns: | |
5593 * true if there was a streamerror, false otherwise. | |
5594 */ | |
5595 _check_streamerror: function _check_streamerror(bodyWrap, connectstatus) { | |
5596 var errors; | |
5597 | |
5598 if (bodyWrap.getElementsByTagNameNS) { | |
5599 errors = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "error"); | |
5600 } else { | |
5601 errors = bodyWrap.getElementsByTagName("stream:error"); | |
5602 } | |
5603 | |
5604 if (errors.length === 0) { | |
5605 return false; | |
5606 } | |
5607 | |
5608 var error = errors[0]; | |
5609 var condition = ""; | |
5610 var text = ""; | |
5611 var ns = "urn:ietf:params:xml:ns:xmpp-streams"; | |
5612 | |
5613 for (var i = 0; i < error.childNodes.length; i++) { | |
5614 var e = error.childNodes[i]; | |
5615 | |
5616 if (e.getAttribute("xmlns") !== ns) { | |
5617 break; | |
5618 } | |
5619 | |
5620 if (e.nodeName === "text") { | |
5621 text = e.textContent; | |
5622 } else { | |
5623 condition = e.nodeName; | |
5624 } | |
5625 } | |
5626 | |
5627 var errorString = "WebSocket stream error: "; | |
5628 | |
5629 if (condition) { | |
5630 errorString += condition; | |
5631 } else { | |
5632 errorString += "unknown"; | |
5633 } | |
5634 | |
5635 if (text) { | |
5636 errorString += " - " + text; | |
5637 } | |
5638 | |
5639 Strophe.error(errorString); // close the connection on stream_error | |
5640 | |
5641 this._conn._changeConnectStatus(connectstatus, condition); | |
5642 | |
5643 this._conn._doDisconnect(); | |
5644 | |
5645 return true; | |
5646 }, | |
5647 | |
5648 /** PrivateFunction: _reset | |
5649 * Reset the connection. | |
5650 * | |
5651 * This function is called by the reset function of the Strophe Connection. | |
5652 * Is not needed by WebSockets. | |
5653 */ | |
5654 _reset: function _reset() { | |
5655 return; | |
5656 }, | |
5657 | |
5658 /** PrivateFunction: _connect | |
5659 * _Private_ function called by Strophe.Connection.connect | |
5660 * | |
5661 * Creates a WebSocket for a connection and assigns Callbacks to it. | |
5662 * Does nothing if there already is a WebSocket. | |
5663 */ | |
5664 _connect: function _connect() { | |
5665 // Ensure that there is no open WebSocket from a previous Connection. | |
5666 this._closeSocket(); // Create the new WobSocket | |
5667 | |
5668 | |
5669 this.socket = new WebSocket(this._conn.service, "xmpp"); | |
5670 this.socket.onopen = this._onOpen.bind(this); | |
5671 this.socket.onerror = this._onError.bind(this); | |
5672 this.socket.onclose = this._onClose.bind(this); | |
5673 this.socket.onmessage = this._connect_cb_wrapper.bind(this); | |
5674 }, | |
5675 | |
5676 /** PrivateFunction: _connect_cb | |
5677 * _Private_ function called by Strophe.Connection._connect_cb | |
5678 * | |
5679 * checks for stream:error | |
5680 * | |
5681 * Parameters: | |
5682 * (Strophe.Request) bodyWrap - The received stanza. | |
5683 */ | |
5684 _connect_cb: function _connect_cb(bodyWrap) { | |
5685 var error = this._check_streamerror(bodyWrap, Strophe.Status.CONNFAIL); | |
5686 | |
5687 if (error) { | |
5688 return Strophe.Status.CONNFAIL; | |
5689 } | |
5690 }, | |
5691 | |
5692 /** PrivateFunction: _handleStreamStart | |
5693 * _Private_ function that checks the opening <open /> tag for errors. | |
5694 * | |
5695 * Disconnects if there is an error and returns false, true otherwise. | |
5696 * | |
5697 * Parameters: | |
5698 * (Node) message - Stanza containing the <open /> tag. | |
5699 */ | |
5700 _handleStreamStart: function _handleStreamStart(message) { | |
5701 var error = false; // Check for errors in the <open /> tag | |
5702 | |
5703 var ns = message.getAttribute("xmlns"); | |
5704 | |
5705 if (typeof ns !== "string") { | |
5706 error = "Missing xmlns in <open />"; | |
5707 } else if (ns !== Strophe.NS.FRAMING) { | |
5708 error = "Wrong xmlns in <open />: " + ns; | |
5709 } | |
5710 | |
5711 var ver = message.getAttribute("version"); | |
5712 | |
5713 if (typeof ver !== "string") { | |
5714 error = "Missing version in <open />"; | |
5715 } else if (ver !== "1.0") { | |
5716 error = "Wrong version in <open />: " + ver; | |
5717 } | |
5718 | |
5719 if (error) { | |
5720 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, error); | |
5721 | |
5722 this._conn._doDisconnect(); | |
5723 | |
5724 return false; | |
5725 } | |
5726 | |
5727 return true; | |
5728 }, | |
5729 | |
5730 /** PrivateFunction: _connect_cb_wrapper | |
5731 * _Private_ function that handles the first connection messages. | |
5732 * | |
5733 * On receiving an opening stream tag this callback replaces itself with the real | |
5734 * message handler. On receiving a stream error the connection is terminated. | |
5735 */ | |
5736 _connect_cb_wrapper: function _connect_cb_wrapper(message) { | |
5737 if (message.data.indexOf("<open ") === 0 || message.data.indexOf("<?xml") === 0) { | |
5738 // Strip the XML Declaration, if there is one | |
5739 var data = message.data.replace(/^(<\?.*?\?>\s*)*/, ""); | |
5740 if (data === '') return; | |
5741 var streamStart = new DOMParser().parseFromString(data, "text/xml").documentElement; | |
5742 | |
5743 this._conn.xmlInput(streamStart); | |
5744 | |
5745 this._conn.rawInput(message.data); //_handleStreamSteart will check for XML errors and disconnect on error | |
5746 | |
5747 | |
5748 if (this._handleStreamStart(streamStart)) { | |
5749 //_connect_cb will check for stream:error and disconnect on error | |
5750 this._connect_cb(streamStart); | |
5751 } | |
5752 } else if (message.data.indexOf("<close ") === 0) { | |
5753 // <close xmlns="urn:ietf:params:xml:ns:xmpp-framing /> | |
5754 // Parse the raw string to an XML element | |
5755 var parsedMessage = new DOMParser().parseFromString(message.data, "text/xml").documentElement; // Report this input to the raw and xml handlers | |
5756 | |
5757 this._conn.xmlInput(parsedMessage); | |
5758 | |
5759 this._conn.rawInput(message.data); | |
5760 | |
5761 var see_uri = parsedMessage.getAttribute("see-other-uri"); | |
5762 | |
5763 if (see_uri) { | |
5764 var service = this._conn.service; // Valid scenarios: WSS->WSS, WS->ANY | |
5765 | |
5766 var isSecureRedirect = service.indexOf("wss:") >= 0 && see_uri.indexOf("wss:") >= 0 || service.indexOf("ws:") >= 0; | |
5767 | |
5768 if (isSecureRedirect) { | |
5769 this._conn._changeConnectStatus(Strophe.Status.REDIRECT, "Received see-other-uri, resetting connection"); | |
5770 | |
5771 this._conn.reset(); | |
5772 | |
5773 this._conn.service = see_uri; | |
5774 | |
5775 this._connect(); | |
5776 } | |
5777 } else { | |
5778 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Received closing stream"); | |
5779 | |
5780 this._conn._doDisconnect(); | |
5781 } | |
5782 } else { | |
5783 var string = this._streamWrap(message.data); | |
5784 | |
5785 var elem = new DOMParser().parseFromString(string, "text/xml").documentElement; | |
5786 this.socket.onmessage = this._onMessage.bind(this); | |
5787 | |
5788 this._conn._connect_cb(elem, null, message.data); | |
5789 } | |
5790 }, | |
5791 | |
5792 /** PrivateFunction: _disconnect | |
5793 * _Private_ function called by Strophe.Connection.disconnect | |
5794 * | |
5795 * Disconnects and sends a last stanza if one is given | |
5796 * | |
5797 * Parameters: | |
5798 * (Request) pres - This stanza will be sent before disconnecting. | |
5799 */ | |
5800 _disconnect: function _disconnect(pres) { | |
5801 if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { | |
5802 if (pres) { | |
5803 this._conn.send(pres); | |
5804 } | |
5805 | |
5806 var close = $build("close", { | |
5807 "xmlns": Strophe.NS.FRAMING | |
5808 }); | |
5809 | |
5810 this._conn.xmlOutput(close.tree()); | |
5811 | |
5812 var closeString = Strophe.serialize(close); | |
5813 | |
5814 this._conn.rawOutput(closeString); | |
5815 | |
5816 try { | |
5817 this.socket.send(closeString); | |
5818 } catch (e) { | |
5819 Strophe.info("Couldn't send <close /> tag."); | |
5820 } | |
5821 } | |
5822 | |
5823 this._conn._doDisconnect(); | |
5824 }, | |
5825 | |
5826 /** PrivateFunction: _doDisconnect | |
5827 * _Private_ function to disconnect. | |
5828 * | |
5829 * Just closes the Socket for WebSockets | |
5830 */ | |
5831 _doDisconnect: function _doDisconnect() { | |
5832 Strophe.info("WebSockets _doDisconnect was called"); | |
5833 | |
5834 this._closeSocket(); | |
5835 }, | |
5836 | |
5837 /** PrivateFunction _streamWrap | |
5838 * _Private_ helper function to wrap a stanza in a <stream> tag. | |
5839 * This is used so Strophe can process stanzas from WebSockets like BOSH | |
5840 */ | |
5841 _streamWrap: function _streamWrap(stanza) { | |
5842 return "<wrapper>" + stanza + '</wrapper>'; | |
5843 }, | |
5844 | |
5845 /** PrivateFunction: _closeSocket | |
5846 * _Private_ function to close the WebSocket. | |
5847 * | |
5848 * Closes the socket if it is still open and deletes it | |
5849 */ | |
5850 _closeSocket: function _closeSocket() { | |
5851 if (this.socket) { | |
5852 try { | |
5853 this.socket.onerror = null; | |
5854 this.socket.close(); | |
5855 } catch (e) { | |
5856 Strophe.debug(e.message); | |
5857 } | |
5858 } | |
5859 | |
5860 this.socket = null; | |
5861 }, | |
5862 | |
5863 /** PrivateFunction: _emptyQueue | |
5864 * _Private_ function to check if the message queue is empty. | |
5865 * | |
5866 * Returns: | |
5867 * True, because WebSocket messages are send immediately after queueing. | |
5868 */ | |
5869 _emptyQueue: function _emptyQueue() { | |
5870 return true; | |
5871 }, | |
5872 | |
5873 /** PrivateFunction: _onClose | |
5874 * _Private_ function to handle websockets closing. | |
5875 * | |
5876 * Nothing to do here for WebSockets | |
5877 */ | |
5878 _onClose: function _onClose(e) { | |
5879 if (this._conn.connected && !this._conn.disconnecting) { | |
5880 Strophe.error("Websocket closed unexpectedly"); | |
5881 | |
5882 this._conn._doDisconnect(); | |
5883 } else if (e && e.code === 1006 && !this._conn.connected && this.socket) { | |
5884 // in case the onError callback was not called (Safari 10 does not | |
5885 // call onerror when the initial connection fails) we need to | |
5886 // dispatch a CONNFAIL status update to be consistent with the | |
5887 // behavior on other browsers. | |
5888 Strophe.error("Websocket closed unexcectedly"); | |
5889 | |
5890 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected."); | |
5891 | |
5892 this._conn._doDisconnect(); | |
5893 } else { | |
5894 Strophe.info("Websocket closed"); | |
5895 } | |
5896 }, | |
5897 | |
5898 /** PrivateFunction: _no_auth_received | |
5899 * | |
5900 * Called on stream start/restart when no stream:features | |
5901 * has been received. | |
5902 */ | |
5903 _no_auth_received: function _no_auth_received(callback) { | |
5904 Strophe.error("Server did not offer a supported authentication mechanism"); | |
5905 | |
5906 this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.NO_AUTH_MECH); | |
5907 | |
5908 if (callback) { | |
5909 callback.call(this._conn); | |
5910 } | |
5911 | |
5912 this._conn._doDisconnect(); | |
5913 }, | |
5914 | |
5915 /** PrivateFunction: _onDisconnectTimeout | |
5916 * _Private_ timeout handler for handling non-graceful disconnection. | |
5917 * | |
5918 * This does nothing for WebSockets | |
5919 */ | |
5920 _onDisconnectTimeout: function _onDisconnectTimeout() {}, | |
5921 | |
5922 /** PrivateFunction: _abortAllRequests | |
5923 * _Private_ helper function that makes sure all pending requests are aborted. | |
5924 */ | |
5925 _abortAllRequests: function _abortAllRequests() {}, | |
5926 | |
5927 /** PrivateFunction: _onError | |
5928 * _Private_ function to handle websockets errors. | |
5929 * | |
5930 * Parameters: | |
5931 * (Object) error - The websocket error. | |
5932 */ | |
5933 _onError: function _onError(error) { | |
5934 Strophe.error("Websocket error " + error); | |
5935 | |
5936 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected."); | |
5937 | |
5938 this._disconnect(); | |
5939 }, | |
5940 | |
5941 /** PrivateFunction: _onIdle | |
5942 * _Private_ function called by Strophe.Connection._onIdle | |
5943 * | |
5944 * sends all queued stanzas | |
5945 */ | |
5946 _onIdle: function _onIdle() { | |
5947 var data = this._conn._data; | |
5948 | |
5949 if (data.length > 0 && !this._conn.paused) { | |
5950 for (var i = 0; i < data.length; i++) { | |
5951 if (data[i] !== null) { | |
5952 var stanza = void 0; | |
5953 | |
5954 if (data[i] === "restart") { | |
5955 stanza = this._buildStream().tree(); | |
5956 } else { | |
5957 stanza = data[i]; | |
5958 } | |
5959 | |
5960 var rawStanza = Strophe.serialize(stanza); | |
5961 | |
5962 this._conn.xmlOutput(stanza); | |
5963 | |
5964 this._conn.rawOutput(rawStanza); | |
5965 | |
5966 this.socket.send(rawStanza); | |
5967 } | |
5968 } | |
5969 | |
5970 this._conn._data = []; | |
5971 } | |
5972 }, | |
5973 | |
5974 /** PrivateFunction: _onMessage | |
5975 * _Private_ function to handle websockets messages. | |
5976 * | |
5977 * This function parses each of the messages as if they are full documents. | |
5978 * [TODO : We may actually want to use a SAX Push parser]. | |
5979 * | |
5980 * Since all XMPP traffic starts with | |
5981 * <stream:stream version='1.0' | |
5982 * xml:lang='en' | |
5983 * xmlns='jabber:client' | |
5984 * xmlns:stream='http://etherx.jabber.org/streams' | |
5985 * id='3697395463' | |
5986 * from='SERVER'> | |
5987 * | |
5988 * The first stanza will always fail to be parsed. | |
5989 * | |
5990 * Additionally, the seconds stanza will always be <stream:features> with | |
5991 * the stream NS defined in the previous stanza, so we need to 'force' | |
5992 * the inclusion of the NS in this stanza. | |
5993 * | |
5994 * Parameters: | |
5995 * (string) message - The websocket message. | |
5996 */ | |
5997 _onMessage: function _onMessage(message) { | |
5998 var elem; // check for closing stream | |
5999 | |
6000 var close = '<close xmlns="urn:ietf:params:xml:ns:xmpp-framing" />'; | |
6001 | |
6002 if (message.data === close) { | |
6003 this._conn.rawInput(close); | |
6004 | |
6005 this._conn.xmlInput(message); | |
6006 | |
6007 if (!this._conn.disconnecting) { | |
6008 this._conn._doDisconnect(); | |
6009 } | |
6010 | |
6011 return; | |
6012 } else if (message.data.search("<open ") === 0) { | |
6013 // This handles stream restarts | |
6014 elem = new DOMParser().parseFromString(message.data, "text/xml").documentElement; | |
6015 | |
6016 if (!this._handleStreamStart(elem)) { | |
6017 return; | |
6018 } | |
6019 } else { | |
6020 var data = this._streamWrap(message.data); | |
6021 | |
6022 elem = new DOMParser().parseFromString(data, "text/xml").documentElement; | |
6023 } | |
6024 | |
6025 if (this._check_streamerror(elem, Strophe.Status.ERROR)) { | |
6026 return; | |
6027 } //handle unavailable presence stanza before disconnecting | |
6028 | |
6029 | |
6030 if (this._conn.disconnecting && elem.firstChild.nodeName === "presence" && elem.firstChild.getAttribute("type") === "unavailable") { | |
6031 this._conn.xmlInput(elem); | |
6032 | |
6033 this._conn.rawInput(Strophe.serialize(elem)); // if we are already disconnecting we will ignore the unavailable stanza and | |
6034 // wait for the </stream:stream> tag before we close the connection | |
6035 | |
6036 | |
6037 return; | |
6038 } | |
6039 | |
6040 this._conn._dataRecv(elem, message.data); | |
6041 }, | |
6042 | |
6043 /** PrivateFunction: _onOpen | |
6044 * _Private_ function to handle websockets connection setup. | |
6045 * | |
6046 * The opening stream tag is sent here. | |
6047 */ | |
6048 _onOpen: function _onOpen() { | |
6049 Strophe.info("Websocket open"); | |
6050 | |
6051 var start = this._buildStream(); | |
6052 | |
6053 this._conn.xmlOutput(start.tree()); | |
6054 | |
6055 var startString = Strophe.serialize(start); | |
6056 | |
6057 this._conn.rawOutput(startString); | |
6058 | |
6059 this.socket.send(startString); | |
6060 }, | |
6061 | |
6062 /** PrivateFunction: _reqToData | |
6063 * _Private_ function to get a stanza out of a request. | |
6064 * | |
6065 * WebSockets don't use requests, so the passed argument is just returned. | |
6066 * | |
6067 * Parameters: | |
6068 * (Object) stanza - The stanza. | |
6069 * | |
6070 * Returns: | |
6071 * The stanza that was passed. | |
6072 */ | |
6073 _reqToData: function _reqToData(stanza) { | |
6074 return stanza; | |
6075 }, | |
6076 | |
6077 /** PrivateFunction: _send | |
6078 * _Private_ part of the Connection.send function for WebSocket | |
6079 * | |
6080 * Just flushes the messages that are in the queue | |
6081 */ | |
6082 _send: function _send() { | |
6083 this._conn.flush(); | |
6084 }, | |
6085 | |
6086 /** PrivateFunction: _sendRestart | |
6087 * | |
6088 * Send an xmpp:restart stanza. | |
6089 */ | |
6090 _sendRestart: function _sendRestart() { | |
6091 clearTimeout(this._conn._idleTimeout); | |
6092 | |
6093 this._conn._onIdle.bind(this._conn)(); | |
6094 } | |
6095 }; | |
6096 | |
6097 /***/ }) | |
6098 | |
6099 /******/ })["default"]; | |
6100 }); | |
6101 //# sourceMappingURL=strophe.js.map |