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, "&amp;");
1620 text = text.replace(/</g, "&lt;");
1621 text = text.replace(/>/g, "&gt;");
1622 text = text.replace(/'/g, "&apos;");
1623 text = text.replace(/"/g, "&quot;");
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(/\&amp;/g, "&");
1638 text = text.replace(/&lt;/g, "<");
1639 text = text.replace(/&gt;/g, ">");
1640 text = text.replace(/&apos;/g, "'");
1641 text = text.replace(/&quot;/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