UNPKG

strophe.js

Version:

Strophe.js is an XMPP library for JavaScript

1,299 lines (1,087 loc) 253 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.strophe = {})); })(this, (function (exports) { 'use strict'; var global$1 = typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}; /* * This module provides uniform * Shims APIs and globals that are not present in all JS environments, * the most common example for Strophe being browser APIs like WebSocket * and DOM that don't exist under nodejs. * * Usually these will be supplied in nodejs by conditionally requiring a * NPM module that provides a compatible implementation. */ /* global global */ /** * WHATWG WebSockets API * https://www.w3.org/TR/websockets/ * * Interface to use the web socket protocol * * Used implementations: * - supported browsers: built-in in WebSocket global * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Browser_compatibility * - nodejs: use standard-compliant 'ws' module * https://www.npmjs.com/package/ws */ function getWebSocketImplementation() { let WebSocketImplementation = global$1.WebSocket; if (typeof WebSocketImplementation === 'undefined') { try { WebSocketImplementation = require('ws'); } catch (err) { throw new Error('You must install the "ws" package to use Strophe in nodejs.'); } } return WebSocketImplementation; } const WebSocket = getWebSocketImplementation(); /** * DOMParser * https://w3c.github.io/DOM-Parsing/#the-domparser-interface * * Interface to parse XML strings into Document objects * * Used implementations: * - supported browsers: built-in in DOMParser global * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility * - nodejs: use '@xmldom/xmldom' module * https://www.npmjs.com/package/@xmldom/xmldom */ function getDOMParserImplementation() { let DOMParserImplementation = global$1.DOMParser; if (typeof DOMParserImplementation === 'undefined') { try { DOMParserImplementation = require('@xmldom/xmldom').DOMParser; } catch (err) { throw new Error('You must install the "@xmldom/xmldom" package to use Strophe in nodejs.'); } } return DOMParserImplementation; } const DOMParser = getDOMParserImplementation(); /** * Gets IE xml doc object. Used by getDummyXMLDocument shim. * * Returns: * A Microsoft XML DOM Object * See Also: * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx */ function _getIEXmlDom() { const docStrings = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]; for (let d = 0; d < docStrings.length; d++) { try { // eslint-disable-next-line no-undef const doc = new ActiveXObject(docStrings[d]); return doc; } catch (e) {// Try next one } } } /** * Creates a dummy XML DOM document to serve as an element and text node generator. * * Used implementations: * - IE < 10: avoid using createDocument() due to a memory leak, use ie-specific * workaround * - other supported browsers: use document's createDocument * - nodejs: use '@xmldom/xmldom' */ function getDummyXMLDOMDocument() { // nodejs if (typeof document === 'undefined') { try { const DOMImplementation = require('@xmldom/xmldom').DOMImplementation; return new DOMImplementation().createDocument('jabber:client', 'strophe', null); } catch (err) { throw new Error('You must install the "@xmldom/xmldom" package to use Strophe in nodejs.'); } } // IE < 10 if (document.implementation.createDocument === undefined || document.implementation.createDocument && document.documentMode && document.documentMode < 10) { const doc = _getIEXmlDom(); doc.appendChild(doc.createElement('strophe')); return doc; } // All other supported browsers return document.implementation.createDocument('jabber:client', 'strophe', null); } /* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorithm, as defined in RFC 1321. * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for more info. */ /* * Everything that isn't used by Strophe has been stripped here! */ /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ const safe_add$1 = function (x, y) { const lsw = (x & 0xFFFF) + (y & 0xFFFF); const msw = (x >> 16) + (y >> 16) + (lsw >> 16); return msw << 16 | lsw & 0xFFFF; }; /* * Bitwise rotate a 32-bit number to the left. */ const bit_rol = function (num, cnt) { return num << cnt | num >>> 32 - cnt; }; /* * Convert a string to an array of little-endian words */ const str2binl = function (str) { if (typeof str !== "string") { throw new Error("str2binl was passed a non-string"); } const bin = []; for (let i = 0; i < str.length * 8; i += 8) { bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32; } return bin; }; /* * Convert an array of little-endian words to a string */ const binl2str = function (bin) { let str = ""; for (let i = 0; i < bin.length * 32; i += 8) { str += String.fromCharCode(bin[i >> 5] >>> i % 32 & 255); } return str; }; /* * Convert an array of little-endian words to a hex string. */ const binl2hex = function (binarray) { const hex_tab = "0123456789abcdef"; let str = ""; for (let i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 + 4 & 0xF) + hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 & 0xF); } return str; }; /* * These functions implement the four basic operations the algorithm uses. */ const md5_cmn = function (q, a, b, x, s, t) { return safe_add$1(bit_rol(safe_add$1(safe_add$1(a, q), safe_add$1(x, t)), s), b); }; const md5_ff = function (a, b, c, d, x, s, t) { return md5_cmn(b & c | ~b & d, a, b, x, s, t); }; const md5_gg = function (a, b, c, d, x, s, t) { return md5_cmn(b & d | c & ~d, a, b, x, s, t); }; const md5_hh = function (a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); }; const md5_ii = function (a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | ~d), a, b, x, s, t); }; /* * Calculate the MD5 of an array of little-endian words, and a bit length */ const core_md5 = function (x, len) { /* append padding */ x[len >> 5] |= 0x80 << len % 32; x[(len + 64 >>> 9 << 4) + 14] = len; let a = 1732584193; let b = -271733879; let c = -1732584194; let d = 271733878; let olda, oldb, oldc, oldd; for (let i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936); d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302); a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222); c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844); d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); a = safe_add$1(a, olda); b = safe_add$1(b, oldb); c = safe_add$1(c, oldc); d = safe_add$1(d, oldd); } return [a, b, c, d]; }; /* * These are the functions you'll usually want to call. * They take string arguments and return either hex or base-64 encoded * strings. */ const MD5 = { hexdigest: function (s) { return binl2hex(core_md5(str2binl(s), s.length * 8)); }, hash: function (s) { return binl2str(core_md5(str2binl(s), s.length * 8)); } }; /** Class: Strophe.SASLMechanism * * Encapsulates an SASL authentication mechanism. * * User code may override the priority for each mechanism or disable it completely. * See <priority> for information about changing priority and <test> for informatian on * how to disable a mechanism. * * By default, all mechanisms are enabled and the priorities are * * SCRAM-SHA-1 - 60 * PLAIN - 50 * OAUTHBEARER - 40 * X-OAUTH2 - 30 * ANONYMOUS - 20 * EXTERNAL - 10 * * See: Strophe.Connection.addSupportedSASLMechanisms */ class SASLMechanism { /** * PrivateConstructor: Strophe.SASLMechanism * SASL auth mechanism abstraction. * * Parameters: * (String) name - SASL Mechanism name. * (Boolean) isClientFirst - If client should send response first without challenge. * (Number) priority - Priority. * * Returns: * A new Strophe.SASLMechanism object. */ constructor(name, isClientFirst, priority) { /** PrivateVariable: mechname * Mechanism name. */ this.mechname = name; /** PrivateVariable: isClientFirst * If client sends response without initial server challenge. */ this.isClientFirst = isClientFirst; /** Variable: priority * Determines which <SASLMechanism> is chosen for authentication (Higher is better). * Users may override this to prioritize mechanisms differently. * * Example: (This will cause Strophe to choose the mechanism that the server sent first) * * > Strophe.SASLPlain.priority = Strophe.SASLSHA1.priority; * * See <SASL mechanisms> for a list of available mechanisms. * */ this.priority = priority; } /** * Function: test * Checks if mechanism able to run. * To disable a mechanism, make this return false; * * To disable plain authentication run * > Strophe.SASLPlain.test = function() { * > return false; * > } * * See <SASL mechanisms> for a list of available mechanisms. * * Parameters: * (Strophe.Connection) connection - Target Connection. * * Returns: * (Boolean) If mechanism was able to run. */ test() { // eslint-disable-line class-methods-use-this return true; } /** PrivateFunction: onStart * Called before starting mechanism on some connection. * * Parameters: * (Strophe.Connection) connection - Target Connection. */ onStart(connection) { this._connection = connection; } /** PrivateFunction: onChallenge * Called by protocol implementation on incoming challenge. * * By deafult, if the client is expected to send data first (isClientFirst === true), * this method is called with `challenge` as null on the first call, * unless `clientChallenge` is overridden in the relevant subclass. * * Parameters: * (Strophe.Connection) connection - Target Connection. * (String) challenge - current challenge to handle. * * Returns: * (String) Mechanism response. */ onChallenge(connection, challenge) { // eslint-disable-line throw new Error("You should implement challenge handling!"); } /** PrivateFunction: clientChallenge * Called by the protocol implementation if the client is expected to send * data first in the authentication exchange (i.e. isClientFirst === true). * * Parameters: * (Strophe.Connection) connection - Target Connection. * * Returns: * (String) Mechanism response. */ clientChallenge(connection) { if (!this.isClientFirst) { throw new Error("clientChallenge should not be called if isClientFirst is false!"); } return this.onChallenge(connection); } /** PrivateFunction: onFailure * Protocol informs mechanism implementation about SASL failure. */ onFailure() { this._connection = null; } /** PrivateFunction: onSuccess * Protocol informs mechanism implementation about SASL success. */ onSuccess() { this._connection = null; } } class SASLAnonymous extends SASLMechanism { /** PrivateConstructor: SASLAnonymous * SASL ANONYMOUS authentication. */ constructor(mechname = 'ANONYMOUS', isClientFirst = false, priority = 20) { super(mechname, isClientFirst, priority); } test(connection) { // eslint-disable-line class-methods-use-this return connection.authcid === null; } } class SASLExternal extends SASLMechanism { /** PrivateConstructor: SASLExternal * SASL EXTERNAL authentication. * * The EXTERNAL mechanism allows a client to request the server to use * credentials established by means external to the mechanism to * authenticate the client. The external means may be, for instance, * TLS services. */ constructor(mechname = 'EXTERNAL', isClientFirst = true, priority = 10) { super(mechname, isClientFirst, priority); } onChallenge(connection) { // eslint-disable-line class-methods-use-this /** According to XEP-178, an authzid SHOULD NOT be presented when the * authcid contained or implied in the client certificate is the JID (i.e. * authzid) with which the user wants to log in as. * * To NOT send the authzid, the user should therefore set the authcid equal * to the JID when instantiating a new Strophe.Connection object. */ return connection.authcid === connection.authzid ? '' : connection.authzid; } } const utils = { utf16to8: function (str) { var i, c; var out = ""; var len = str.length; for (i = 0; i < len; i++) { c = str.charCodeAt(i); if (c >= 0x0000 && c <= 0x007F) { out += str.charAt(i); } else if (c > 0x07FF) { out += String.fromCharCode(0xE0 | c >> 12 & 0x0F); out += String.fromCharCode(0x80 | c >> 6 & 0x3F); out += String.fromCharCode(0x80 | c >> 0 & 0x3F); } else { out += String.fromCharCode(0xC0 | c >> 6 & 0x1F); out += String.fromCharCode(0x80 | c >> 0 & 0x3F); } } return out; }, addCookies: function (cookies) { /* Parameters: * (Object) cookies - either a map of cookie names * to string values or to maps of cookie values. * * For example: * { "myCookie": "1234" } * * or: * { "myCookie": { * "value": "1234", * "domain": ".example.org", * "path": "/", * "expires": expirationDate * } * } * * These values get passed to Strophe.Connection via * options.cookies */ cookies = cookies || {}; for (const cookieName in cookies) { if (Object.prototype.hasOwnProperty.call(cookies, cookieName)) { let expires = ''; let domain = ''; let path = ''; const cookieObj = cookies[cookieName]; const isObj = typeof cookieObj === "object"; const cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj)); if (isObj) { expires = cookieObj.expires ? ";expires=" + cookieObj.expires : ''; domain = cookieObj.domain ? ";domain=" + cookieObj.domain : ''; path = cookieObj.path ? ";path=" + cookieObj.path : ''; } document.cookie = cookieName + '=' + cookieValue + expires + domain + path; } } } }; class SASLOAuthBearer extends SASLMechanism { /** PrivateConstructor: SASLOAuthBearer * SASL OAuth Bearer authentication. */ constructor(mechname = 'OAUTHBEARER', isClientFirst = true, priority = 40) { super(mechname, isClientFirst, priority); } test(connection) { // eslint-disable-line class-methods-use-this return connection.pass !== null; } onChallenge(connection) { // eslint-disable-line class-methods-use-this let auth_str = 'n,'; if (connection.authcid !== null) { auth_str = auth_str + 'a=' + connection.authzid; } auth_str = auth_str + ','; auth_str = auth_str + "\u0001"; auth_str = auth_str + 'auth=Bearer '; auth_str = auth_str + connection.pass; auth_str = auth_str + "\u0001"; auth_str = auth_str + "\u0001"; return utils.utf16to8(auth_str); } } class SASLPlain extends SASLMechanism { /** PrivateConstructor: SASLPlain * SASL PLAIN authentication. */ constructor(mechname = 'PLAIN', isClientFirst = true, priority = 50) { super(mechname, isClientFirst, priority); } test(connection) { // eslint-disable-line class-methods-use-this return connection.authcid !== null; } onChallenge(connection) { // eslint-disable-line class-methods-use-this const { authcid, authzid, domain, pass } = connection; if (!domain) { throw new Error("SASLPlain onChallenge: domain is not defined!"); } // Only include authzid if it differs from authcid. // See: https://tools.ietf.org/html/rfc6120#section-6.3.8 let auth_str = authzid !== `${authcid}@${domain}` ? authzid : ''; auth_str = auth_str + "\u0000"; auth_str = auth_str + authcid; auth_str = auth_str + "\u0000"; auth_str = auth_str + pass; return utils.utf16to8(auth_str); } } /* * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined * in FIPS PUB 180-1 * Version 2.1a Copyright Paul Johnston 2000 - 2002. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for details. */ /* global define */ /* Some functions and variables have been stripped for use with Strophe */ /* * Calculate the SHA-1 of an array of big-endian words, and a bit length */ function core_sha1(x, len) { /* append padding */ x[len >> 5] |= 0x80 << 24 - len % 32; x[(len + 64 >> 9 << 4) + 15] = len; var w = new Array(80); var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; var e = -1009589776; var i, j, t, olda, oldb, oldc, oldd, olde; for (i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; olde = e; for (j = 0; j < 80; j++) { if (j < 16) { w[j] = x[i + j]; } else { w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); } t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); e = d; d = c; c = rol(b, 30); b = a; a = t; } a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); e = safe_add(e, olde); } return [a, b, c, d, e]; } /* * Perform the appropriate triplet combination function for the current * iteration */ function sha1_ft(t, b, c, d) { if (t < 20) { return b & c | ~b & d; } if (t < 40) { return b ^ c ^ d; } if (t < 60) { return b & c | b & d | c & d; } return b ^ c ^ d; } /* * Determine the appropriate additive constant for the current iteration */ function sha1_kt(t) { return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514; } /* * Calculate the HMAC-SHA1 of a key and some data */ function core_hmac_sha1(key, data) { var bkey = str2binb(key); if (bkey.length > 16) { bkey = core_sha1(bkey, key.length * 8); } var ipad = new Array(16), opad = new Array(16); for (var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8); return core_sha1(opad.concat(hash), 512 + 160); } /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ function safe_add(x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return msw << 16 | lsw & 0xFFFF; } /* * Bitwise rotate a 32-bit number to the left. */ function rol(num, cnt) { return num << cnt | num >>> 32 - cnt; } /* * Convert an 8-bit or 16-bit string to an array of big-endian words * In 8-bit function, characters >255 have their hi-byte silently ignored. */ function str2binb(str) { var bin = []; var mask = 255; for (var i = 0; i < str.length * 8; i += 8) { bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << 24 - i % 32; } return bin; } /* * Convert an array of big-endian words to a base-64 string */ function binb2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; var triplet, j; for (var i = 0; i < binarray.length * 4; i += 3) { triplet = (binarray[i >> 2] >> 8 * (3 - i % 4) & 0xFF) << 16 | (binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4) & 0xFF) << 8 | binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4) & 0xFF; for (j = 0; j < 4; j++) { if (i * 8 + j * 6 > binarray.length * 32) { str += "="; } else { str += tab.charAt(triplet >> 6 * (3 - j) & 0x3F); } } } return str; } /* * Convert an array of big-endian words to a string */ function binb2str(bin) { var str = ""; var mask = 255; for (var i = 0; i < bin.length * 32; i += 8) { str += String.fromCharCode(bin[i >> 5] >>> 24 - i % 32 & mask); } return str; } /* * These are the functions you'll usually want to call * They take string arguments and return either hex or base-64 encoded strings */ const SHA1 = { b64_hmac_sha1: function (key, data) { return binb2b64(core_hmac_sha1(key, data)); }, b64_sha1: function (s) { return binb2b64(core_sha1(str2binb(s), s.length * 8)); }, binb2str: binb2str, core_hmac_sha1: core_hmac_sha1, str_hmac_sha1: function (key, data) { return binb2str(core_hmac_sha1(key, data)); }, str_sha1: function (s) { return binb2str(core_sha1(str2binb(s), s.length * 8)); } }; class SASLSHA1 extends SASLMechanism { /** PrivateConstructor: SASLSHA1 * SASL SCRAM SHA 1 authentication. */ constructor(mechname = 'SCRAM-SHA-1', isClientFirst = true, priority = 60) { super(mechname, isClientFirst, priority); } test(connection) { // eslint-disable-line class-methods-use-this return connection.authcid !== null; } onChallenge(connection, challenge) { // eslint-disable-line class-methods-use-this let nonce, salt, iter, Hi, U, U_old, i, k; let responseText = "c=biws,"; let authMessage = `${connection._sasl_data["client-first-message-bare"]},${challenge},`; const cnonce = connection._sasl_data.cnonce; const attribMatch = /([a-z]+)=([^,]+)(,|$)/; while (challenge.match(attribMatch)) { const matches = challenge.match(attribMatch); challenge = challenge.replace(matches[0], ""); switch (matches[1]) { case "r": nonce = matches[2]; break; case "s": salt = matches[2]; break; case "i": iter = matches[2]; break; } } if (nonce.substr(0, cnonce.length) !== cnonce) { connection._sasl_data = {}; return connection._sasl_failure_cb(); } responseText += "r=" + nonce; authMessage += responseText; salt = atob(salt); salt += "\x00\x00\x00\x01"; const pass = utils.utf16to8(connection.pass); Hi = U_old = SHA1.core_hmac_sha1(pass, salt); for (i = 1; i < iter; i++) { U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old)); for (k = 0; k < 5; k++) { Hi[k] ^= U[k]; } U_old = U; } Hi = SHA1.binb2str(Hi); const clientKey = SHA1.core_hmac_sha1(Hi, "Client Key"); const serverKey = SHA1.str_hmac_sha1(Hi, "Server Key"); const clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage); connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage); for (k = 0; k < 5; k++) { clientKey[k] ^= clientSignature[k]; } responseText += ",p=" + btoa(SHA1.binb2str(clientKey)); return responseText; } clientChallenge(connection, test_cnonce) { // eslint-disable-line class-methods-use-this const cnonce = test_cnonce || MD5.hexdigest("" + Math.random() * 1234567890); let 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; auth_str = "n,," + auth_str; return auth_str; } } class SASLXOAuth2 extends SASLMechanism { /** PrivateConstructor: SASLXOAuth2 * SASL X-OAuth2 authentication. */ constructor(mechname = 'X-OAUTH2', isClientFirst = true, priority = 30) { super(mechname, isClientFirst, priority); } test(connection) { // eslint-disable-line class-methods-use-this return connection.pass !== null; } onChallenge(connection) { // eslint-disable-line class-methods-use-this let auth_str = '\u0000'; if (connection.authcid !== null) { auth_str = auth_str + connection.authzid; } auth_str = auth_str + "\u0000"; auth_str = auth_str + connection.pass; return utils.utf16to8(auth_str); } } /** * Implementation of atob() according to the HTML and Infra specs, except that * instead of throwing INVALID_CHARACTER_ERR we return null. */ function atob$1(data) { // Web IDL requires DOMStrings to just be converted using ECMAScript // ToString, which in our case amounts to using a template literal. data = `${data}`; // "Remove all ASCII whitespace from data." data = data.replace(/[ \t\n\f\r]/g, ""); // "If data's length divides by 4 leaving no remainder, then: if data ends // with one or two U+003D (=) code points, then remove them from data." if (data.length % 4 === 0) { data = data.replace(/==?$/, ""); } // "If data's length divides by 4 leaving a remainder of 1, then return // failure." // // "If data contains a code point that is not one of // // U+002B (+) // U+002F (/) // ASCII alphanumeric // // then return failure." if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) { return null; } // "Let output be an empty byte sequence." let output = ""; // "Let buffer be an empty buffer that can have bits appended to it." // // We append bits via left-shift and or. accumulatedBits is used to track // when we've gotten to 24 bits. let buffer = 0; let accumulatedBits = 0; // "Let position be a position variable for data, initially pointing at the // start of data." // // "While position does not point past the end of data:" for (let i = 0; i < data.length; i++) { // "Find the code point pointed to by position in the second column of // Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in // the first cell of the same row. // // "Append to buffer the six bits corresponding to n, most significant bit // first." // // atobLookup() implements the table from RFC 4648. buffer <<= 6; buffer |= atobLookup(data[i]); accumulatedBits += 6; // "If buffer has accumulated 24 bits, interpret them as three 8-bit // big-endian numbers. Append three bytes with values equal to those // numbers to output, in the same order, and then empty buffer." if (accumulatedBits === 24) { output += String.fromCharCode((buffer & 0xff0000) >> 16); output += String.fromCharCode((buffer & 0xff00) >> 8); output += String.fromCharCode(buffer & 0xff); buffer = accumulatedBits = 0; } // "Advance position by 1." } // "If buffer is not empty, it contains either 12 or 18 bits. If it contains // 12 bits, then discard the last four and interpret the remaining eight as // an 8-bit big-endian number. If it contains 18 bits, then discard the last // two and interpret the remaining 16 as two 8-bit big-endian numbers. Append // the one or two bytes with values equal to those one or two numbers to // output, in the same order." if (accumulatedBits === 12) { buffer >>= 4; output += String.fromCharCode(buffer); } else if (accumulatedBits === 18) { buffer >>= 2; output += String.fromCharCode((buffer & 0xff00) >> 8); output += String.fromCharCode(buffer & 0xff); } // "Return output." return output; } /** * A lookup table for atob(), which converts an ASCII character to the * corresponding six-bit number. */ function atobLookup(chr) { if (/[A-Z]/.test(chr)) { return chr.charCodeAt(0) - "A".charCodeAt(0); } if (/[a-z]/.test(chr)) { return chr.charCodeAt(0) - "a".charCodeAt(0) + 26; } if (/[0-9]/.test(chr)) { return chr.charCodeAt(0) - "0".charCodeAt(0) + 52; } if (chr === "+") { return 62; } if (chr === "/") { return 63; } // Throw exception; should not be hit in tests return undefined; } var atob_1 = atob$1; /** * btoa() as defined by the HTML and Infra specs, which mostly just references * RFC 4648. */ function btoa$1(s) { let i; // String conversion as required by Web IDL. s = `${s}`; // "The btoa() method must throw an "InvalidCharacterError" DOMException if // data contains any character whose code point is greater than U+00FF." for (i = 0; i < s.length; i++) { if (s.charCodeAt(i) > 255) { return null; } } let out = ""; for (i = 0; i < s.length; i += 3) { const groupsOfSix = [undefined, undefined, undefined, undefined]; groupsOfSix[0] = s.charCodeAt(i) >> 2; groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; if (s.length > i + 1) { groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; } if (s.length > i + 2) { groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; } for (let j = 0; j < groupsOfSix.length; j++) { if (typeof groupsOfSix[j] === "undefined") { out += "="; } else { out += btoaLookup(groupsOfSix[j]); } } } return out; } /** * Lookup table for btoa(), which converts a six-bit number into the * corresponding ASCII character. */ function btoaLookup(idx) { if (idx < 26) { return String.fromCharCode(idx + "A".charCodeAt(0)); } if (idx < 52) { return String.fromCharCode(idx - 26 + "a".charCodeAt(0)); } if (idx < 62) { return String.fromCharCode(idx - 52 + "0".charCodeAt(0)); } if (idx === 62) { return "+"; } if (idx === 63) { return "/"; } // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. return undefined; } var btoa_1 = btoa$1; var abab = { atob: atob_1, btoa: btoa_1 }; /* This program is distributed under the terms of the MIT license. Please see the LICENSE file for details. Copyright 2006-2018, OGG, LLC */ /** Function: $build * Create a Strophe.Builder. * This is an alias for 'new Strophe.Builder(name, attrs)'. * * Parameters: * (String) name - The root element name. * (Object) attrs - The attributes for the root element in object notation. * * Returns: * A new Strophe.Builder object. */ function $build(name, attrs) { return new Strophe.Builder(name, attrs); } /** Function: $msg * Create a Strophe.Builder with a <message/> element as the root. * * Parameters: * (Object) attrs - The <message/> element attributes in object notation. * * Returns: * A new Strophe.Builder object. */ function $msg(attrs) { return new Strophe.Builder("message", attrs); } /** Function: $iq * Create a Strophe.Builder with an <iq/> element as the root. * * Parameters: * (Object) attrs - The <iq/> element attributes in object notation. * * Returns: * A new Strophe.Builder object. */ function $iq(attrs) { return new Strophe.Builder("iq", attrs); } /** Function: $pres * Create a Strophe.Builder with a <presence/> element as the root. * * Parameters: * (Object) attrs - The <presence/> element attributes in object notation. * * Returns: * A new Strophe.Builder object. */ function $pres(attrs) { return new Strophe.Builder("presence", attrs); } /** Class: Strophe * An object container for all Strophe library functions. * * This class is just a container for all the objects and constants * used in the library. It is not meant to be instantiated, but to * provide a namespace for library objects, constants, and functions. */ const Strophe = { /** Constant: VERSION */ VERSION: "1.4.4", /** Constants: XMPP Namespace Constants * Common namespace constants from the XMPP RFCs and XEPs. * * NS.HTTPBIND - HTTP BIND namespace from XEP 124. * NS.BOSH - BOSH namespace from XEP 206. * NS.CLIENT - Main XMPP client namespace. * NS.AUTH - Legacy authentication namespace. * NS.ROSTER - Roster operations namespace. * NS.PROFILE - Profile namespace. * NS.DISCO_INFO - Service discovery info namespace from XEP 30. * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30. * NS.MUC - Multi-User Chat namespace from XEP 45. * NS.SASL - XMPP SASL namespace from RFC 3920. * NS.STREAM - XMPP Streams namespace from RFC 3920. * NS.BIND - XMPP Binding namespace from RFC 3920 and RFC 6120. * NS.SESSION - XMPP Session namespace from RFC 3920. * NS.XHTML_IM - XHTML-IM namespace from XEP 71. * NS.XHTML - XHTML body namespace from XEP 71. */ 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"