strophe.js
Version:
Strophe.js is an XMPP library for JavaScript
1,299 lines (1,087 loc) • 253 kB
JavaScript
(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"