diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a09ffe..1c61343a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Strophe.js Change Log -## Version 1.2.9 - Unreleased +## Version 1.2.9 - 2016-10-24 * Allow SASL mechanisms to be supported to be passed in as option to `Strophe.Connection` constructor. * Add new matching option to `Strophe.Handler`, namely `ignoreNamespaceFragment`. * The `matchBare` matching option for `Strophe.Handler` has been renamed to diff --git a/Makefile b/Makefile index ac7c02dd..c2c8b9e0 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ release: .PHONY: check check:: + @@$(GRUNT) jshint make stamp-bower $(PHANTOMJS) node_modules/qunit-phantomjs-runner/runner-list.js tests/strophe.html diff --git a/bower.json b/bower.json index c9a10d98..82e1bba8 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "strophe.js", "description": "Strophe.js is an XMPP library for JavaScript", - "version": "1.2.8", + "version": "1.2.9", "license": "MIT", "main": "strophe.js", "authors": [ diff --git a/package.json b/package.json index e02b3760..b256903d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "strophe.js", "description": "Strophe.js is an XMPP library for JavaScript", - "version": "1.2.8", + "version": "1.2.9", "homepage": "http://strophe.im/strophejs", "repository": { "type": "git", @@ -34,9 +34,7 @@ "Brendon Crawford (brendoncrawford)", "JC Brand (jcbrand)" ], - "licenses": [ - "MIT" - ], + "license": "MIT", "main": "strophe.js", "browser": "strophe.js", "scripts": { diff --git a/src/polyfills.js b/src/polyfills.js index 51d60128..62eb3c09 100644 --- a/src/polyfills.js +++ b/src/polyfills.js @@ -96,7 +96,7 @@ if (!Array.prototype.indexOf) { /** Function: Array.prototype.forEach - * + * * This function is not available in IE < 9 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach */ @@ -114,7 +114,7 @@ if (!Array.prototype.forEach) { // method of O with the argument "length". // 3. Let len be toUint32(lenValue). var len = O.length >>> 0; - // 4. If isCallable(callback) is false, throw a TypeError exception. + // 4. If isCallable(callback) is false, throw a TypeError exception. // See: http://es5.github.com/#x9.11 if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); diff --git a/src/sha1.js b/src/sha1.js index 4545bca6..d7992653 100644 --- a/src/sha1.js +++ b/src/sha1.js @@ -7,7 +7,7 @@ * See http://pajhome.org.uk/crypt/md5 for details. */ -/* jshint undef: true, unused: true:, noarg: true, latedef: true */ +/* jshint undef: true, unused: true:, noarg: true, latedef: false */ /* global define */ /* Some functions and variables have been stripped for use with Strophe */ diff --git a/strophe.js b/strophe.js index 3e725623..fb337792 100644 --- a/strophe.js +++ b/strophe.js @@ -1,13 +1,11 @@ /** File: strophe.js - * A JavaScript library for XMPP BOSH/XMPP over Websocket. + * A JavaScript library for writing XMPP clients. * - * This is the JavaScript version of the Strophe library. Since JavaScript - * had no facilities for persistent TCP connections, this library uses - * Bidirectional-streams Over Synchronous HTTP (BOSH) to emulate - * a persistent, stateful, two-way connection to an XMPP server. More - * information on BOSH can be found in XEP 124. + * This library uses either Bidirectional-streams Over Synchronous HTTP (BOSH) + * to emulate a persistent, stateful, two-way connection to an XMPP server or + * alternatively WebSockets. * - * This version of Strophe also works with WebSockets. + * More information on BOSH can be found in XEP 124. * For more information on XMPP-over WebSocket see this RFC: * http://tools.ietf.org/html/rfc7395 */ @@ -121,7 +119,7 @@ * See http://pajhome.org.uk/crypt/md5 for details. */ -/* jshint undef: true, unused: true:, noarg: true, latedef: true */ +/* jshint undef: true, unused: true:, noarg: true, latedef: false */ /* global define */ /* Some functions and variables have been stripped for use with Strophe */ @@ -618,7 +616,7 @@ return { } }(this, function () { -/** PrivateFunction: Function.prototype.bind +/** Function: Function.prototype.bind * Bind a function to an instance. * * This Function object extension method creates a bound method similar @@ -645,16 +643,13 @@ if (!Function.prototype.bind) { var _slice = Array.prototype.slice; var _concat = Array.prototype.concat; var _args = _slice.call(arguments, 1); - return function () { - return func.apply(obj ? obj : this, - _concat.call(_args, - _slice.call(arguments, 0))); + return func.apply(obj ? obj : this, _concat.call(_args, _slice.call(arguments, 0))); }; }; } -/** PrivateFunction: Array.isArray +/** Function: Array.isArray * This is a polyfill for the ES5 Array.isArray method. */ if (!Array.isArray) { @@ -663,7 +658,7 @@ if (!Array.isArray) { }; } -/** PrivateFunction: Array.prototype.indexOf +/** Function: Array.prototype.indexOf * Return the index of an object in an array. * * This function is not supplied by some JavaScript implementations, so @@ -677,12 +672,9 @@ if (!Array.isArray) { * Returns: * The index of elt in the array or -1 if not found. */ -if (!Array.prototype.indexOf) - { - Array.prototype.indexOf = function(elt /*, from*/) - { +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length; - var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) { @@ -694,12 +686,67 @@ if (!Array.prototype.indexOf) return from; } } - return -1; }; } })); + +/** Function: Array.prototype.forEach + * + * This function is not available in IE < 9 + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach + */ +if (!Array.prototype.forEach) { + Array.prototype.forEach = function(callback, thisArg) { + var T, k; + if (this === null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Let O be the result of calling toObject() passing the + // |this| value as the argument. + var O = Object(this); + // 2. Let lenValue be the result of calling the Get() internal + // method of O with the argument "length". + // 3. Let len be toUint32(lenValue). + var len = O.length >>> 0; + // 4. If isCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + // 5. If thisArg was supplied, let T be thisArg; else let + // T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + // 6. Let k be 0 + k = 0; + // 7. Repeat, while k < len + while (k < len) { + var kValue; + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty + // internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + // i. Let kValue be the result of calling the Get internal + // method of O with argument Pk. + kValue = O[k]; + // ii. Call the Call internal method of callback with T as + // the this value and argument list containing kValue, k, and O. + callback.call(T, kValue, k, O); + } + // d. Increase k by 1. + k++; + } + // 8. return undefined + }; +} + /* This program is distributed under the terms of the MIT license. Please see the LICENSE file for details. @@ -708,7 +755,7 @@ if (!Array.prototype.indexOf) */ /* jshint undef: true, unused: true:, noarg: true, latedef: true */ -/*global define, document, window, setTimeout, clearTimeout, console, ActiveXObject, DOMParser */ +/*global define, document, window, setTimeout, clearTimeout, ActiveXObject, DOMParser */ (function (root, factory) { if (typeof define === 'function' && define.amd) { @@ -799,7 +846,7 @@ Strophe = { * The version of the Strophe library. Unreleased builds will have * a version of head-HASH where HASH is a partial revision. */ - VERSION: "1.2.8", + VERSION: "1.2.9", /** Constants: XMPP Namespace Constants * Common namespace constants from the XMPP RFCs and XEPs. @@ -997,7 +1044,7 @@ Strophe = { * (String) value - The actual namespace. */ addNamespace: function (name, value) { - Strophe.NS[name] = value; + Strophe.NS[name] = value; }, /** Function: forEachChild @@ -1016,7 +1063,6 @@ Strophe = { */ forEachChild: function (elem, elemName, func) { var i, childNode; - for (i = 0; i < elem.childNodes.length; i++) { childNode = elem.childNodes[i]; if (childNode.nodeType == Strophe.ElementType.NORMAL && @@ -1055,7 +1101,6 @@ Strophe = { */ _makeGenerator: function () { var doc; - // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload. // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be // less than 10 in the case of IE9 and below. @@ -1067,7 +1112,6 @@ Strophe = { doc = document.implementation .createDocument('jabber:client', 'strophe', null); } - return doc; }, @@ -1115,7 +1159,6 @@ Strophe = { break; } } - return doc; }, @@ -1141,7 +1184,6 @@ Strophe = { if (!name) { return null; } var node = Strophe.xmlGenerator().createElement(name); - // FIXME: this should throw errors if args are the wrong type or // there are more than two optional args var a, i, k; @@ -1186,8 +1228,7 @@ Strophe = { * Returns: * Escaped text. */ - xmlescape: function(text) - { + xmlescape: function(text) { text = text.replace(/\&/g, "&"); text = text.replace(//g, ">"); @@ -1205,8 +1246,7 @@ Strophe = { * Returns: * Unescaped text. */ - xmlunescape: function(text) - { + xmlunescape: function(text) { text = text.replace(/\&/g, "&"); text = text.replace(/</g, "<"); text = text.replace(/>/g, ">"); @@ -1308,7 +1348,6 @@ Strophe = { } else if (elem.nodeType == Strophe.ElementType.TEXT) { el = Strophe.xmlGenerator().createTextNode(elem.nodeValue); } - return el; }, @@ -1384,7 +1423,6 @@ Strophe = { } else if (elem.nodeType == Strophe.ElementType.TEXT) { el = Strophe.xmlTextNode(elem.nodeValue); } - return el; }, @@ -1498,6 +1536,25 @@ Strophe = { return jid ? jid.split("/")[0] : null; }, + /** PrivateFunction: _handleError + * _Private_ function that properly logs an error to the console + */ + _handleError: function (e) { + if (typeof e.stack !== "undefined") { + Strophe.fatal(e.stack); + } + if (e.sourceURL) { + Strophe.fatal("error: " + this.handler + " " + e.sourceURL + ":" + + e.line + " - " + e.name + ": " + e.message); + } else if (e.fileName) { + Strophe.fatal("error: " + this.handler + " " + + e.fileName + ":" + e.lineNumber + " - " + + e.name + ": " + e.message); + } else { + Strophe.fatal("error: " + e.message); + } + }, + /** Function: log * User overrideable logging function. * @@ -1539,8 +1596,7 @@ Strophe = { * Parameters: * (String) msg - The log message. */ - debug: function(msg) - { + debug: function(msg) { this.log(this.LogLevel.DEBUG, msg); }, @@ -1678,6 +1734,7 @@ Strophe = { * > .c('query', {xmlns: 'strophe:example'}) * > .c('example') * > .toString() + * * The above generates this XML fragment * > * > @@ -1768,6 +1825,21 @@ Strophe.Builder.prototype = { return this; }, + /** Function: root + * Make the root element the new current element. + * + * When at a deeply nested element in the tree, this function can be used + * to jump back to the root of the tree, instead of having to repeatedly + * call up(). + * + * Returns: + * The Stophe.Builder object. + */ + root: function () { + this.node = this.nodeTree; + return this; + }, + /** Function: attrs * Add or modify attributes of the current element. * @@ -1837,8 +1909,7 @@ Strophe.Builder.prototype = { var xmlGen = Strophe.xmlGenerator(); try { impNode = (xmlGen.importNode !== undefined); - } - catch (e) { + } catch (e) { impNode = false; } var newElem = impNode ? @@ -1929,66 +2000,90 @@ Strophe.Handler = function (handler, ns, name, type, id, from, options) { this.name = name; this.type = type; this.id = id; - this.options = options || {matchBare: false}; - - // default matchBare to false if undefined - if (!this.options.matchBare) { - this.options.matchBare = false; + this.options = options || {'matchBareFromJid': false, 'ignoreNamespaceFragment': false}; + // BBB: Maintain backward compatibility with old `matchBare` option + if (this.options.matchBare) { + Strophe.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.'); + this.options.matchBareFromJid = this.options.matchBare; + delete this.options.matchBare; } - if (this.options.matchBare) { + if (this.options.matchBareFromJid) { this.from = from ? Strophe.getBareJidFromJid(from) : null; } else { this.from = from; } - // whether the handler is a user handler or a system handler this.user = true; }; Strophe.Handler.prototype = { - /** PrivateFunction: isMatch - * Tests if a stanza matches the Strophe.Handler. + /** PrivateFunction: getNamespace + * Returns the XML namespace attribute on an element. + * If `ignoreNamespaceFragment` was passed in for this handler, then the + * URL fragment will be stripped. * * Parameters: - * (XMLElement) elem - The XML element to test. + * (XMLElement) elem - The XML element with the namespace. * * Returns: - * true if the stanza matches and false otherwise. + * The namespace, with optionally the fragment stripped. */ - isMatch: function (elem) { - var nsMatch; - var from = null; - - if (this.options.matchBare) { - from = Strophe.getBareJidFromJid(elem.getAttribute('from')); - } else { - from = elem.getAttribute('from'); + getNamespace: function (elem) { + var elNamespace = elem.getAttribute("xmlns"); + if (elNamespace && this.options.ignoreNamespaceFragment) { + elNamespace = elNamespace.split('#')[0]; } + return elNamespace; + }, - nsMatch = false; + /** PrivateFunction: namespaceMatch + * Tests if a stanza matches the namespace set for this Strophe.Handler. + * + * Parameters: + * (XMLElement) elem - The XML element to test. + * + * Returns: + * true if the stanza matches and false otherwise. + */ + namespaceMatch: function (elem) { + var nsMatch = false; if (!this.ns) { - nsMatch = true; + return true; } else { var that = this; Strophe.forEachChild(elem, null, function (elem) { - if (elem.getAttribute("xmlns") == that.ns) { + if (that.getNamespace(elem) === that.ns) { nsMatch = true; } }); - - nsMatch = nsMatch || elem.getAttribute("xmlns") == this.ns; + nsMatch = nsMatch || this.getNamespace(elem) === this.ns; } + return nsMatch; + }, + /** PrivateFunction: isMatch + * Tests if a stanza matches the Strophe.Handler. + * + * Parameters: + * (XMLElement) elem - The XML element to test. + * + * Returns: + * true if the stanza matches and false otherwise. + */ + isMatch: function (elem) { + var from = elem.getAttribute('from'); + if (this.options.matchBareFromJid) { + from = Strophe.getBareJidFromJid(from); + } var elem_type = elem.getAttribute("type"); - if (nsMatch && + 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)) { return true; } - return false; }, @@ -2007,25 +2102,9 @@ Strophe.Handler.prototype = { try { result = this.handler(elem); } catch (e) { - if (e.sourceURL) { - Strophe.fatal("error: " + this.handler + - " " + e.sourceURL + ":" + - e.line + " - " + e.name + ": " + e.message); - } else if (e.fileName) { - if (typeof(console) != "undefined") { - console.trace(); - console.error(this.handler, " - error - ", e, e.message); - } - Strophe.fatal("error: " + this.handler + " " + - e.fileName + ":" + e.lineNumber + " - " + - e.name + ": " + e.message); - } else { - Strophe.fatal("error: " + e.message + "\n" + e.stack); - } - + Strophe._handleError(e); throw e; } - return result; }, @@ -2069,7 +2148,6 @@ Strophe.Handler.prototype = { Strophe.TimedHandler = function (period, handler) { this.period = period; this.handler = handler; - this.lastCalled = new Date().getTime(); this.user = true; }; @@ -2143,25 +2221,48 @@ Strophe.TimedHandler.prototype = { * Options common to both Websocket and BOSH: * ------------------------------------------ * + * cookies + * ~~~~~~~ + * * The "cookies" option allows you to pass in cookies to be added to the * document. These cookies will then be included in the BOSH XMLHttpRequest * or in the websocket connection. * * The passed in value must be a map of cookie names and string values: * - * { "myCookie": { - * "value": "1234", - * "domain": ".example.org", - * "path": "/", - * "expires": expirationDate - * } - * } + * > { "myCookie": { + * > "value": "1234", + * > "domain": ".example.org", + * > "path": "/", + * > "expires": expirationDate + * > } + * > } * * Note that cookies can't be set in this way for other domains (i.e. cross-domain). * Those cookies need to be set under those domains, for example they can be * set server-side by making a XHR call to that domain to ask it to set any * necessary cookies. * + * mechanisms + * ~~~~~~~~~~ + * + * The "mechanisms" option allows you to specify the SASL mechanisms that this + * instance of Strophe.Connection (and therefore your XMPP client) will + * support. + * + * The value must be an array of objects with Strophe.SASLMechanism + * prototypes. + * + * If nothing is specified, then the following mechanisms (and their + * priorities) are registered: + * + * EXTERNAL - 60 + * OAUTHBEARER - 50 + * SCRAM-SHA1 - 40 + * DIGEST-MD5 - 30 + * PLAIN - 20 + * ANONYMOUS - 10 + * * WebSocket options: * ------------------ * @@ -2259,8 +2360,11 @@ Strophe.Connection = function (service, options) { this.removeHandlers = []; this.addTimeds = []; this.addHandlers = []; + this.protocolErrorHandlers = { + 'HTTP': {}, + 'websocket': {} + }; - this._authentication = {}; this._idleTimeout = null; this._disconnectTimeout = null; @@ -2288,6 +2392,7 @@ Strophe.Connection = function (service, options) { }.bind(this), 100); utils.addCookies(this.options.cookies); + this.registerSASLMechanisms(this.options.mechanisms); // initialize plugins for (var k in Strophe._connectionPlugins) { @@ -2323,7 +2428,6 @@ Strophe.Connection.prototype = { this.removeHandlers = []; this.addTimeds = []; this.addHandlers = []; - this._authentication = {}; this.authenticated = false; this.connected = false; @@ -2392,6 +2496,33 @@ Strophe.Connection.prototype = { } }, + /** Function: addProtocolErrorHandler + * Register a handler function for when a protocol (websocker or HTTP) + * error occurs. + * + * NOTE: Currently only HTTP errors for BOSH requests are handled. + * Patches that handle websocket errors would be very welcome. + * + * Parameters: + * (String) protocol - 'HTTP' or 'websocket' + * (Integer) status_code - Error status code (e.g 500, 400 or 404) + * (Function) callback - Function that will fire on Http error + * + * Example: + * function onError(err_code){ + * //do stuff + * } + * + * var conn = Strophe.connect('http://example.com/http-bind'); + * conn.addProtocolErrorHandler('HTTP', 500, onError); + * // Triggers HTTP 500 error and onError handler will be called + * conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect); + */ + addProtocolErrorHandler: function(protocol, status_code, callback){ + this.protocolErrorHandlers[protocol][status_code] = callback; + }, + + /** Function: connect * Starts the connection process. * @@ -2878,13 +3009,39 @@ Strophe.Connection.prototype = { * and also any of its immediate children. This is primarily to make * matching /iq/query elements easy. * - * The options argument contains handler matching flags that affect how - * matches are determined. Currently the only flag is matchBare (a - * boolean). When matchBare is true, the from parameter and the from - * attribute on the stanza will be matched as bare JIDs instead of - * full JIDs. To use this, pass {matchBare: true} as the value of - * options. The default value for matchBare is false. - * + * Options + * ~~~~~~~ + * With the options argument, you can specify boolean flags that affect how + * matches are being done. + * + * Currently two flags exist: + * + * - matchBareFromJid: + * When set to true, the from parameter and the + * from attribute on the stanza will be matched as bare JIDs instead + * of full JIDs. To use this, pass {matchBareFromJid: true} as the + * value of options. The default value for matchBareFromJid is false. + * + * - ignoreNamespaceFragment: + * When set to true, a fragment specified on the stanza's namespace + * URL will be ignored when it's matched with the one configured for + * the handler. + * + * This means that if you register like this: + * > connection.addHandler( + * > handler, + * > 'http://jabber.org/protocol/muc', + * > null, null, null, null, + * > {'ignoreNamespaceFragment': true} + * > ); + * + * Then a stanza with XML namespace of + * 'http://jabber.org/protocol/muc#user' will also be matched. If + * 'ignoreNamespaceFragment' is false, then only stanzas with + * 'http://jabber.org/protocol/muc' will be matched. + * + * Deleting the handler + * ~~~~~~~~~~~~~~~~~~~~ * The return value should be saved if you wish to remove the handler * with deleteHandler(). * @@ -2892,7 +3049,7 @@ Strophe.Connection.prototype = { * (Function) handler - The user callback. * (String) ns - The namespace to match. * (String) name - The stanza name to match. - * (String) type - The stanza type attribute to match. + * (String|Array) type - The stanza type (or types if an array) to match. * (String) id - The stanza id attribute to match. * (String) from - The stanza from attribute to match. * (String) options - The handler options @@ -2928,6 +3085,40 @@ Strophe.Connection.prototype = { } }, + /** Function: registerSASLMechanisms + * + * Register the SASL mechanisms which will be supported by this instance of + * Strophe.Connection (i.e. which this XMPP client will support). + * + * Parameters: + * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes + * + */ + registerSASLMechanisms: function (mechanisms) { + this.mechanisms = {}; + mechanisms = mechanisms || [ + Strophe.SASLAnonymous, + Strophe.SASLExternal, + Strophe.SASLMD5, + Strophe.SASLOAuthBearer, + Strophe.SASLPlain, + Strophe.SASLSHA1 + ]; + mechanisms.forEach(this.registerSASLMechanism.bind(this)); + }, + + /** Function: registerSASLMechanism + * + * Register a single SASL mechanism, to be supported by this client. + * + * Parameters: + * (Object) mechanism - Object with a Strophe.SASLMechanism prototype + * + */ + registerSASLMechanism: function (mechanism) { + this.mechanisms[mechanism.prototype.name] = mechanism; + }, + /** Function: disconnect * Start the graceful disconnection process. * @@ -2997,8 +3188,9 @@ Strophe.Connection.prototype = { try { this.connect_callback(status, condition); } catch (e) { - Strophe.error("User connection callback caused an " + - "exception: " + e); + Strophe._handleError(e); + Strophe.error( + "User connection callback caused an "+"exception: "+e); } } }, @@ -3138,7 +3330,7 @@ Strophe.Connection.prototype = { } } catch(e) { // if the handler throws an exception, we consider it as false - Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message); + Strophe.warn('Removing Strophe handlers due to uncaught exception: '+e.message); } } }); @@ -3146,7 +3338,7 @@ Strophe.Connection.prototype = { /** Attribute: mechanisms - * SASL Mechanisms available for Conncection. + * SASL Mechanisms available for Connection. */ mechanisms: {}, @@ -3168,7 +3360,6 @@ Strophe.Connection.prototype = { */ _connect_cb: function (req, _callback, raw) { Strophe.info("_connect_cb was called"); - this.connected = true; var bodyWrap; @@ -3201,132 +3392,175 @@ Strophe.Connection.prototype = { return; } - this._authentication.sasl_scram_sha1 = false; - this._authentication.sasl_plain = false; - this._authentication.sasl_digest_md5 = false; - this._authentication.sasl_anonymous = false; - this._authentication.legacy_auth = false; - // Check for the stream:features tag var hasFeatures; if (bodyWrap.getElementsByTagNameNS) { hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0; } else { - hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 || bodyWrap.getElementsByTagName("features").length > 0; + hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 || + bodyWrap.getElementsByTagName("features").length > 0; } - var mechanisms = bodyWrap.getElementsByTagName("mechanism"); - var matched = []; - var i, mech, found_authentication = false; if (!hasFeatures) { this._proto._no_auth_received(_callback); return; } + + var matched = [], i, mech; + var mechanisms = bodyWrap.getElementsByTagName("mechanism"); if (mechanisms.length > 0) { for (i = 0; i < mechanisms.length; i++) { mech = Strophe.getText(mechanisms[i]); if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]); } } - this._authentication.legacy_auth = - bodyWrap.getElementsByTagName("auth").length > 0; - found_authentication = this._authentication.legacy_auth || - matched.length > 0; - if (!found_authentication) { - this._proto._no_auth_received(_callback); - return; + if (matched.length === 0) { + if (bodyWrap.getElementsByTagName("auth").length === 0) { + // There are no matching SASL mechanisms and also no legacy + // auth available. + this._proto._no_auth_received(_callback); + return; + } } - if (this.do_authentication !== false) + if (this.do_authentication !== false) { this.authenticate(matched); + } }, - /** Function: authenticate - * Set up authentication + /** Function: sortMechanismsByPriority * - * Contiunues the initial connection request by setting up authentication - * handlers and start the authentication process. + * Sorts an array of objects with prototype SASLMechanism according to + * their priorities. * - * SASL authentication will be attempted if available, otherwise - * the code will fall back to legacy authentication. + * Parameters: + * (Array) mechanisms - Array of SASL mechanisms. * */ - authenticate: function (matched) { - var i; - // Sorting matched mechanisms according to priority. - for (i = 0; i < matched.length - 1; ++i) { - var higher = i; - for (var j = i + 1; j < matched.length; ++j) { - if (matched[j].prototype.priority > matched[higher].prototype.priority) { - higher = j; - } - } - if (higher != i) { - var swap = matched[i]; - matched[i] = matched[higher]; - matched[higher] = swap; + sortMechanismsByPriority: function (mechanisms) { + // Sorting mechanisms according to priority. + var i, j, higher, swap; + for (i = 0; i < mechanisms.length - 1; ++i) { + higher = i; + for (j = i + 1; j < mechanisms.length; ++j) { + if (mechanisms[j].prototype.priority > mechanisms[higher].prototype.priority) { + higher = j; + } + } + if (higher != i) { + swap = mechanisms[i]; + mechanisms[i] = mechanisms[higher]; + mechanisms[higher] = swap; + } } - } - - // run each mechanism - var mechanism_found = false; - for (i = 0; i < matched.length; ++i) { - if (!matched[i].prototype.test(this)) continue; - - this._sasl_success_handler = this._addSysHandler( - this._sasl_success_cb.bind(this), null, - "success", null, null); - this._sasl_failure_handler = this._addSysHandler( - this._sasl_failure_cb.bind(this), null, - "failure", null, null); - this._sasl_challenge_handler = this._addSysHandler( - this._sasl_challenge_cb.bind(this), null, - "challenge", null, null); - - this._sasl_mechanism = new matched[i](); - this._sasl_mechanism.onStart(this); - - var request_auth_exchange = $build("auth", { - xmlns: Strophe.NS.SASL, - mechanism: this._sasl_mechanism.name - }); + return mechanisms; + }, - if (this._sasl_mechanism.isClientFirst) { - var response = this._sasl_mechanism.onChallenge(this, null); - request_auth_exchange.t(Base64.encode(response)); + /** PrivateFunction: _attemptSASLAuth + * + * Iterate through an array of SASL mechanisms and attempt authentication + * with the highest priority (enabled) mechanism. + * + * Parameters: + * (Array) mechanisms - Array of SASL mechanisms. + * + * Returns: + * (Boolean) mechanism_found - true or false, depending on whether a + * valid SASL mechanism was found with which authentication could be + * started. + */ + _attemptSASLAuth: function (mechanisms) { + mechanisms = this.sortMechanismsByPriority(mechanisms || []); + var i = 0, mechanism_found = false; + for (i = 0; i < mechanisms.length; ++i) { + if (!mechanisms[i].prototype.test(this)) { + continue; + } + this._sasl_success_handler = this._addSysHandler( + this._sasl_success_cb.bind(this), null, + "success", null, null); + this._sasl_failure_handler = this._addSysHandler( + this._sasl_failure_cb.bind(this), null, + "failure", null, null); + this._sasl_challenge_handler = this._addSysHandler( + this._sasl_challenge_cb.bind(this), null, + "challenge", null, null); + + this._sasl_mechanism = new mechanisms[i](); + this._sasl_mechanism.onStart(this); + + var request_auth_exchange = $build("auth", { + xmlns: Strophe.NS.SASL, + mechanism: this._sasl_mechanism.name + }); + if (this._sasl_mechanism.isClientFirst) { + var response = this._sasl_mechanism.onChallenge(this, null); + request_auth_exchange.t(Base64.encode(response)); + } + this.send(request_auth_exchange.tree()); + mechanism_found = true; + break; } - this.send(request_auth_exchange.tree()); - mechanism_found = true; - break; - } + return mechanism_found; + }, - if (!mechanism_found) { - // if none of the mechanism worked + /** PrivateFunction: _attemptLegacyAuth + * + * Attempt legacy (i.e. non-SASL) authentication. + * + */ + _attemptLegacyAuth: function () { if (Strophe.getNodeFromJid(this.jid) === null) { // we don't have a node, which is required for non-anonymous // client connections - this._changeConnectStatus(Strophe.Status.CONNFAIL, - 'x-strophe-bad-non-anon-jid'); + this._changeConnectStatus( + Strophe.Status.CONNFAIL, + 'x-strophe-bad-non-anon-jid' + ); this.disconnect('x-strophe-bad-non-anon-jid'); } else { - // fall back to legacy authentication - this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null); - this._addSysHandler(this._auth1_cb.bind(this), null, null, - null, "_auth_1"); - this.send($iq({ - type: "get", - to: this.domain, - id: "_auth_1" - }).c("query", { - xmlns: Strophe.NS.AUTH - }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree()); + // Fall back to legacy authentication + this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null); + this._addSysHandler( + this._auth1_cb.bind(this), + null, null, null, "_auth_1" + ); + this.send($iq({ + 'type': "get", + 'to': this.domain, + 'id': "_auth_1" + }).c("query", {xmlns: Strophe.NS.AUTH}) + .c("username", {}).t(Strophe.getNodeFromJid(this.jid)) + .tree()); + } + }, + + /** Function: authenticate + * Set up authentication + * + * Continues the initial connection request by setting up authentication + * handlers and starting the authentication process. + * + * SASL authentication will be attempted if available, otherwise + * the code will fall back to legacy authentication. + * + * Parameters: + * (Array) matched - Array of SASL mechanisms supported. + * + */ + authenticate: function (matched) { + if (!this._attemptSASLAuth(matched)) { + this._attemptLegacyAuth(); } - } }, + /** PrivateFunction: _sasl_challenge_cb + * _Private_ handler for the SASL challenge + * + */ _sasl_challenge_cb: function(elem) { var challenge = Base64.decode(Strophe.getText(elem)); var response = this._sasl_mechanism.onChallenge(this, challenge); var stanza = $build('response', { - xmlns: Strophe.NS.SASL + 'xmlns': Strophe.NS.SASL }); if (response !== "") { stanza.t(Base64.encode(response)); @@ -3405,7 +3639,6 @@ Strophe.Connection.prototype = { return this._sasl_failure_cb(null); } } - Strophe.info("SASL authentication succeeded."); if (this._sasl_mechanism) { @@ -3641,8 +3874,7 @@ Strophe.Connection.prototype = { * (String) type - The stanza type attribute to match. * (String) id - The stanza id attribute to match. */ - _addSysHandler: function (handler, ns, name, type, id) - { + _addSysHandler: function (handler, ns, name, type, id) { var hand = new Strophe.Handler(handler, ns, name, type, id); hand.user = false; this.addHandlers.push(hand); @@ -3740,6 +3972,8 @@ Strophe.Connection.prototype = { * DIGEST-MD5 - 30 * PLAIN - 20 * ANONYMOUS - 10 + * + * See: Strophe.Connection.addSupportedSASLMechanisms */ /** @@ -3867,20 +4101,17 @@ Strophe.SASLMechanism.prototype = { * SASL ANONYMOUS authentication. */ Strophe.SASLAnonymous = function() {}; - Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism("ANONYMOUS", false, 10); Strophe.SASLAnonymous.prototype.test = function(connection) { return connection.authcid === null; }; -Strophe.Connection.prototype.mechanisms[Strophe.SASLAnonymous.prototype.name] = Strophe.SASLAnonymous; /** PrivateConstructor: SASLPlain * SASL PLAIN authentication. */ Strophe.SASLPlain = function() {}; - Strophe.SASLPlain.prototype = new Strophe.SASLMechanism("PLAIN", true, 20); Strophe.SASLPlain.prototype.test = function(connection) { @@ -3896,13 +4127,11 @@ Strophe.SASLPlain.prototype.onChallenge = function(connection) { return utils.utf16to8(auth_str); }; -Strophe.Connection.prototype.mechanisms[Strophe.SASLPlain.prototype.name] = Strophe.SASLPlain; /** PrivateConstructor: SASLSHA1 * SASL SCRAM SHA 1 authentication. */ Strophe.SASLSHA1 = function() {}; - Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism("SCRAM-SHA-1", true, 40); Strophe.SASLSHA1.prototype.test = function(connection) { @@ -3914,7 +4143,6 @@ Strophe.SASLSHA1.prototype.onChallenge = function(connection, challenge, test_cn var auth_str = "n=" + utils.utf16to8(connection.authcid); auth_str += ",r="; auth_str += cnonce; - connection._sasl_data.cnonce = cnonce; connection._sasl_data["client-first-message-bare"] = auth_str; @@ -3983,13 +4211,11 @@ Strophe.SASLSHA1.prototype.onChallenge = function(connection, challenge, test_cn return auth_str; }; -Strophe.Connection.prototype.mechanisms[Strophe.SASLSHA1.prototype.name] = Strophe.SASLSHA1; /** PrivateConstructor: SASLMD5 * SASL DIGEST MD5 authentication. */ Strophe.SASLMD5 = function() {}; - Strophe.SASLMD5.prototype = new Strophe.SASLMechanism("DIGEST-MD5", false, 30); Strophe.SASLMD5.prototype.test = function(connection) { @@ -4010,7 +4236,6 @@ Strophe.SASLMD5.prototype._quote = function (str) { //" end string workaround for emacs }; - Strophe.SASLMD5.prototype.onChallenge = function(connection, challenge, test_cnonce) { var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/; var cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890)); @@ -4066,17 +4291,14 @@ Strophe.SASLMD5.prototype.onChallenge = function(connection, challenge, test_cno this.onChallenge = function () { return ""; }; - return responseText; }; -Strophe.Connection.prototype.mechanisms[Strophe.SASLMD5.prototype.name] = Strophe.SASLMD5; /** PrivateConstructor: SASLOAuthBearer * SASL OAuth Bearer authentication. */ Strophe.SASLOAuthBearer = function() {}; - Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism("OAUTHBEARER", true, 50); Strophe.SASLOAuthBearer.prototype.test = function(connection) { @@ -4095,8 +4317,6 @@ Strophe.SASLOAuthBearer.prototype.onChallenge = function(connection) { return utils.utf16to8(auth_str); }; -Strophe.Connection.prototype.mechanisms[Strophe.SASLOAuthBearer.prototype.name] = Strophe.SASLOAuthBearer; - /** PrivateConstructor: SASLExternal * SASL EXTERNAL authentication. @@ -4120,8 +4340,6 @@ Strophe.SASLExternal.prototype.onChallenge = function(connection) { return connection.authcid === connection.authzid ? '' : connection.authzid; }; -Strophe.Connection.prototype.mechanisms[Strophe.SASLExternal.prototype.name] = Strophe.SASLExternal; - return { Strophe: Strophe, $build: $build, @@ -4174,8 +4392,7 @@ return { * (Function) func - The function that will be called when the * XMLHttpRequest readyState changes. * (Integer) rid - The BOSH rid attribute associated with this request. - * (Integer) sends - The number of times this same request has been - * sent. + * (Integer) sends - The number of times this same request has been sent. */ Strophe.Request = function (elem, func, rid, sends) { this.id = ++Strophe._requestId; @@ -4564,6 +4781,21 @@ Strophe.Bosh.prototype = { return this._requests.length === 0; }, + /** PrivateFunction: _callProtocolErrorHandlers + * _Private_ function to call error handlers registered for HTTP errors. + * + * Parameters: + * (Strophe.Request) req - The request that is changing readyState. + */ + _callProtocolErrorHandlers: function (req) { + var reqStatus = this._getRequestStatus(req), + err_callback; + err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus]; + if (err_callback) { + err_callback.call(this, reqStatus); + } + }, + /** PrivateFunction: _hitError * _Private_ function to handle the error count. * @@ -4634,7 +4866,6 @@ Strophe.Bosh.prototype = { */ _onIdle: function () { var data = this._conn._data; - // if no requests are in progress, poll if (this._conn.authenticated && this._requests.length === 0 && data.length === 0 && !this._conn.disconnecting) { @@ -4692,6 +4923,34 @@ Strophe.Bosh.prototype = { } }, + /** PrivateFunction: _getRequestStatus + * + * Returns the HTTP status code from a Strophe.Request + * + * Parameters: + * (Strophe.Request) req - The Strophe.Request instance. + * (Integer) def - The default value that should be returned if no + * status value was found. + */ + _getRequestStatus: function (req, def) { + var reqStatus; + if (req.xhr.readyState == 4) { + try { + reqStatus = req.xhr.status; + } catch (e) { + // ignore errors from undefined status attribute. Works + // around a browser bug + Strophe.error( + "Caught an error while retrieving a request's status, " + + "reqStatus: " + reqStatus); + } + } + if (typeof(reqStatus) == "undefined") { + reqStatus = typeof def === 'number' ? def : 0; + } + return reqStatus; + }, + /** PrivateFunction: _onRequestStateChange * _Private_ handler for Strophe.Request state changes. * @@ -4705,88 +4964,62 @@ Strophe.Bosh.prototype = { * (Strophe.Request) req - The request that is changing readyState. */ _onRequestStateChange: function (func, req) { - Strophe.debug("request id " + req.id + - "." + req.sends + " state changed to " + - req.xhr.readyState); - + Strophe.debug("request id "+req.id+"."+req.sends+ + " state changed to "+req.xhr.readyState); if (req.abort) { req.abort = false; return; } + if (req.xhr.readyState !== 4) { + // The request is not yet complete + return; + } + var reqStatus = this._getRequestStatus(req); + if (this.disconnecting && reqStatus >= 400) { + this._hitError(reqStatus); + this._callProtocolErrorHandlers(req); + return; + } - // request complete - var reqStatus; - if (req.xhr.readyState == 4) { - reqStatus = 0; - try { - reqStatus = req.xhr.status; - } catch (e) { - // ignore errors from undefined status attribute. works - // around a browser bug - } - - if (typeof(reqStatus) == "undefined") { - reqStatus = 0; - } - - if (this.disconnecting) { - if (reqStatus >= 400) { - this._hitError(reqStatus); - return; - } - } + if ((reqStatus > 0 && reqStatus < 500) || req.sends > 5) { + // remove from internal queue + this._removeRequest(req); + Strophe.debug("request id "+req.id+" should now be removed"); + } + if (reqStatus == 200) { + // request succeeded var reqIs0 = (this._requests[0] == req); var reqIs1 = (this._requests[1] == req); - - if ((reqStatus > 0 && reqStatus < 500) || req.sends > 5) { - // remove from internal queue - this._removeRequest(req); - Strophe.debug("request id " + - req.id + - " should now be removed"); + // if request 1 finished, or request 0 finished and request + // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to + // restart the other - both will be in the first spot, as the + // completed request has been removed from the queue already + if (reqIs1 || + (reqIs0 && this._requests.length > 0 && + this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait))) { + this._restartRequest(0); } - - // request succeeded - if (reqStatus == 200) { - // if request 1 finished, or request 0 finished and request - // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to - // restart the other - both will be in the first spot, as the - // completed request has been removed from the queue already - if (reqIs1 || - (reqIs0 && this._requests.length > 0 && - this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait))) { - this._restartRequest(0); - } - - this._conn.nextValidRid(Number(req.rid) + 1); - - // call handler - Strophe.debug("request id " + - req.id + "." + - req.sends + " got 200"); - func(req); - this.errors = 0; - } else { - Strophe.error("request id " + - req.id + "." + - req.sends + " error " + reqStatus + - " happened"); - if (reqStatus === 0 || - (reqStatus >= 400 && reqStatus < 600) || - reqStatus >= 12000) { - this._hitError(reqStatus); - if (reqStatus >= 400 && reqStatus < 500) { - this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null); - this._conn._doDisconnect(); - } - } - } - - if (!((reqStatus > 0 && reqStatus < 500) || - req.sends > 5)) { - this._throttledRequestHandler(); + this._conn.nextValidRid(Number(req.rid) + 1); + Strophe.debug("request id "+req.id+"."+req.sends+" got 200"); + func(req); // call handler + this.errors = 0; + } else if (reqStatus === 0 || + (reqStatus >= 400 && reqStatus < 600) || + reqStatus >= 12000) { + // request failed + Strophe.error("request id "+req.id+"."+req.sends+" error "+reqStatus+" happened"); + this._hitError(reqStatus); + this._callProtocolErrorHandlers(req); + if (reqStatus >= 400 && reqStatus < 500) { + this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null); + this._conn._doDisconnect(); } + } else { + Strophe.error("request id "+req.id+"."+req.sends+" error "+reqStatus+" happened"); + } + if (!(reqStatus > 0 && reqStatus < 500) || req.sends > 5) { + this._throttledRequestHandler(); } }, @@ -4802,20 +5035,7 @@ Strophe.Bosh.prototype = { _processRequest: function (i) { var self = this; var req = this._requests[i]; - var reqStatus = -1; - - try { - if (req.xhr.readyState == 4) { - reqStatus = req.xhr.status; - } - } catch (e) { - Strophe.error("caught an error in _requests[" + i + - "], reqStatus: " + reqStatus); - } - - if (typeof(reqStatus) == "undefined") { - reqStatus = -1; - } + var reqStatus = this._getRequestStatus(req, -1); // make sure we limit the number of retries if (req.sends > this._conn.maxRetries) { @@ -4829,13 +5049,11 @@ Strophe.Bosh.prototype = { var secondaryTimeout = (req.dead !== null && req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)); var requestCompletedWithServerError = (req.xhr.readyState == 4 && - (reqStatus < 1 || - reqStatus >= 500)); + (reqStatus < 1 || reqStatus >= 500)); if (primaryTimeout || secondaryTimeout || requestCompletedWithServerError) { if (secondaryTimeout) { - Strophe.error("Request " + - this._requests[i].id + + Strophe.error("Request " + this._requests[i].id + " timed out (secondary), restarting"); } req.abort = true; @@ -4850,21 +5068,23 @@ Strophe.Bosh.prototype = { } if (req.xhr.readyState === 0) { - Strophe.debug("request id " + req.id + - "." + req.sends + " posting"); + Strophe.debug("request id "+req.id+"."+req.sends+" posting"); try { var contentType = this._conn.options.contentType || "text/xml; charset=utf-8"; req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true); - req.xhr.setRequestHeader("Content-Type", contentType); + if (typeof req.xhr.setRequestHeader !== 'undefined') { + // IE9 doesn't have setRequestHeader + req.xhr.setRequestHeader("Content-Type", contentType); + } if (this._conn.options.withCredentials) { req.xhr.withCredentials = true; } } catch (e2) { Strophe.error("XHR open failed."); if (!this._conn.connected) { - this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, - "bad-service"); + this._conn._changeConnectStatus( + Strophe.Status.CONNFAIL, "bad-service"); } this._conn.disconnect(); return; @@ -4892,9 +5112,8 @@ Strophe.Bosh.prototype = { // expanding retry window var backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait), Math.pow(req.sends, 3)) * 1000; - - // XXX: setTimeout should be called only with function expressions (23974bc1) setTimeout(function() { + // XXX: setTimeout should be called only with function expressions (23974bc1) sendFunc(); }, backoff); } else { @@ -4929,17 +5148,14 @@ Strophe.Bosh.prototype = { */ _removeRequest: function (req) { Strophe.debug("removing request"); - var i; for (i = this._requests.length - 1; i >= 0; i--) { if (req == this._requests[i]) { this._requests.splice(i, 1); } } - // IE6 fails on setting to null, so set to empty function req.xhr.onreadystatechange = function () {}; - this._throttledRequestHandler(); }, @@ -4989,16 +5205,15 @@ Strophe.Bosh.prototype = { _sendTerminate: function (pres) { Strophe.info("_sendTerminate was called"); var body = this._buildBody().attrs({type: "terminate"}); - if (pres) { body.cnode(pres.tree()); } - - var req = new Strophe.Request(body.tree(), - this._onRequestStateChange.bind( - this, this._conn._dataRecv.bind(this._conn)), - body.tree().getAttribute("rid")); - + var req = new Strophe.Request( + body.tree(), + this._onRequestStateChange.bind( + this, this._conn._dataRecv.bind(this._conn)), + body.tree().getAttribute("rid") + ); this._requests.push(req); this._throttledRequestHandler(); }, diff --git a/strophe.min.js b/strophe.min.js index 415798c1..20e23a50 100644 --- a/strophe.min.js +++ b/strophe.min.js @@ -1,3 +1,3 @@ -/*! strophe.js v1.2.8 - built on 16-09-2016 */ -!function(a){if(function(a,b){"function"==typeof define&&define.amd?define("strophe-base64",function(){return b()}):a.Base64=b()}(this,function(){var a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",b={encode:function(b){var c,d,e,f,g,h,i,j="",k=0;do c=b.charCodeAt(k++),d=b.charCodeAt(k++),e=b.charCodeAt(k++),f=c>>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,isNaN(d)?(g=(3&c)<<4,h=i=64):isNaN(e)&&(i=64),j=j+a.charAt(f)+a.charAt(g)+a.charAt(h)+a.charAt(i);while(k>4,d=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(c),64!=h&&(j+=String.fromCharCode(d)),64!=i&&(j+=String.fromCharCode(e));while(k>5]|=128<<24-d%32,a[(d+64>>9<<4)+15]=d;var g,h,i,j,k,l,m,n,o=new Array(80),p=1732584193,q=-271733879,r=-1732584194,s=271733878,t=-1009589776;for(g=0;gh;h++)16>h?o[h]=a[g+h]:o[h]=f(o[h-3]^o[h-8]^o[h-14]^o[h-16],1),i=e(e(f(p,5),b(h,q,r,s)),e(e(t,o[h]),c(h))),t=s,s=r,r=f(q,30),q=p,p=i;p=e(p,j),q=e(q,k),r=e(r,l),s=e(s,m),t=e(t,n)}return[p,q,r,s,t]}function b(a,b,c,d){return 20>a?b&c|~b&d:40>a?b^c^d:60>a?b&c|b&d|c&d:b^c^d}function c(a){return 20>a?1518500249:40>a?1859775393:60>a?-1894007588:-899497514}function d(b,c){var d=g(b);d.length>16&&(d=a(d,8*b.length));for(var e=new Array(16),f=new Array(16),h=0;16>h;h++)e[h]=909522486^d[h],f[h]=1549556828^d[h];var i=a(e.concat(g(c)),512+8*c.length);return a(f.concat(i),672)}function e(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c}function f(a,b){return a<>>32-b}function g(a){for(var b=[],c=255,d=0;d<8*a.length;d+=8)b[d>>5]|=(a.charCodeAt(d/8)&c)<<24-d%32;return b}function h(a){for(var b="",c=255,d=0;d<32*a.length;d+=8)b+=String.fromCharCode(a[d>>5]>>>24-d%32&c);return b}function i(a){for(var b,c,d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",e="",f=0;f<4*a.length;f+=3)for(b=(a[f>>2]>>8*(3-f%4)&255)<<16|(a[f+1>>2]>>8*(3-(f+1)%4)&255)<<8|a[f+2>>2]>>8*(3-(f+2)%4)&255,c=0;4>c;c++)e+=8*f+6*c>32*a.length?"=":d.charAt(b>>6*(3-c)&63);return e}return{b64_hmac_sha1:function(a,b){return i(d(a,b))},b64_sha1:function(b){return i(a(g(b),8*b.length))},binb2str:h,core_hmac_sha1:d,str_hmac_sha1:function(a,b){return h(d(a,b))},str_sha1:function(b){return h(a(g(b),8*b.length))}}}),function(a,b){"function"==typeof define&&define.amd?define("strophe-md5",function(){return b()}):a.MD5=b()}(this,function(a){var b=function(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c},c=function(a,b){return a<>>32-b},d=function(a){for(var b=[],c=0;c<8*a.length;c+=8)b[c>>5]|=(255&a.charCodeAt(c/8))<>5]>>>c%32&255);return b},f=function(a){for(var b="0123456789abcdef",c="",d=0;d<4*a.length;d++)c+=b.charAt(a[d>>2]>>d%4*8+4&15)+b.charAt(a[d>>2]>>d%4*8&15);return c},g=function(a,d,e,f,g,h){return b(c(b(b(d,a),b(f,h)),g),e)},h=function(a,b,c,d,e,f,h){return g(b&c|~b&d,a,b,e,f,h)},i=function(a,b,c,d,e,f,h){return g(b&d|c&~d,a,b,e,f,h)},j=function(a,b,c,d,e,f,h){return g(b^c^d,a,b,e,f,h)},k=function(a,b,c,d,e,f,h){return g(c^(b|~d),a,b,e,f,h)},l=function(a,c){a[c>>5]|=128<>>9<<4)+14]=c;for(var d,e,f,g,l=1732584193,m=-271733879,n=-1732584194,o=271733878,p=0;pb;b++)c=a.charCodeAt(b),c>=0&&127>=c?d+=a.charAt(b):c>2047?(d+=String.fromCharCode(224|c>>12&15),d+=String.fromCharCode(128|c>>6&63),d+=String.fromCharCode(128|c>>0&63)):(d+=String.fromCharCode(192|c>>6&31),d+=String.fromCharCode(128|c>>0&63));return d},addCookies:function(a){var b,c,d,e,f,g,h;for(b in a||{})f="",g="",h="",c=a[b],d="object"==typeof c,e=escape(unescape(d?c.value:c)),d&&(f=c.expires?";expires="+c.expires:"",g=c.domain?";domain="+c.domain:"",h=c.path?";path="+c.path:""),document.cookie=b+"="+e+f+g+h}};return a}),function(a,b){return"function"==typeof define&&define.amd?void define("strophe-polyfill",[],function(){return b()}):b()}(this,function(){Function.prototype.bind||(Function.prototype.bind=function(a){var b=this,c=Array.prototype.slice,d=Array.prototype.concat,e=c.call(arguments,1);return function(){return b.apply(a?a:this,d.call(e,c.call(arguments,0)))}}),Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length,c=Number(arguments[1])||0;for(c=0>c?Math.ceil(c):Math.floor(c),0>c&&(c+=b);b>c;c++)if(c in this&&this[c]===a)return c;return-1})}),function(a,b){if("function"==typeof define&&define.amd)define("strophe-core",["strophe-sha1","strophe-base64","strophe-md5","strophe-utils","strophe-polyfill"],function(){return b.apply(this,arguments)});else{var c=b(a.SHA1,a.Base64,a.MD5,a.stropheUtils);window.Strophe=c.Strophe,window.$build=c.$build,window.$iq=c.$iq,window.$msg=c.$msg,window.$pres=c.$pres,window.SHA1=c.SHA1,window.Base64=c.Base64,window.MD5=c.MD5,window.b64_hmac_sha1=c.SHA1.b64_hmac_sha1,window.b64_sha1=c.SHA1.b64_sha1,window.str_hmac_sha1=c.SHA1.str_hmac_sha1,window.str_sha1=c.SHA1.str_sha1}}(this,function(a,b,c,d){function e(a,b){return new i.Builder(a,b)}function f(a){return new i.Builder("message",a)}function g(a){return new i.Builder("iq",a)}function h(a){return new i.Builder("presence",a)}var i;return i={VERSION:"1.2.8",NS:{HTTPBIND:"http://jabber.org/protocol/httpbind",BOSH:"urn:xmpp:xbosh",CLIENT:"jabber:client",AUTH:"jabber:iq:auth",ROSTER:"jabber:iq:roster",PROFILE:"jabber:iq:profile",DISCO_INFO:"http://jabber.org/protocol/disco#info",DISCO_ITEMS:"http://jabber.org/protocol/disco#items",MUC:"http://jabber.org/protocol/muc",SASL:"urn:ietf:params:xml:ns:xmpp-sasl",STREAM:"http://etherx.jabber.org/streams",FRAMING:"urn:ietf:params:xml:ns:xmpp-framing",BIND:"urn:ietf:params:xml:ns:xmpp-bind",SESSION:"urn:ietf:params:xml:ns:xmpp-session",VERSION:"jabber:iq:version",STANZAS:"urn:ietf:params:xml:ns:xmpp-stanzas",XHTML_IM:"http://jabber.org/protocol/xhtml-im",XHTML:"http://www.w3.org/1999/xhtml"},XHTML:{tags:["a","blockquote","br","cite","em","img","li","ol","p","span","strong","ul","body"],attributes:{a:["href"],blockquote:["style"],br:[],cite:["style"],em:[],img:["src","alt","style","height","width"],li:["style"],ol:["style"],p:["style"],span:["style"],strong:[],ul:["style"],body:[]},css:["background-color","color","font-family","font-size","font-style","font-weight","margin-left","margin-right","text-align","text-decoration"],validTag:function(a){for(var b=0;b0)for(var c=0;c/g,">"),a=a.replace(/'/g,"'"),a=a.replace(/"/g,""")},xmlunescape:function(a){return a=a.replace(/\&/g,"&"),a=a.replace(/</g,"<"),a=a.replace(/>/g,">"),a=a.replace(/'/g,"'"),a=a.replace(/"/g,'"')},xmlTextNode:function(a){return i.xmlGenerator().createTextNode(a)},xmlHtmlNode:function(a){var b;if(window.DOMParser){var c=new DOMParser;b=c.parseFromString(a,"text/xml")}else b=new ActiveXObject("Microsoft.XMLDOM"),b.async="false",b.loadXML(a);return b},getText:function(a){if(!a)return null;var b="";0===a.childNodes.length&&a.nodeType==i.ElementType.TEXT&&(b+=a.nodeValue);for(var c=0;c0&&(g=h.join("; "),c.setAttribute(f,g))}else c.setAttribute(f,g);for(b=0;b/g,"\\3e").replace(/@/g,"\\40")},unescapeNode:function(a){return"string"!=typeof a?a:a.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,"\\")},getNodeFromJid:function(a){return a.indexOf("@")<0?null:a.split("@")[0]},getDomainFromJid:function(a){var b=i.getBareJidFromJid(a);if(b.indexOf("@")<0)return b;var c=b.split("@");return c.splice(0,1),c.join("@")},getResourceFromJid:function(a){var b=a.split("/");return b.length<2?null:(b.splice(0,1),b.join("/"))},getBareJidFromJid:function(a){return a?a.split("/")[0]:null},log:function(a,b){},debug:function(a){this.log(this.LogLevel.DEBUG,a)},info:function(a){this.log(this.LogLevel.INFO,a)},warn:function(a){this.log(this.LogLevel.WARN,a)},error:function(a){this.log(this.LogLevel.ERROR,a)},fatal:function(a){this.log(this.LogLevel.FATAL,a)},serialize:function(a){var b;if(!a)return null;"function"==typeof a.tree&&(a=a.tree());var c,d,e=a.nodeName;for(a.getAttribute("_realname")&&(e=a.getAttribute("_realname")),b="<"+e,c=0;c0){for(b+=">",c=0;c"}b+=""}else b+="/>";return b},_requestId:0,_connectionPlugins:{},addConnectionPlugin:function(a,b){i._connectionPlugins[a]=b}},i.Builder=function(a,b){("presence"==a||"message"==a||"iq"==a)&&(b&&!b.xmlns?b.xmlns=i.NS.CLIENT:b||(b={xmlns:i.NS.CLIENT})),this.nodeTree=i.xmlElement(a,b),this.node=this.nodeTree},i.Builder.prototype={tree:function(){return this.nodeTree},toString:function(){return i.serialize(this.nodeTree)},up:function(){return this.node=this.node.parentNode,this},attrs:function(a){for(var b in a)a.hasOwnProperty(b)&&(void 0===a[b]?this.node.removeAttribute(b):this.node.setAttribute(b,a[b]));return this},c:function(a,b,c){var d=i.xmlElement(a,b,c);return this.node.appendChild(d),"string"!=typeof c&&"number"!=typeof c&&(this.node=d),this},cnode:function(a){var b,c=i.xmlGenerator();try{b=void 0!==c.importNode}catch(d){b=!1}var e=b?c.importNode(a,!0):i.copyElement(a);return this.node.appendChild(e),this.node=e,this},t:function(a){var b=i.xmlTextNode(a);return this.node.appendChild(b),this},h:function(a){var b=document.createElement("body");b.innerHTML=a;for(var c=i.createHtml(b);c.childNodes.length>0;)this.node.appendChild(c.childNodes[0]);return this}},i.Handler=function(a,b,c,d,e,f,g){this.handler=a,this.ns=b,this.name=c,this.type=d,this.id=e,this.options=g||{matchBare:!1},this.options.matchBare||(this.options.matchBare=!1),this.options.matchBare?this.from=f?i.getBareJidFromJid(f):null:this.from=f,this.user=!0},i.Handler.prototype={isMatch:function(a){var b,c=null;if(c=this.options.matchBare?i.getBareJidFromJid(a.getAttribute("from")):a.getAttribute("from"),b=!1,this.ns){var d=this;i.forEachChild(a,null,function(a){a.getAttribute("xmlns")==d.ns&&(b=!0)}),b=b||a.getAttribute("xmlns")==this.ns}else b=!0;var e=a.getAttribute("type");return!b||this.name&&!i.isTagEqual(a,this.name)||this.type&&(Array.isArray(this.type)?-1==this.type.indexOf(e):e!=this.type)||this.id&&a.getAttribute("id")!=this.id||this.from&&c!=this.from?!1:!0},run:function(a){var b=null;try{b=this.handler(a)}catch(c){throw c.sourceURL?i.fatal("error: "+this.handler+" "+c.sourceURL+":"+c.line+" - "+c.name+": "+c.message):c.fileName?("undefined"!=typeof console&&(console.trace(),console.error(this.handler," - error - ",c,c.message)),i.fatal("error: "+this.handler+" "+c.fileName+":"+c.lineNumber+" - "+c.name+": "+c.message)):i.fatal("error: "+c.message+"\n"+c.stack),c}return b},toString:function(){return"{Handler: "+this.handler+"("+this.name+","+this.id+","+this.ns+")}"}},i.TimedHandler=function(a,b){this.period=a,this.handler=b,this.lastCalled=(new Date).getTime(),this.user=!0},i.TimedHandler.prototype={run:function(){return this.lastCalled=(new Date).getTime(),this.handler()},reset:function(){this.lastCalled=(new Date).getTime()},toString:function(){return"{TimedHandler: "+this.handler+"("+this.period+")}"}},i.Connection=function(a,b){this.service=a,this.options=b||{};var c=this.options.protocol||"";0===a.indexOf("ws:")||0===a.indexOf("wss:")||0===c.indexOf("ws")?this._proto=new i.Websocket(this):this._proto=new i.Bosh(this),this.jid="",this.domain=null,this.features=null,this._sasl_data={},this.do_session=!1,this.do_bind=!1,this.timedHandlers=[],this.handlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this._authentication={},this._idleTimeout=null,this._disconnectTimeout=null,this.authenticated=!1,this.connected=!1,this.disconnecting=!1,this.do_authentication=!0,this.paused=!1,this.restored=!1,this._data=[],this._uniqueId=0,this._sasl_success_handler=null,this._sasl_failure_handler=null,this._sasl_challenge_handler=null,this.maxRetries=5,this._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this),100),d.addCookies(this.options.cookies);for(var e in i._connectionPlugins)if(i._connectionPlugins.hasOwnProperty(e)){var f=i._connectionPlugins[e],g=function(){};g.prototype=f,this[e]=new g,this[e].init(this)}},i.Connection.prototype={reset:function(){this._proto._reset(),this.do_session=!1,this.do_bind=!1,this.timedHandlers=[],this.handlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this._authentication={},this.authenticated=!1,this.connected=!1,this.disconnecting=!1,this.restored=!1,this._data=[],this._requests=[],this._uniqueId=0},pause:function(){this.paused=!0},resume:function(){this.paused=!1},getUniqueId:function(a){var b="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"==a?b:3&b|8;return c.toString(16)});return"string"==typeof a||"number"==typeof a?b+":"+a:b+""},connect:function(a,b,c,d,e,f,g){this.jid=a,this.authzid=i.getBareJidFromJid(this.jid),this.authcid=g||i.getNodeFromJid(this.jid),this.pass=b,this.servtype="xmpp",this.connect_callback=c,this.disconnecting=!1,this.connected=!1,this.authenticated=!1,this.restored=!1,this.domain=i.getDomainFromJid(this.jid),this._changeConnectStatus(i.Status.CONNECTING,null),this._proto._connect(d,e,f)},attach:function(a,b,c,d,e,f,g){if(!(this._proto instanceof i.Bosh))throw{name:"StropheSessionError",message:'The "attach" method can only be used with a BOSH connection.'};this._proto._attach(a,b,c,d,e,f,g)},restore:function(a,b,c,d,e){if(!this._sessionCachingSupported())throw{name:"StropheSessionError",message:'The "restore" method can only be used with a BOSH connection.'};this._proto._restore(a,b,c,d,e)},_sessionCachingSupported:function(){if(this._proto instanceof i.Bosh){if(!JSON)return!1;try{window.sessionStorage.setItem("_strophe_","_strophe_"),window.sessionStorage.removeItem("_strophe_")}catch(a){return!1}return!0}return!1},xmlInput:function(a){},xmlOutput:function(a){},rawInput:function(a){},rawOutput:function(a){},nextValidRid:function(a){},send:function(a){if(null!==a){if("function"==typeof a.sort)for(var b=0;b=0&&this.addHandlers.splice(b,1)},disconnect:function(a){if(this._changeConnectStatus(i.Status.DISCONNECTING,a),i.info("Disconnect was called because: "+a),this.connected){var b=!1;this.disconnecting=!0,this.authenticated&&(b=h({xmlns:i.NS.CLIENT,type:"unavailable"})),this._disconnectTimeout=this._addSysTimedHandler(3e3,this._onDisconnectTimeout.bind(this)),this._proto._disconnect(b)}else i.info("Disconnect was called before Strophe connected to the server"),this._proto._abortAllRequests()},_changeConnectStatus:function(a,b){for(var c in i._connectionPlugins)if(i._connectionPlugins.hasOwnProperty(c)){var d=this[c];if(d.statusChanged)try{d.statusChanged(a,b)}catch(e){i.error(""+c+" plugin caused an exception changing status: "+e)}}if(this.connect_callback)try{this.connect_callback(a,b)}catch(f){i.error("User connection callback caused an exception: "+f)}},_doDisconnect:function(a){"number"==typeof this._idleTimeout&&clearTimeout(this._idleTimeout),null!==this._disconnectTimeout&&(this.deleteTimedHandler(this._disconnectTimeout),this._disconnectTimeout=null),i.info("_doDisconnect was called"),this._proto._doDisconnect(),this.authenticated=!1,this.disconnecting=!1,this.restored=!1,this.handlers=[],this.timedHandlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this._changeConnectStatus(i.Status.DISCONNECTED,a),this.connected=!1},_dataRecv:function(a,b){i.info("_dataRecv called");var c=this._proto._reqToData(a);if(null!==c){this.xmlInput!==i.Connection.prototype.xmlInput&&(c.nodeName===this._proto.strip&&c.childNodes.length?this.xmlInput(c.childNodes[0]):this.xmlInput(c)),this.rawInput!==i.Connection.prototype.rawInput&&(b?this.rawInput(b):this.rawInput(i.serialize(c)));for(var d,e;this.removeHandlers.length>0;)e=this.removeHandlers.pop(),d=this.handlers.indexOf(e),d>=0&&this.handlers.splice(d,1);for(;this.addHandlers.length>0;)this.handlers.push(this.addHandlers.pop());if(this.disconnecting&&this._proto._emptyQueue())return void this._doDisconnect();var f,g,h=c.getAttribute("type");if(null!==h&&"terminate"==h){if(this.disconnecting)return;return f=c.getAttribute("condition"),g=c.getElementsByTagName("conflict"),null!==f?("remote-stream-error"==f&&g.length>0&&(f="conflict"),this._changeConnectStatus(i.Status.CONNFAIL,f)):this._changeConnectStatus(i.Status.CONNFAIL,"unknown"),void this._doDisconnect(f)}var j=this;i.forEachChild(c,null,function(a){var b,c;for(c=j.handlers,j.handlers=[],b=0;b0:d.getElementsByTagName("stream:features").length>0||d.getElementsByTagName("features").length>0;var h,j,k=d.getElementsByTagName("mechanism"),l=[],m=!1;if(!g)return void this._proto._no_auth_received(b);if(k.length>0)for(h=0;h0,(m=this._authentication.legacy_auth||l.length>0)?void(this.do_authentication!==!1&&this.authenticate(l)):void this._proto._no_auth_received(b)}}},authenticate:function(a){var c;for(c=0;ca[d].prototype.priority&&(d=f);if(d!=c){var h=a[c];a[c]=a[d],a[d]=h}}var j=!1;for(c=0;c0&&(b="conflict"),this._changeConnectStatus(i.Status.AUTHFAIL,b),!1}var d,e=a.getElementsByTagName("bind");return e.length>0?(d=e[0].getElementsByTagName("jid"),void(d.length>0&&(this.jid=i.getText(d[0]),this.do_session?(this._addSysHandler(this._sasl_session_cb.bind(this),null,null,null,"_session_auth_2"),this.send(g({type:"set",id:"_session_auth_2"}).c("session",{xmlns:i.NS.SESSION}).tree())):(this.authenticated=!0,this._changeConnectStatus(i.Status.CONNECTED,null))))):(i.info("SASL binding failed."),this._changeConnectStatus(i.Status.AUTHFAIL,null),!1)},_sasl_session_cb:function(a){if("result"==a.getAttribute("type"))this.authenticated=!0,this._changeConnectStatus(i.Status.CONNECTED,null);else if("error"==a.getAttribute("type"))return i.info("Session creation failed."),this._changeConnectStatus(i.Status.AUTHFAIL,null),!1;return!1},_sasl_failure_cb:function(a){return this._sasl_success_handler&&(this.deleteHandler(this._sasl_success_handler),this._sasl_success_handler=null),this._sasl_challenge_handler&&(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null),this._sasl_mechanism&&this._sasl_mechanism.onFailure(), -this._changeConnectStatus(i.Status.AUTHFAIL,null),!1},_auth2_cb:function(a){return"result"==a.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(i.Status.CONNECTED,null)):"error"==a.getAttribute("type")&&(this._changeConnectStatus(i.Status.AUTHFAIL,null),this.disconnect("authentication failed")),!1},_addSysTimedHandler:function(a,b){var c=new i.TimedHandler(a,b);return c.user=!1,this.addTimeds.push(c),c},_addSysHandler:function(a,b,c,d,e){var f=new i.Handler(a,b,c,d,e);return f.user=!1,this.addHandlers.push(f),f},_onDisconnectTimeout:function(){return i.info("_onDisconnectTimeout was called"),this._changeConnectStatus(i.Status.CONNTIMEOUT,null),this._proto._onDisconnectTimeout(),this._doDisconnect(),!1},_onIdle:function(){for(var a,b,c,d;this.addTimeds.length>0;)this.timedHandlers.push(this.addTimeds.pop());for(;this.removeTimeds.length>0;)b=this.removeTimeds.pop(),a=this.timedHandlers.indexOf(b),a>=0&&this.timedHandlers.splice(a,1);var e=(new Date).getTime();for(d=[],a=0;a=c-e?b.run()&&d.push(b):d.push(b));this.timedHandlers=d,clearTimeout(this._idleTimeout),this._proto._onIdle(),this.connected&&(this._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this),100))}},i.SASLMechanism=function(a,b,c){this.name=a,this.isClientFirst=b,this.priority=c},i.SASLMechanism.prototype={test:function(a){return!0},onStart:function(a){this._connection=a},onChallenge:function(a,b){throw new Error("You should implement challenge handling!")},onFailure:function(){this._connection=null},onSuccess:function(){this._connection=null}},i.SASLAnonymous=function(){},i.SASLAnonymous.prototype=new i.SASLMechanism("ANONYMOUS",!1,10),i.SASLAnonymous.prototype.test=function(a){return null===a.authcid},i.Connection.prototype.mechanisms[i.SASLAnonymous.prototype.name]=i.SASLAnonymous,i.SASLPlain=function(){},i.SASLPlain.prototype=new i.SASLMechanism("PLAIN",!0,20),i.SASLPlain.prototype.test=function(a){return null!==a.authcid},i.SASLPlain.prototype.onChallenge=function(a){var b=a.authzid;return b+="\x00",b+=a.authcid,b+="\x00",b+=a.pass,d.utf16to8(b)},i.Connection.prototype.mechanisms[i.SASLPlain.prototype.name]=i.SASLPlain,i.SASLSHA1=function(){},i.SASLSHA1.prototype=new i.SASLMechanism("SCRAM-SHA-1",!0,40),i.SASLSHA1.prototype.test=function(a){return null!==a.authcid},i.SASLSHA1.prototype.onChallenge=function(e,f,g){var h=g||c.hexdigest(1234567890*Math.random()),i="n="+d.utf16to8(e.authcid);return i+=",r=",i+=h,e._sasl_data.cnonce=h,e._sasl_data["client-first-message-bare"]=i,i="n,,"+i,this.onChallenge=function(c,e){for(var f,g,h,i,j,k,l,m,n,o,p,q,r="c=biws,",s=c._sasl_data["client-first-message-bare"]+","+e+",",t=c._sasl_data.cnonce,u=/([a-z]+)=([^,]+)(,|$)/;e.match(u);){var v=e.match(u);switch(e=e.replace(v[0],""),v[1]){case"r":f=v[2];break;case"s":g=v[2];break;case"i":h=v[2]}}if(f.substr(0,t.length)!==t)return c._sasl_data={},c._sasl_failure_cb();for(r+="r="+f,s+=r,g=b.decode(g),g+="\x00\x00\x00",n=d.utf16to8(c.pass),i=k=a.core_hmac_sha1(n,g),l=1;h>l;l++){for(j=a.core_hmac_sha1(n,a.binb2str(k)),m=0;5>m;m++)i[m]^=j[m];k=j}for(i=a.binb2str(i),o=a.core_hmac_sha1(i,"Client Key"),p=a.str_hmac_sha1(i,"Server Key"),q=a.core_hmac_sha1(a.str_sha1(a.binb2str(o)),s),c._sasl_data["server-signature"]=a.b64_hmac_sha1(p,s),m=0;5>m;m++)o[m]^=q[m];return r+=",p="+b.encode(a.binb2str(o))}.bind(this),i},i.Connection.prototype.mechanisms[i.SASLSHA1.prototype.name]=i.SASLSHA1,i.SASLMD5=function(){},i.SASLMD5.prototype=new i.SASLMechanism("DIGEST-MD5",!1,30),i.SASLMD5.prototype.test=function(a){return null!==a.authcid},i.SASLMD5.prototype._quote=function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'},i.SASLMD5.prototype.onChallenge=function(a,b,e){for(var f,g=/([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/,h=e||c.hexdigest(""+1234567890*Math.random()),i="",j=null,k="",l="";b.match(g);)switch(f=b.match(g),b=b.replace(f[0],""),f[2]=f[2].replace(/^"(.+)"$/,"$1"),f[1]){case"realm":i=f[2];break;case"nonce":k=f[2];break;case"qop":l=f[2];break;case"host":j=f[2]}var m=a.servtype+"/"+a.domain;null!==j&&(m=m+"/"+j);var n=d.utf16to8(a.authcid+":"+i+":"+this._connection.pass),o=c.hash(n)+":"+k+":"+h,p="AUTHENTICATE:"+m,q="";return q+="charset=utf-8,",q+="username="+this._quote(d.utf16to8(a.authcid))+",",q+="realm="+this._quote(i)+",",q+="nonce="+this._quote(k)+",",q+="nc=00000001,",q+="cnonce="+this._quote(h)+",",q+="digest-uri="+this._quote(m)+",",q+="response="+c.hexdigest(c.hexdigest(o)+":"+k+":00000001:"+h+":auth:"+c.hexdigest(p))+",",q+="qop=auth",this.onChallenge=function(){return""},q},i.Connection.prototype.mechanisms[i.SASLMD5.prototype.name]=i.SASLMD5,i.SASLOAuthBearer=function(){},i.SASLOAuthBearer.prototype=new i.SASLMechanism("OAUTHBEARER",!0,50),i.SASLOAuthBearer.prototype.test=function(a){return null!==a.authcid},i.SASLOAuthBearer.prototype.onChallenge=function(a){var b="n,a=";return b+=a.authzid,b+=",",b+="",b+="auth=Bearer ",b+=a.pass,b+="",b+="",d.utf16to8(b)},i.Connection.prototype.mechanisms[i.SASLOAuthBearer.prototype.name]=i.SASLOAuthBearer,i.SASLExternal=function(){},i.SASLExternal.prototype=new i.SASLMechanism("EXTERNAL",!0,60),i.SASLExternal.prototype.onChallenge=function(a){return a.authcid===a.authzid?"":a.authzid},i.Connection.prototype.mechanisms[i.SASLExternal.prototype.name]=i.SASLExternal,{Strophe:i,$build:e,$msg:f,$iq:g,$pres:h,SHA1:a,Base64:b,MD5:c}}),function(a,b){return"function"==typeof define&&define.amd?void define("strophe-bosh",["strophe-core"],function(a){return b(a.Strophe,a.$build)}):b(Strophe,$build)}(this,function(a,b){return a.Request=function(b,c,d,e){this.id=++a._requestId,this.xmlData=b,this.data=a.serialize(b),this.origFunc=c,this.func=c,this.rid=d,this.date=NaN,this.sends=e||0,this.abort=!1,this.dead=null,this.age=function(){if(!this.date)return 0;var a=new Date;return(a-this.date)/1e3},this.timeDead=function(){if(!this.dead)return 0;var a=new Date;return(a-this.dead)/1e3},this.xhr=this._newXHR()},a.Request.prototype={getResponse:function(){var b=null;if(this.xhr.responseXML&&this.xhr.responseXML.documentElement){if(b=this.xhr.responseXML.documentElement,"parsererror"==b.tagName)throw a.error("invalid response received"),a.error("responseText: "+this.xhr.responseText),a.error("responseXML: "+a.serialize(this.xhr.responseXML)),"parsererror"}else if(this.xhr.responseText)throw a.error("invalid response received"),a.error("responseText: "+this.xhr.responseText),"badformat";return b},_newXHR:function(){var a=null;return window.XMLHttpRequest?(a=new XMLHttpRequest,a.overrideMimeType&&a.overrideMimeType("text/xml; charset=utf-8")):window.ActiveXObject&&(a=new ActiveXObject("Microsoft.XMLHTTP")),a.onreadystatechange=this.func.bind(null,this),a}},a.Bosh=function(a){this._conn=a,this.rid=Math.floor(4294967295*Math.random()),this.sid=null,this.hold=1,this.wait=60,this.window=5,this.errors=0,this._requests=[]},a.Bosh.prototype={strip:null,_buildBody:function(){var c=b("body",{rid:this.rid++,xmlns:a.NS.HTTPBIND});return null!==this.sid&&c.attrs({sid:this.sid}),this._conn.options.keepalive&&this._conn._sessionCachingSupported()&&this._cacheSession(),c},_reset:function(){this.rid=Math.floor(4294967295*Math.random()),this.sid=null,this.errors=0,this._conn._sessionCachingSupported()&&window.sessionStorage.removeItem("strophe-bosh-session"),this._conn.nextValidRid(this.rid)},_connect:function(b,c,d){this.wait=b||this.wait,this.hold=c||this.hold,this.errors=0;var e=this._buildBody().attrs({to:this._conn.domain,"xml:lang":"en",wait:this.wait,hold:this.hold,content:"text/xml; charset=utf-8",ver:"1.6","xmpp:version":"1.0","xmlns:xmpp":a.NS.BOSH});d&&e.attrs({route:d});var f=this._conn._connect_cb;this._requests.push(new a.Request(e.tree(),this._onRequestStateChange.bind(this,f.bind(this._conn)),e.tree().getAttribute("rid"))),this._throttledRequestHandler()},_attach:function(b,c,d,e,f,g,h){this._conn.jid=b,this.sid=c,this.rid=d,this._conn.connect_callback=e,this._conn.domain=a.getDomainFromJid(this._conn.jid),this._conn.authenticated=!0,this._conn.connected=!0,this.wait=f||this.wait,this.hold=g||this.hold,this.window=h||this.window,this._conn._changeConnectStatus(a.Status.ATTACHED,null)},_restore:function(b,c,d,e,f){var g=JSON.parse(window.sessionStorage.getItem("strophe-bosh-session"));if(!("undefined"!=typeof g&&null!==g&&g.rid&&g.sid&&g.jid)||"undefined"!=typeof b&&null!==b&&a.getBareJidFromJid(g.jid)!=a.getBareJidFromJid(b))throw{name:"StropheSessionError",message:"_restore: no restoreable session."};this._conn.restored=!0,this._attach(g.jid,g.sid,g.rid,c,d,e,f)},_cacheSession:function(){this._conn.authenticated?this._conn.jid&&this.rid&&this.sid&&window.sessionStorage.setItem("strophe-bosh-session",JSON.stringify({jid:this._conn.jid,rid:this.rid,sid:this.sid})):window.sessionStorage.removeItem("strophe-bosh-session")},_connect_cb:function(b){var c,d,e=b.getAttribute("type");if(null!==e&&"terminate"==e)return c=b.getAttribute("condition"),a.error("BOSH-Connection failed: "+c),d=b.getElementsByTagName("conflict"),null!==c?("remote-stream-error"==c&&d.length>0&&(c="conflict"),this._conn._changeConnectStatus(a.Status.CONNFAIL,c)):this._conn._changeConnectStatus(a.Status.CONNFAIL,"unknown"),this._conn._doDisconnect(c),a.Status.CONNFAIL;this.sid||(this.sid=b.getAttribute("sid"));var f=b.getAttribute("requests");f&&(this.window=parseInt(f,10));var g=b.getAttribute("hold");g&&(this.hold=parseInt(g,10));var h=b.getAttribute("wait");h&&(this.wait=parseInt(h,10))},_disconnect:function(a){this._sendTerminate(a)},_doDisconnect:function(){this.sid=null,this.rid=Math.floor(4294967295*Math.random()),this._conn._sessionCachingSupported()&&window.sessionStorage.removeItem("strophe-bosh-session"),this._conn.nextValidRid(this.rid)},_emptyQueue:function(){return 0===this._requests.length},_hitError:function(b){this.errors++,a.warn("request errored, status: "+b+", number of errors: "+this.errors),this.errors>4&&this._conn._onDisconnectTimeout()},_no_auth_received:function(b){b=b?b.bind(this._conn):this._conn._connect_cb.bind(this._conn);var c=this._buildBody();this._requests.push(new a.Request(c.tree(),this._onRequestStateChange.bind(this,b.bind(this._conn)),c.tree().getAttribute("rid"))),this._throttledRequestHandler()},_onDisconnectTimeout:function(){this._abortAllRequests()},_abortAllRequests:function(){for(var a;this._requests.length>0;)a=this._requests.pop(),a.abort=!0,a.xhr.abort(),a.xhr.onreadystatechange=function(){}},_onIdle:function(){var b=this._conn._data;if(this._conn.authenticated&&0===this._requests.length&&0===b.length&&!this._conn.disconnecting&&(a.info("no requests during idle cycle, sending blank request"),b.push(null)),!this._conn.paused){if(this._requests.length<2&&b.length>0){for(var c=this._buildBody(),d=0;d0){var e=this._requests[0].age();null!==this._requests[0].dead&&this._requests[0].timeDead()>Math.floor(a.SECONDARY_TIMEOUT*this.wait)&&this._throttledRequestHandler(),e>Math.floor(a.TIMEOUT*this.wait)&&(a.warn("Request "+this._requests[0].id+" timed out, over "+Math.floor(a.TIMEOUT*this.wait)+" seconds since last activity"),this._throttledRequestHandler())}}},_onRequestStateChange:function(b,c){if(a.debug("request id "+c.id+"."+c.sends+" state changed to "+c.xhr.readyState),c.abort)return void(c.abort=!1);var d;if(4==c.xhr.readyState){d=0;try{d=c.xhr.status}catch(e){}if("undefined"==typeof d&&(d=0),this.disconnecting&&d>=400)return void this._hitError(d);var f=this._requests[0]==c,g=this._requests[1]==c;(d>0&&500>d||c.sends>5)&&(this._removeRequest(c),a.debug("request id "+c.id+" should now be removed")),200==d?((g||f&&this._requests.length>0&&this._requests[0].age()>Math.floor(a.SECONDARY_TIMEOUT*this.wait))&&this._restartRequest(0),this._conn.nextValidRid(Number(c.rid)+1),a.debug("request id "+c.id+"."+c.sends+" got 200"),b(c),this.errors=0):(a.error("request id "+c.id+"."+c.sends+" error "+d+" happened"),(0===d||d>=400&&600>d||d>=12e3)&&(this._hitError(d),d>=400&&500>d&&(this._conn._changeConnectStatus(a.Status.DISCONNECTING,null),this._conn._doDisconnect()))),d>0&&500>d||c.sends>5||this._throttledRequestHandler()}},_processRequest:function(b){var c=this,d=this._requests[b],e=-1;try{4==d.xhr.readyState&&(e=d.xhr.status)}catch(f){a.error("caught an error in _requests["+b+"], reqStatus: "+e)}if("undefined"==typeof e&&(e=-1),d.sends>this._conn.maxRetries)return void this._conn._onDisconnectTimeout();var g=d.age(),h=!isNaN(g)&&g>Math.floor(a.TIMEOUT*this.wait),i=null!==d.dead&&d.timeDead()>Math.floor(a.SECONDARY_TIMEOUT*this.wait),j=4==d.xhr.readyState&&(1>e||e>=500);if((h||i||j)&&(i&&a.error("Request "+this._requests[b].id+" timed out (secondary), restarting"),d.abort=!0,d.xhr.abort(),d.xhr.onreadystatechange=function(){},this._requests[b]=new a.Request(d.xmlData,d.origFunc,d.rid,d.sends),d=this._requests[b]),0===d.xhr.readyState){a.debug("request id "+d.id+"."+d.sends+" posting");try{var k=this._conn.options.contentType||"text/xml; charset=utf-8";d.xhr.open("POST",this._conn.service,this._conn.options.sync?!1:!0),d.xhr.setRequestHeader("Content-Type",k),this._conn.options.withCredentials&&(d.xhr.withCredentials=!0)}catch(l){return a.error("XHR open failed."),this._conn.connected||this._conn._changeConnectStatus(a.Status.CONNFAIL,"bad-service"),void this._conn.disconnect()}var m=function(){if(d.date=new Date,c._conn.options.customHeaders){var a=c._conn.options.customHeaders;for(var b in a)a.hasOwnProperty(b)&&d.xhr.setRequestHeader(b,a[b])}d.xhr.send(d.data)};if(d.sends>1){var n=1e3*Math.min(Math.floor(a.TIMEOUT*this.wait),Math.pow(d.sends,3));setTimeout(function(){m()},n)}else m();d.sends++,this._conn.xmlOutput!==a.Connection.prototype.xmlOutput&&(d.xmlData.nodeName===this.strip&&d.xmlData.childNodes.length?this._conn.xmlOutput(d.xmlData.childNodes[0]):this._conn.xmlOutput(d.xmlData)),this._conn.rawOutput!==a.Connection.prototype.rawOutput&&this._conn.rawOutput(d.data)}else a.debug("_processRequest: "+(0===b?"first":"second")+" request has readyState of "+d.xhr.readyState)},_removeRequest:function(b){a.debug("removing request");var c;for(c=this._requests.length-1;c>=0;c--)b==this._requests[c]&&this._requests.splice(c,1);b.xhr.onreadystatechange=function(){},this._throttledRequestHandler()},_restartRequest:function(a){var b=this._requests[a];null===b.dead&&(b.dead=new Date),this._processRequest(a)},_reqToData:function(a){try{return a.getResponse()}catch(b){if("parsererror"!=b)throw b;this._conn.disconnect("strophe-parsererror")}},_sendTerminate:function(b){a.info("_sendTerminate was called");var c=this._buildBody().attrs({type:"terminate"});b&&c.cnode(b.tree());var d=new a.Request(c.tree(),this._onRequestStateChange.bind(this,this._conn._dataRecv.bind(this._conn)),c.tree().getAttribute("rid"));this._requests.push(d),this._throttledRequestHandler()},_send:function(){clearTimeout(this._conn._idleTimeout),this._throttledRequestHandler(),this._conn._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this._conn),100)},_sendRestart:function(){this._throttledRequestHandler(),clearTimeout(this._conn._idleTimeout)},_throttledRequestHandler:function(){this._requests?a.debug("_throttledRequestHandler called with "+this._requests.length+" requests"):a.debug("_throttledRequestHandler called with undefined requests"),this._requests&&0!==this._requests.length&&(this._requests.length>0&&this._processRequest(0),this._requests.length>1&&Math.abs(this._requests[0].rid-this._requests[1].rid): "+d);var e=b.getAttribute("version");return"string"!=typeof e?c="Missing version in ":"1.0"!==e&&(c="Wrong version in : "+e),c?(this._conn._changeConnectStatus(a.Status.CONNFAIL,c),this._conn._doDisconnect(),!1):!0},_connect_cb_wrapper:function(b){if(0===b.data.indexOf("\s*)*/,"");if(""===c)return;var d=(new DOMParser).parseFromString(c,"text/xml").documentElement;this._conn.xmlInput(d),this._conn.rawInput(b.data),this._handleStreamStart(d)&&this._connect_cb(d)}else if(0===b.data.indexOf(" tag.")}}this._conn._doDisconnect()},_doDisconnect:function(){a.info("WebSockets _doDisconnect was called"),this._closeSocket()},_streamWrap:function(a){return""+a+""},_closeSocket:function(){if(this.socket)try{this.socket.close()}catch(a){}this.socket=null},_emptyQueue:function(){return!0},_onClose:function(){this._conn.connected&&!this._conn.disconnecting?(a.error("Websocket closed unexpectedly"),this._conn._doDisconnect()):a.info("Websocket closed")},_no_auth_received:function(b){a.error("Server did not send any auth methods"),this._conn._changeConnectStatus(a.Status.CONNFAIL,"Server did not send any auth methods"),b&&(b=b.bind(this._conn))(),this._conn._doDisconnect()},_onDisconnectTimeout:function(){},_abortAllRequests:function(){},_onError:function(b){a.error("Websocket error "+b),this._conn._changeConnectStatus(a.Status.CONNFAIL,"The WebSocket connection could not be established or was disconnected."),this._disconnect()},_onIdle:function(){var b=this._conn._data;if(b.length>0&&!this._conn.paused){for(var c=0;c>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,isNaN(d)?(g=(3&c)<<4,h=i=64):isNaN(e)&&(i=64),j=j+a.charAt(f)+a.charAt(g)+a.charAt(h)+a.charAt(i);while(k>4,d=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(c),64!=h&&(j+=String.fromCharCode(d)),64!=i&&(j+=String.fromCharCode(e));while(k>5]|=128<<24-d%32,a[(d+64>>9<<4)+15]=d;var g,h,i,j,k,l,m,n,o=new Array(80),p=1732584193,q=-271733879,r=-1732584194,s=271733878,t=-1009589776;for(g=0;g16&&(d=a(d,8*b.length));for(var e=new Array(16),f=new Array(16),h=0;h<16;h++)e[h]=909522486^d[h],f[h]=1549556828^d[h];var i=a(e.concat(g(c)),512+8*c.length);return a(f.concat(i),672)}function e(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c}function f(a,b){return a<>>32-b}function g(a){for(var b=[],c=255,d=0;d<8*a.length;d+=8)b[d>>5]|=(a.charCodeAt(d/8)&c)<<24-d%32;return b}function h(a){for(var b="",c=255,d=0;d<32*a.length;d+=8)b+=String.fromCharCode(a[d>>5]>>>24-d%32&c);return b}function i(a){for(var b,c,d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",e="",f=0;f<4*a.length;f+=3)for(b=(a[f>>2]>>8*(3-f%4)&255)<<16|(a[f+1>>2]>>8*(3-(f+1)%4)&255)<<8|a[f+2>>2]>>8*(3-(f+2)%4)&255,c=0;c<4;c++)e+=8*f+6*c>32*a.length?"=":d.charAt(b>>6*(3-c)&63);return e}return{b64_hmac_sha1:function(a,b){return i(d(a,b))},b64_sha1:function(b){return i(a(g(b),8*b.length))},binb2str:h,core_hmac_sha1:d,str_hmac_sha1:function(a,b){return h(d(a,b))},str_sha1:function(b){return h(a(g(b),8*b.length))}}}),function(a,b){"function"==typeof define&&define.amd?define("strophe-md5",function(){return b()}):a.MD5=b()}(this,function(a){var b=function(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c},c=function(a,b){return a<>>32-b},d=function(a){for(var b=[],c=0;c<8*a.length;c+=8)b[c>>5]|=(255&a.charCodeAt(c/8))<>5]>>>c%32&255);return b},f=function(a){for(var b="0123456789abcdef",c="",d=0;d<4*a.length;d++)c+=b.charAt(a[d>>2]>>d%4*8+4&15)+b.charAt(a[d>>2]>>d%4*8&15);return c},g=function(a,d,e,f,g,h){return b(c(b(b(d,a),b(f,h)),g),e)},h=function(a,b,c,d,e,f,h){return g(b&c|~b&d,a,b,e,f,h)},i=function(a,b,c,d,e,f,h){return g(b&d|c&~d,a,b,e,f,h)},j=function(a,b,c,d,e,f,h){return g(b^c^d,a,b,e,f,h)},k=function(a,b,c,d,e,f,h){return g(c^(b|~d),a,b,e,f,h)},l=function(a,c){a[c>>5]|=128<>>9<<4)+14]=c;for(var d,e,f,g,l=1732584193,m=-271733879,n=-1732584194,o=271733878,p=0;p=0&&c<=127?d+=a.charAt(b):c>2047?(d+=String.fromCharCode(224|c>>12&15),d+=String.fromCharCode(128|c>>6&63),d+=String.fromCharCode(128|c>>0&63)):(d+=String.fromCharCode(192|c>>6&31),d+=String.fromCharCode(128|c>>0&63));return d},addCookies:function(a){var b,c,d,e,f,g,h;for(b in a||{})f="",g="",h="",c=a[b],d="object"==typeof c,e=escape(unescape(d?c.value:c)),d&&(f=c.expires?";expires="+c.expires:"",g=c.domain?";domain="+c.domain:"",h=c.path?";path="+c.path:""),document.cookie=b+"="+e+f+g+h}};return a}),function(a,b){return"function"==typeof define&&define.amd?void define("strophe-polyfill",[],function(){return b()}):b()}(this,function(){Function.prototype.bind||(Function.prototype.bind=function(a){var b=this,c=Array.prototype.slice,d=Array.prototype.concat,e=c.call(arguments,1);return function(){return b.apply(a?a:this,d.call(e,c.call(arguments,0)))}}),Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length,c=Number(arguments[1])||0;for(c=c<0?Math.ceil(c):Math.floor(c),c<0&&(c+=b);c>>0;if("function"!=typeof a)throw new TypeError(a+" is not a function");for(arguments.length>1&&(c=b),d=0;d0)for(var c=0;c/g,">"),a=a.replace(/'/g,"'"),a=a.replace(/"/g,""")},xmlunescape:function(a){return a=a.replace(/\&/g,"&"),a=a.replace(/</g,"<"),a=a.replace(/>/g,">"),a=a.replace(/'/g,"'"),a=a.replace(/"/g,'"')},xmlTextNode:function(a){return i.xmlGenerator().createTextNode(a)},xmlHtmlNode:function(a){var b;if(window.DOMParser){var c=new DOMParser;b=c.parseFromString(a,"text/xml")}else b=new ActiveXObject("Microsoft.XMLDOM"),b.async="false",b.loadXML(a);return b},getText:function(a){if(!a)return null;var b="";0===a.childNodes.length&&a.nodeType==i.ElementType.TEXT&&(b+=a.nodeValue);for(var c=0;c0&&(g=h.join("; "),c.setAttribute(f,g))}else c.setAttribute(f,g);for(b=0;b/g,"\\3e").replace(/@/g,"\\40")},unescapeNode:function(a){return"string"!=typeof a?a:a.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,"\\")},getNodeFromJid:function(a){return a.indexOf("@")<0?null:a.split("@")[0]},getDomainFromJid:function(a){var b=i.getBareJidFromJid(a);if(b.indexOf("@")<0)return b;var c=b.split("@");return c.splice(0,1),c.join("@")},getResourceFromJid:function(a){var b=a.split("/");return b.length<2?null:(b.splice(0,1),b.join("/"))},getBareJidFromJid:function(a){return a?a.split("/")[0]:null},_handleError:function(a){"undefined"!=typeof a.stack&&i.fatal(a.stack),a.sourceURL?i.fatal("error: "+this.handler+" "+a.sourceURL+":"+a.line+" - "+a.name+": "+a.message):a.fileName?i.fatal("error: "+this.handler+" "+a.fileName+":"+a.lineNumber+" - "+a.name+": "+a.message):i.fatal("error: "+a.message)},log:function(a,b){},debug:function(a){this.log(this.LogLevel.DEBUG,a)},info:function(a){this.log(this.LogLevel.INFO,a)},warn:function(a){this.log(this.LogLevel.WARN,a)},error:function(a){this.log(this.LogLevel.ERROR,a)},fatal:function(a){this.log(this.LogLevel.FATAL,a)},serialize:function(a){var b;if(!a)return null;"function"==typeof a.tree&&(a=a.tree());var c,d,e=a.nodeName;for(a.getAttribute("_realname")&&(e=a.getAttribute("_realname")),b="<"+e,c=0;c0){for(b+=">",c=0;c"}b+=""}else b+="/>";return b},_requestId:0,_connectionPlugins:{},addConnectionPlugin:function(a,b){i._connectionPlugins[a]=b}},i.Builder=function(a,b){"presence"!=a&&"message"!=a&&"iq"!=a||(b&&!b.xmlns?b.xmlns=i.NS.CLIENT:b||(b={xmlns:i.NS.CLIENT})),this.nodeTree=i.xmlElement(a,b),this.node=this.nodeTree},i.Builder.prototype={tree:function(){return this.nodeTree},toString:function(){return i.serialize(this.nodeTree)},up:function(){return this.node=this.node.parentNode,this},root:function(){return this.node=this.nodeTree,this},attrs:function(a){for(var b in a)a.hasOwnProperty(b)&&(void 0===a[b]?this.node.removeAttribute(b):this.node.setAttribute(b,a[b]));return this},c:function(a,b,c){var d=i.xmlElement(a,b,c);return this.node.appendChild(d),"string"!=typeof c&&"number"!=typeof c&&(this.node=d),this},cnode:function(a){var b,c=i.xmlGenerator();try{b=void 0!==c.importNode}catch(a){b=!1}var d=b?c.importNode(a,!0):i.copyElement(a);return this.node.appendChild(d),this.node=d,this},t:function(a){var b=i.xmlTextNode(a);return this.node.appendChild(b),this},h:function(a){var b=document.createElement("body");b.innerHTML=a;for(var c=i.createHtml(b);c.childNodes.length>0;)this.node.appendChild(c.childNodes[0]);return this}},i.Handler=function(a,b,c,d,e,f,g){this.handler=a,this.ns=b,this.name=c,this.type=d,this.id=e,this.options=g||{matchBareFromJid:!1,ignoreNamespaceFragment:!1},this.options.matchBare&&(i.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.'),this.options.matchBareFromJid=this.options.matchBare,delete this.options.matchBare),this.options.matchBareFromJid?this.from=f?i.getBareJidFromJid(f):null:this.from=f,this.user=!0},i.Handler.prototype={getNamespace:function(a){var b=a.getAttribute("xmlns");return b&&this.options.ignoreNamespaceFragment&&(b=b.split("#")[0]),b},namespaceMatch:function(a){var b=!1;if(!this.ns)return!0;var c=this;return i.forEachChild(a,null,function(a){c.getNamespace(a)===c.ns&&(b=!0)}),b=b||this.getNamespace(a)===this.ns},isMatch:function(a){var b=a.getAttribute("from");this.options.matchBareFromJid&&(b=i.getBareJidFromJid(b));var c=a.getAttribute("type");return!(!this.namespaceMatch(a)||this.name&&!i.isTagEqual(a,this.name)||this.type&&(Array.isArray(this.type)?this.type.indexOf(c)==-1:c!=this.type)||this.id&&a.getAttribute("id")!=this.id||this.from&&b!=this.from)},run:function(a){var b=null;try{b=this.handler(a)}catch(a){throw i._handleError(a),a}return b},toString:function(){return"{Handler: "+this.handler+"("+this.name+","+this.id+","+this.ns+")}"}},i.TimedHandler=function(a,b){this.period=a,this.handler=b,this.lastCalled=(new Date).getTime(),this.user=!0},i.TimedHandler.prototype={run:function(){return this.lastCalled=(new Date).getTime(),this.handler()},reset:function(){this.lastCalled=(new Date).getTime()},toString:function(){return"{TimedHandler: "+this.handler+"("+this.period+")}"}},i.Connection=function(a,b){this.service=a,this.options=b||{};var c=this.options.protocol||"";0===a.indexOf("ws:")||0===a.indexOf("wss:")||0===c.indexOf("ws")?this._proto=new i.Websocket(this):this._proto=new i.Bosh(this),this.jid="",this.domain=null,this.features=null,this._sasl_data={},this.do_session=!1,this.do_bind=!1,this.timedHandlers=[],this.handlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this.protocolErrorHandlers={HTTP:{},websocket:{}},this._idleTimeout=null,this._disconnectTimeout=null,this.authenticated=!1,this.connected=!1,this.disconnecting=!1,this.do_authentication=!0,this.paused=!1,this.restored=!1,this._data=[],this._uniqueId=0,this._sasl_success_handler=null,this._sasl_failure_handler=null,this._sasl_challenge_handler=null,this.maxRetries=5,this._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this),100),d.addCookies(this.options.cookies),this.registerSASLMechanisms(this.options.mechanisms);for(var e in i._connectionPlugins)if(i._connectionPlugins.hasOwnProperty(e)){var f=i._connectionPlugins[e],g=function(){};g.prototype=f,this[e]=new g,this[e].init(this)}},i.Connection.prototype={reset:function(){this._proto._reset(),this.do_session=!1,this.do_bind=!1,this.timedHandlers=[],this.handlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this.authenticated=!1,this.connected=!1,this.disconnecting=!1,this.restored=!1,this._data=[],this._requests=[],this._uniqueId=0},pause:function(){this.paused=!0},resume:function(){this.paused=!1},getUniqueId:function(a){var b="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"==a?b:3&b|8;return c.toString(16)});return"string"==typeof a||"number"==typeof a?b+":"+a:b+""},addProtocolErrorHandler:function(a,b,c){this.protocolErrorHandlers[a][b]=c},connect:function(a,b,c,d,e,f,g){this.jid=a,this.authzid=i.getBareJidFromJid(this.jid),this.authcid=g||i.getNodeFromJid(this.jid),this.pass=b,this.servtype="xmpp",this.connect_callback=c,this.disconnecting=!1,this.connected=!1,this.authenticated=!1,this.restored=!1,this.domain=i.getDomainFromJid(this.jid),this._changeConnectStatus(i.Status.CONNECTING,null),this._proto._connect(d,e,f)},attach:function(a,b,c,d,e,f,g){if(!(this._proto instanceof i.Bosh))throw{name:"StropheSessionError",message:'The "attach" method can only be used with a BOSH connection.'};this._proto._attach(a,b,c,d,e,f,g)},restore:function(a,b,c,d,e){if(!this._sessionCachingSupported())throw{name:"StropheSessionError",message:'The "restore" method can only be used with a BOSH connection.'};this._proto._restore(a,b,c,d,e)},_sessionCachingSupported:function(){if(this._proto instanceof i.Bosh){if(!JSON)return!1;try{window.sessionStorage.setItem("_strophe_","_strophe_"),window.sessionStorage.removeItem("_strophe_")}catch(a){return!1}return!0}return!1},xmlInput:function(a){},xmlOutput:function(a){},rawInput:function(a){},rawOutput:function(a){},nextValidRid:function(a){},send:function(a){if(null!==a){if("function"==typeof a.sort)for(var b=0;b=0&&this.addHandlers.splice(b,1)},registerSASLMechanisms:function(a){this.mechanisms={},a=a||[i.SASLAnonymous,i.SASLExternal,i.SASLMD5,i.SASLOAuthBearer,i.SASLPlain,i.SASLSHA1],a.forEach(this.registerSASLMechanism.bind(this))},registerSASLMechanism:function(a){this.mechanisms[a.prototype.name]=a},disconnect:function(a){if(this._changeConnectStatus(i.Status.DISCONNECTING,a),i.info("Disconnect was called because: "+a),this.connected){var b=!1;this.disconnecting=!0,this.authenticated&&(b=h({xmlns:i.NS.CLIENT,type:"unavailable"})),this._disconnectTimeout=this._addSysTimedHandler(3e3,this._onDisconnectTimeout.bind(this)),this._proto._disconnect(b)}else i.info("Disconnect was called before Strophe connected to the server"),this._proto._abortAllRequests()},_changeConnectStatus:function(a,b){for(var c in i._connectionPlugins)if(i._connectionPlugins.hasOwnProperty(c)){var d=this[c];if(d.statusChanged)try{d.statusChanged(a,b)}catch(a){i.error(""+c+" plugin caused an exception changing status: "+a)}}if(this.connect_callback)try{this.connect_callback(a,b)}catch(a){i._handleError(a),i.error("User connection callback caused an exception: "+a)}},_doDisconnect:function(a){"number"==typeof this._idleTimeout&&clearTimeout(this._idleTimeout),null!==this._disconnectTimeout&&(this.deleteTimedHandler(this._disconnectTimeout),this._disconnectTimeout=null),i.info("_doDisconnect was called"),this._proto._doDisconnect(),this.authenticated=!1,this.disconnecting=!1,this.restored=!1,this.handlers=[],this.timedHandlers=[],this.removeTimeds=[],this.removeHandlers=[],this.addTimeds=[],this.addHandlers=[],this._changeConnectStatus(i.Status.DISCONNECTED,a),this.connected=!1},_dataRecv:function(a,b){i.info("_dataRecv called");var c=this._proto._reqToData(a);if(null!==c){this.xmlInput!==i.Connection.prototype.xmlInput&&(c.nodeName===this._proto.strip&&c.childNodes.length?this.xmlInput(c.childNodes[0]):this.xmlInput(c)),this.rawInput!==i.Connection.prototype.rawInput&&(b?this.rawInput(b):this.rawInput(i.serialize(c)));for(var d,e;this.removeHandlers.length>0;)e=this.removeHandlers.pop(),d=this.handlers.indexOf(e),d>=0&&this.handlers.splice(d,1);for(;this.addHandlers.length>0;)this.handlers.push(this.addHandlers.pop());if(this.disconnecting&&this._proto._emptyQueue())return void this._doDisconnect();var f,g,h=c.getAttribute("type");if(null!==h&&"terminate"==h){if(this.disconnecting)return;return f=c.getAttribute("condition"),g=c.getElementsByTagName("conflict"),null!==f?("remote-stream-error"==f&&g.length>0&&(f="conflict"),this._changeConnectStatus(i.Status.CONNFAIL,f)):this._changeConnectStatus(i.Status.CONNFAIL,"unknown"),void this._doDisconnect(f)}var j=this;i.forEachChild(c,null,function(a){var b,c;for(c=j.handlers,j.handlers=[],b=0;b0:d.getElementsByTagName("stream:features").length>0||d.getElementsByTagName("features").length>0,!f)return void this._proto._no_auth_received(b);var g,h,j=[],k=d.getElementsByTagName("mechanism");if(k.length>0)for(g=0;ga[d].prototype.priority&&(d=c);d!=b&&(e=a[b],a[b]=a[d],a[d]=e)}return a},_attemptSASLAuth:function(a){a=this.sortMechanismsByPriority(a||[]);var c=0,d=!1;for(c=0;c0&&(b="conflict"),this._changeConnectStatus(i.Status.AUTHFAIL,b),!1}var d,e=a.getElementsByTagName("bind");return e.length>0?(d=e[0].getElementsByTagName("jid"), +void(d.length>0&&(this.jid=i.getText(d[0]),this.do_session?(this._addSysHandler(this._sasl_session_cb.bind(this),null,null,null,"_session_auth_2"),this.send(g({type:"set",id:"_session_auth_2"}).c("session",{xmlns:i.NS.SESSION}).tree())):(this.authenticated=!0,this._changeConnectStatus(i.Status.CONNECTED,null))))):(i.info("SASL binding failed."),this._changeConnectStatus(i.Status.AUTHFAIL,null),!1)},_sasl_session_cb:function(a){if("result"==a.getAttribute("type"))this.authenticated=!0,this._changeConnectStatus(i.Status.CONNECTED,null);else if("error"==a.getAttribute("type"))return i.info("Session creation failed."),this._changeConnectStatus(i.Status.AUTHFAIL,null),!1;return!1},_sasl_failure_cb:function(a){return this._sasl_success_handler&&(this.deleteHandler(this._sasl_success_handler),this._sasl_success_handler=null),this._sasl_challenge_handler&&(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null),this._sasl_mechanism&&this._sasl_mechanism.onFailure(),this._changeConnectStatus(i.Status.AUTHFAIL,null),!1},_auth2_cb:function(a){return"result"==a.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(i.Status.CONNECTED,null)):"error"==a.getAttribute("type")&&(this._changeConnectStatus(i.Status.AUTHFAIL,null),this.disconnect("authentication failed")),!1},_addSysTimedHandler:function(a,b){var c=new i.TimedHandler(a,b);return c.user=!1,this.addTimeds.push(c),c},_addSysHandler:function(a,b,c,d,e){var f=new i.Handler(a,b,c,d,e);return f.user=!1,this.addHandlers.push(f),f},_onDisconnectTimeout:function(){return i.info("_onDisconnectTimeout was called"),this._changeConnectStatus(i.Status.CONNTIMEOUT,null),this._proto._onDisconnectTimeout(),this._doDisconnect(),!1},_onIdle:function(){for(var a,b,c,d;this.addTimeds.length>0;)this.timedHandlers.push(this.addTimeds.pop());for(;this.removeTimeds.length>0;)b=this.removeTimeds.pop(),a=this.timedHandlers.indexOf(b),a>=0&&this.timedHandlers.splice(a,1);var e=(new Date).getTime();for(d=[],a=0;a0&&(c="conflict"),this._conn._changeConnectStatus(a.Status.CONNFAIL,c)):this._conn._changeConnectStatus(a.Status.CONNFAIL,"unknown"),this._conn._doDisconnect(c),a.Status.CONNFAIL;this.sid||(this.sid=b.getAttribute("sid"));var f=b.getAttribute("requests");f&&(this.window=parseInt(f,10));var g=b.getAttribute("hold");g&&(this.hold=parseInt(g,10));var h=b.getAttribute("wait");h&&(this.wait=parseInt(h,10))},_disconnect:function(a){this._sendTerminate(a)},_doDisconnect:function(){this.sid=null,this.rid=Math.floor(4294967295*Math.random()),this._conn._sessionCachingSupported()&&window.sessionStorage.removeItem("strophe-bosh-session"),this._conn.nextValidRid(this.rid)},_emptyQueue:function(){return 0===this._requests.length},_callProtocolErrorHandlers:function(a){var b,c=this._getRequestStatus(a);b=this._conn.protocolErrorHandlers.HTTP[c],b&&b.call(this,c)},_hitError:function(b){this.errors++,a.warn("request errored, status: "+b+", number of errors: "+this.errors),this.errors>4&&this._conn._onDisconnectTimeout()},_no_auth_received:function(b){b=b?b.bind(this._conn):this._conn._connect_cb.bind(this._conn);var c=this._buildBody();this._requests.push(new a.Request(c.tree(),this._onRequestStateChange.bind(this,b.bind(this._conn)),c.tree().getAttribute("rid"))),this._throttledRequestHandler()},_onDisconnectTimeout:function(){this._abortAllRequests()},_abortAllRequests:function(){for(var a;this._requests.length>0;)a=this._requests.pop(),a.abort=!0,a.xhr.abort(),a.xhr.onreadystatechange=function(){}},_onIdle:function(){var b=this._conn._data;if(this._conn.authenticated&&0===this._requests.length&&0===b.length&&!this._conn.disconnecting&&(a.info("no requests during idle cycle, sending blank request"),b.push(null)),!this._conn.paused){if(this._requests.length<2&&b.length>0){for(var c=this._buildBody(),d=0;d0){var e=this._requests[0].age();null!==this._requests[0].dead&&this._requests[0].timeDead()>Math.floor(a.SECONDARY_TIMEOUT*this.wait)&&this._throttledRequestHandler(),e>Math.floor(a.TIMEOUT*this.wait)&&(a.warn("Request "+this._requests[0].id+" timed out, over "+Math.floor(a.TIMEOUT*this.wait)+" seconds since last activity"),this._throttledRequestHandler())}}},_getRequestStatus:function(b,c){var d;if(4==b.xhr.readyState)try{d=b.xhr.status}catch(b){a.error("Caught an error while retrieving a request's status, reqStatus: "+d)}return"undefined"==typeof d&&(d="number"==typeof c?c:0),d},_onRequestStateChange:function(b,c){if(a.debug("request id "+c.id+"."+c.sends+" state changed to "+c.xhr.readyState),c.abort)return void(c.abort=!1);if(4===c.xhr.readyState){var d=this._getRequestStatus(c);if(this.disconnecting&&d>=400)return this._hitError(d),void this._callProtocolErrorHandlers(c);if((d>0&&d<500||c.sends>5)&&(this._removeRequest(c),a.debug("request id "+c.id+" should now be removed")),200==d){var e=this._requests[0]==c,f=this._requests[1]==c;(f||e&&this._requests.length>0&&this._requests[0].age()>Math.floor(a.SECONDARY_TIMEOUT*this.wait))&&this._restartRequest(0),this._conn.nextValidRid(Number(c.rid)+1),a.debug("request id "+c.id+"."+c.sends+" got 200"),b(c),this.errors=0}else 0===d||d>=400&&d<600||d>=12e3?(a.error("request id "+c.id+"."+c.sends+" error "+d+" happened"),this._hitError(d),this._callProtocolErrorHandlers(c),d>=400&&d<500&&(this._conn._changeConnectStatus(a.Status.DISCONNECTING,null),this._conn._doDisconnect())):a.error("request id "+c.id+"."+c.sends+" error "+d+" happened");d>0&&d<500&&!(c.sends>5)||this._throttledRequestHandler()}},_processRequest:function(b){var c=this,d=this._requests[b],e=this._getRequestStatus(d,-1);if(d.sends>this._conn.maxRetries)return void this._conn._onDisconnectTimeout();var f=d.age(),g=!isNaN(f)&&f>Math.floor(a.TIMEOUT*this.wait),h=null!==d.dead&&d.timeDead()>Math.floor(a.SECONDARY_TIMEOUT*this.wait),i=4==d.xhr.readyState&&(e<1||e>=500);if((g||h||i)&&(h&&a.error("Request "+this._requests[b].id+" timed out (secondary), restarting"),d.abort=!0,d.xhr.abort(),d.xhr.onreadystatechange=function(){},this._requests[b]=new a.Request(d.xmlData,d.origFunc,d.rid,d.sends),d=this._requests[b]),0===d.xhr.readyState){a.debug("request id "+d.id+"."+d.sends+" posting");try{var j=this._conn.options.contentType||"text/xml; charset=utf-8";d.xhr.open("POST",this._conn.service,!this._conn.options.sync),"undefined"!=typeof d.xhr.setRequestHeader&&d.xhr.setRequestHeader("Content-Type",j),this._conn.options.withCredentials&&(d.xhr.withCredentials=!0)}catch(b){return a.error("XHR open failed."),this._conn.connected||this._conn._changeConnectStatus(a.Status.CONNFAIL,"bad-service"),void this._conn.disconnect()}var k=function(){if(d.date=new Date,c._conn.options.customHeaders){var a=c._conn.options.customHeaders;for(var b in a)a.hasOwnProperty(b)&&d.xhr.setRequestHeader(b,a[b])}d.xhr.send(d.data)};if(d.sends>1){var l=1e3*Math.min(Math.floor(a.TIMEOUT*this.wait),Math.pow(d.sends,3));setTimeout(function(){k()},l)}else k();d.sends++,this._conn.xmlOutput!==a.Connection.prototype.xmlOutput&&(d.xmlData.nodeName===this.strip&&d.xmlData.childNodes.length?this._conn.xmlOutput(d.xmlData.childNodes[0]):this._conn.xmlOutput(d.xmlData)),this._conn.rawOutput!==a.Connection.prototype.rawOutput&&this._conn.rawOutput(d.data)}else a.debug("_processRequest: "+(0===b?"first":"second")+" request has readyState of "+d.xhr.readyState)},_removeRequest:function(b){a.debug("removing request");var c;for(c=this._requests.length-1;c>=0;c--)b==this._requests[c]&&this._requests.splice(c,1);b.xhr.onreadystatechange=function(){},this._throttledRequestHandler()},_restartRequest:function(a){var b=this._requests[a];null===b.dead&&(b.dead=new Date),this._processRequest(a)},_reqToData:function(a){try{return a.getResponse()}catch(a){if("parsererror"!=a)throw a;this._conn.disconnect("strophe-parsererror")}},_sendTerminate:function(b){a.info("_sendTerminate was called");var c=this._buildBody().attrs({type:"terminate"});b&&c.cnode(b.tree());var d=new a.Request(c.tree(),this._onRequestStateChange.bind(this,this._conn._dataRecv.bind(this._conn)),c.tree().getAttribute("rid"));this._requests.push(d),this._throttledRequestHandler()},_send:function(){clearTimeout(this._conn._idleTimeout),this._throttledRequestHandler(),this._conn._idleTimeout=setTimeout(function(){this._onIdle()}.bind(this._conn),100)},_sendRestart:function(){this._throttledRequestHandler(),clearTimeout(this._conn._idleTimeout)},_throttledRequestHandler:function(){this._requests?a.debug("_throttledRequestHandler called with "+this._requests.length+" requests"):a.debug("_throttledRequestHandler called with undefined requests"),this._requests&&0!==this._requests.length&&(this._requests.length>0&&this._processRequest(0),this._requests.length>1&&Math.abs(this._requests[0].rid-this._requests[1].rid): "+d);var e=b.getAttribute("version");return"string"!=typeof e?c="Missing version in ":"1.0"!==e&&(c="Wrong version in : "+e),!c||(this._conn._changeConnectStatus(a.Status.CONNFAIL,c),this._conn._doDisconnect(),!1)},_connect_cb_wrapper:function(b){if(0===b.data.indexOf("\s*)*/,"");if(""===c)return;var d=(new DOMParser).parseFromString(c,"text/xml").documentElement;this._conn.xmlInput(d),this._conn.rawInput(b.data),this._handleStreamStart(d)&&this._connect_cb(d)}else if(0===b.data.indexOf(" tag.")}}this._conn._doDisconnect()},_doDisconnect:function(){a.info("WebSockets _doDisconnect was called"),this._closeSocket()},_streamWrap:function(a){return""+a+""},_closeSocket:function(){if(this.socket)try{this.socket.close()}catch(a){}this.socket=null},_emptyQueue:function(){return!0},_onClose:function(){this._conn.connected&&!this._conn.disconnecting?(a.error("Websocket closed unexpectedly"),this._conn._doDisconnect()):a.info("Websocket closed")},_no_auth_received:function(b){a.error("Server did not send any auth methods"),this._conn._changeConnectStatus(a.Status.CONNFAIL,"Server did not send any auth methods"),b&&(b=b.bind(this._conn))(),this._conn._doDisconnect()},_onDisconnectTimeout:function(){},_abortAllRequests:function(){},_onError:function(b){a.error("Websocket error "+b),this._conn._changeConnectStatus(a.Status.CONNFAIL,"The WebSocket connection could not be established or was disconnected."),this._disconnect()},_onIdle:function(){var b=this._conn._data;if(b.length>0&&!this._conn.paused){for(var c=0;c