From 953ddd82e48dd206cef5ac94456549aed13b3ad5 Mon Sep 17 00:00:00 2001 From: RaindropsSys Date: Fri, 17 Nov 2023 23:25:29 +0100 Subject: Updated 30 files and deleted 2976 files (automated) --- .../matrix-js-sdk/dist/browser-matrix.js | 101316 ------------------ 1 file changed, 101316 deletions(-) delete mode 100644 includes/external/matrix/node_modules/matrix-js-sdk/dist/browser-matrix.js (limited to 'includes/external/matrix/node_modules/matrix-js-sdk/dist/browser-matrix.js') diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/dist/browser-matrix.js b/includes/external/matrix/node_modules/matrix-js-sdk/dist/browser-matrix.js deleted file mode 100644 index 01029d7..0000000 --- a/includes/external/matrix/node_modules/matrix-js-sdk/dist/browser-matrix.js +++ /dev/null @@ -1,101316 +0,0 @@ -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i> 6]; - const primitive = (tag & 0x20) === 0; - - // Multi-octet tag - load - if ((tag & 0x1f) === 0x1f) { - let oct = tag; - tag = 0; - while ((oct & 0x80) === 0x80) { - oct = buf.readUInt8(fail); - if (buf.isError(oct)) - return oct; - - tag <<= 7; - tag |= oct & 0x7f; - } - } else { - tag &= 0x1f; - } - const tagStr = der.tag[tag]; - - return { - cls: cls, - primitive: primitive, - tag: tag, - tagStr: tagStr - }; -} - -function derDecodeLen(buf, primitive, fail) { - let len = buf.readUInt8(fail); - if (buf.isError(len)) - return len; - - // Indefinite form - if (!primitive && len === 0x80) - return null; - - // Definite form - if ((len & 0x80) === 0) { - // Short form - return len; - } - - // Long form - const num = len & 0x7f; - if (num > 4) - return buf.error('length octect is too long'); - - len = 0; - for (let i = 0; i < num; i++) { - len <<= 8; - const j = buf.readUInt8(fail); - if (buf.isError(j)) - return j; - len |= j; - } - - return len; -} - -},{"../base/buffer":4,"../base/node":6,"../constants/der":8,"bn.js":18,"inherits":146}],11:[function(require,module,exports){ -'use strict'; - -const decoders = exports; - -decoders.der = require('./der'); -decoders.pem = require('./pem'); - -},{"./der":10,"./pem":12}],12:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); -const Buffer = require('safer-buffer').Buffer; - -const DERDecoder = require('./der'); - -function PEMDecoder(entity) { - DERDecoder.call(this, entity); - this.enc = 'pem'; -} -inherits(PEMDecoder, DERDecoder); -module.exports = PEMDecoder; - -PEMDecoder.prototype.decode = function decode(data, options) { - const lines = data.toString().split(/[\r\n]+/g); - - const label = options.label.toUpperCase(); - - const re = /^-----(BEGIN|END) ([^-]+)-----$/; - let start = -1; - let end = -1; - for (let i = 0; i < lines.length; i++) { - const match = lines[i].match(re); - if (match === null) - continue; - - if (match[2] !== label) - continue; - - if (start === -1) { - if (match[1] !== 'BEGIN') - break; - start = i; - } else { - if (match[1] !== 'END') - break; - end = i; - break; - } - } - if (start === -1 || end === -1) - throw new Error('PEM section not found for: ' + label); - - const base64 = lines.slice(start + 1, end).join(''); - // Remove excessive symbols - base64.replace(/[^a-z0-9+/=]+/gi, ''); - - const input = Buffer.from(base64, 'base64'); - return DERDecoder.prototype.decode.call(this, input, options); -}; - -},{"./der":10,"inherits":146,"safer-buffer":251}],13:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); -const Buffer = require('safer-buffer').Buffer; -const Node = require('../base/node'); - -// Import DER constants -const der = require('../constants/der'); - -function DEREncoder(entity) { - this.enc = 'der'; - this.name = entity.name; - this.entity = entity; - - // Construct base tree - this.tree = new DERNode(); - this.tree._init(entity.body); -} -module.exports = DEREncoder; - -DEREncoder.prototype.encode = function encode(data, reporter) { - return this.tree._encode(data, reporter).join(); -}; - -// Tree methods - -function DERNode(parent) { - Node.call(this, 'der', parent); -} -inherits(DERNode, Node); - -DERNode.prototype._encodeComposite = function encodeComposite(tag, - primitive, - cls, - content) { - const encodedTag = encodeTag(tag, primitive, cls, this.reporter); - - // Short form - if (content.length < 0x80) { - const header = Buffer.alloc(2); - header[0] = encodedTag; - header[1] = content.length; - return this._createEncoderBuffer([ header, content ]); - } - - // Long form - // Count octets required to store length - let lenOctets = 1; - for (let i = content.length; i >= 0x100; i >>= 8) - lenOctets++; - - const header = Buffer.alloc(1 + 1 + lenOctets); - header[0] = encodedTag; - header[1] = 0x80 | lenOctets; - - for (let i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8) - header[i] = j & 0xff; - - return this._createEncoderBuffer([ header, content ]); -}; - -DERNode.prototype._encodeStr = function encodeStr(str, tag) { - if (tag === 'bitstr') { - return this._createEncoderBuffer([ str.unused | 0, str.data ]); - } else if (tag === 'bmpstr') { - const buf = Buffer.alloc(str.length * 2); - for (let i = 0; i < str.length; i++) { - buf.writeUInt16BE(str.charCodeAt(i), i * 2); - } - return this._createEncoderBuffer(buf); - } else if (tag === 'numstr') { - if (!this._isNumstr(str)) { - return this.reporter.error('Encoding of string type: numstr supports ' + - 'only digits and space'); - } - return this._createEncoderBuffer(str); - } else if (tag === 'printstr') { - if (!this._isPrintstr(str)) { - return this.reporter.error('Encoding of string type: printstr supports ' + - 'only latin upper and lower case letters, ' + - 'digits, space, apostrophe, left and rigth ' + - 'parenthesis, plus sign, comma, hyphen, ' + - 'dot, slash, colon, equal sign, ' + - 'question mark'); - } - return this._createEncoderBuffer(str); - } else if (/str$/.test(tag)) { - return this._createEncoderBuffer(str); - } else if (tag === 'objDesc') { - return this._createEncoderBuffer(str); - } else { - return this.reporter.error('Encoding of string type: ' + tag + - ' unsupported'); - } -}; - -DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) { - if (typeof id === 'string') { - if (!values) - return this.reporter.error('string objid given, but no values map found'); - if (!values.hasOwnProperty(id)) - return this.reporter.error('objid not found in values map'); - id = values[id].split(/[\s.]+/g); - for (let i = 0; i < id.length; i++) - id[i] |= 0; - } else if (Array.isArray(id)) { - id = id.slice(); - for (let i = 0; i < id.length; i++) - id[i] |= 0; - } - - if (!Array.isArray(id)) { - return this.reporter.error('objid() should be either array or string, ' + - 'got: ' + JSON.stringify(id)); - } - - if (!relative) { - if (id[1] >= 40) - return this.reporter.error('Second objid identifier OOB'); - id.splice(0, 2, id[0] * 40 + id[1]); - } - - // Count number of octets - let size = 0; - for (let i = 0; i < id.length; i++) { - let ident = id[i]; - for (size++; ident >= 0x80; ident >>= 7) - size++; - } - - const objid = Buffer.alloc(size); - let offset = objid.length - 1; - for (let i = id.length - 1; i >= 0; i--) { - let ident = id[i]; - objid[offset--] = ident & 0x7f; - while ((ident >>= 7) > 0) - objid[offset--] = 0x80 | (ident & 0x7f); - } - - return this._createEncoderBuffer(objid); -}; - -function two(num) { - if (num < 10) - return '0' + num; - else - return num; -} - -DERNode.prototype._encodeTime = function encodeTime(time, tag) { - let str; - const date = new Date(time); - - if (tag === 'gentime') { - str = [ - two(date.getUTCFullYear()), - two(date.getUTCMonth() + 1), - two(date.getUTCDate()), - two(date.getUTCHours()), - two(date.getUTCMinutes()), - two(date.getUTCSeconds()), - 'Z' - ].join(''); - } else if (tag === 'utctime') { - str = [ - two(date.getUTCFullYear() % 100), - two(date.getUTCMonth() + 1), - two(date.getUTCDate()), - two(date.getUTCHours()), - two(date.getUTCMinutes()), - two(date.getUTCSeconds()), - 'Z' - ].join(''); - } else { - this.reporter.error('Encoding ' + tag + ' time is not supported yet'); - } - - return this._encodeStr(str, 'octstr'); -}; - -DERNode.prototype._encodeNull = function encodeNull() { - return this._createEncoderBuffer(''); -}; - -DERNode.prototype._encodeInt = function encodeInt(num, values) { - if (typeof num === 'string') { - if (!values) - return this.reporter.error('String int or enum given, but no values map'); - if (!values.hasOwnProperty(num)) { - return this.reporter.error('Values map doesn\'t contain: ' + - JSON.stringify(num)); - } - num = values[num]; - } - - // Bignum, assume big endian - if (typeof num !== 'number' && !Buffer.isBuffer(num)) { - const numArray = num.toArray(); - if (!num.sign && numArray[0] & 0x80) { - numArray.unshift(0); - } - num = Buffer.from(numArray); - } - - if (Buffer.isBuffer(num)) { - let size = num.length; - if (num.length === 0) - size++; - - const out = Buffer.alloc(size); - num.copy(out); - if (num.length === 0) - out[0] = 0; - return this._createEncoderBuffer(out); - } - - if (num < 0x80) - return this._createEncoderBuffer(num); - - if (num < 0x100) - return this._createEncoderBuffer([0, num]); - - let size = 1; - for (let i = num; i >= 0x100; i >>= 8) - size++; - - const out = new Array(size); - for (let i = out.length - 1; i >= 0; i--) { - out[i] = num & 0xff; - num >>= 8; - } - if(out[0] & 0x80) { - out.unshift(0); - } - - return this._createEncoderBuffer(Buffer.from(out)); -}; - -DERNode.prototype._encodeBool = function encodeBool(value) { - return this._createEncoderBuffer(value ? 0xff : 0); -}; - -DERNode.prototype._use = function use(entity, obj) { - if (typeof entity === 'function') - entity = entity(obj); - return entity._getEncoder('der').tree; -}; - -DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) { - const state = this._baseState; - let i; - if (state['default'] === null) - return false; - - const data = dataBuffer.join(); - if (state.defaultBuffer === undefined) - state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join(); - - if (data.length !== state.defaultBuffer.length) - return false; - - for (i=0; i < data.length; i++) - if (data[i] !== state.defaultBuffer[i]) - return false; - - return true; -}; - -// Utility methods - -function encodeTag(tag, primitive, cls, reporter) { - let res; - - if (tag === 'seqof') - tag = 'seq'; - else if (tag === 'setof') - tag = 'set'; - - if (der.tagByName.hasOwnProperty(tag)) - res = der.tagByName[tag]; - else if (typeof tag === 'number' && (tag | 0) === tag) - res = tag; - else - return reporter.error('Unknown tag: ' + tag); - - if (res >= 0x1f) - return reporter.error('Multi-octet tag encoding unsupported'); - - if (!primitive) - res |= 0x20; - - res |= (der.tagClassByName[cls || 'universal'] << 6); - - return res; -} - -},{"../base/node":6,"../constants/der":8,"inherits":146,"safer-buffer":251}],14:[function(require,module,exports){ -'use strict'; - -const encoders = exports; - -encoders.der = require('./der'); -encoders.pem = require('./pem'); - -},{"./der":13,"./pem":15}],15:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); - -const DEREncoder = require('./der'); - -function PEMEncoder(entity) { - DEREncoder.call(this, entity); - this.enc = 'pem'; -} -inherits(PEMEncoder, DEREncoder); -module.exports = PEMEncoder; - -PEMEncoder.prototype.encode = function encode(data, options) { - const buf = DEREncoder.prototype.encode.call(this, data); - - const p = buf.toString('base64'); - const out = [ '-----BEGIN ' + options.label + '-----' ]; - for (let i = 0; i < p.length; i += 64) - out.push(p.slice(i, i + 64)); - out.push('-----END ' + options.label + '-----'); - return out.join('\n'); -}; - -},{"./der":13,"inherits":146}],16:[function(require,module,exports){ -(function (global){(function (){ -'use strict'; - -var possibleNames = [ - 'BigInt64Array', - 'BigUint64Array', - 'Float32Array', - 'Float64Array', - 'Int16Array', - 'Int32Array', - 'Int8Array', - 'Uint16Array', - 'Uint32Array', - 'Uint8Array', - 'Uint8ClampedArray' -]; - -var g = typeof globalThis === 'undefined' ? global : globalThis; - -module.exports = function availableTypedArrays() { - var out = []; - for (var i = 0; i < possibleNames.length; i++) { - if (typeof g[possibleNames[i]] === 'function') { - out[out.length] = possibleNames[i]; - } - } - return out; -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{}],17:[function(require,module,exports){ -'use strict' - -exports.byteLength = byteLength -exports.toByteArray = toByteArray -exports.fromByteArray = fromByteArray - -var lookup = [] -var revLookup = [] -var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array - -var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -for (var i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i] - revLookup[code.charCodeAt(i)] = i -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup['-'.charCodeAt(0)] = 62 -revLookup['_'.charCodeAt(0)] = 63 - -function getLens (b64) { - var len = b64.length - - if (len % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - var validLen = b64.indexOf('=') - if (validLen === -1) validLen = len - - var placeHoldersLen = validLen === len - ? 0 - : 4 - (validLen % 4) - - return [validLen, placeHoldersLen] -} - -// base64 is 4/3 + up to two characters of the original data -function byteLength (b64) { - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function _byteLength (b64, validLen, placeHoldersLen) { - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function toByteArray (b64) { - var tmp - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - - var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) - - var curByte = 0 - - // if there are placeholders, only get up to the last complete 4 chars - var len = placeHoldersLen > 0 - ? validLen - 4 - : validLen - - var i - for (i = 0; i < len; i += 4) { - tmp = - (revLookup[b64.charCodeAt(i)] << 18) | - (revLookup[b64.charCodeAt(i + 1)] << 12) | - (revLookup[b64.charCodeAt(i + 2)] << 6) | - revLookup[b64.charCodeAt(i + 3)] - arr[curByte++] = (tmp >> 16) & 0xFF - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 2) { - tmp = - (revLookup[b64.charCodeAt(i)] << 2) | - (revLookup[b64.charCodeAt(i + 1)] >> 4) - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 1) { - tmp = - (revLookup[b64.charCodeAt(i)] << 10) | - (revLookup[b64.charCodeAt(i + 1)] << 4) | - (revLookup[b64.charCodeAt(i + 2)] >> 2) - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - return arr -} - -function tripletToBase64 (num) { - return lookup[num >> 18 & 0x3F] + - lookup[num >> 12 & 0x3F] + - lookup[num >> 6 & 0x3F] + - lookup[num & 0x3F] -} - -function encodeChunk (uint8, start, end) { - var tmp - var output = [] - for (var i = start; i < end; i += 3) { - tmp = - ((uint8[i] << 16) & 0xFF0000) + - ((uint8[i + 1] << 8) & 0xFF00) + - (uint8[i + 2] & 0xFF) - output.push(tripletToBase64(tmp)) - } - return output.join('') -} - -function fromByteArray (uint8) { - var tmp - var len = uint8.length - var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes - var parts = [] - var maxChunkLength = 16383 // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1] - parts.push( - lookup[tmp >> 2] + - lookup[(tmp << 4) & 0x3F] + - '==' - ) - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1] - parts.push( - lookup[tmp >> 10] + - lookup[(tmp >> 4) & 0x3F] + - lookup[(tmp << 2) & 0x3F] + - '=' - ) - } - - return parts.join('') -} - -},{}],18:[function(require,module,exports){ -(function (module, exports) { - 'use strict'; - - // Utils - function assert (val, msg) { - if (!val) throw new Error(msg || 'Assertion failed'); - } - - // Could use `inherits` module, but don't want to move from single file - // architecture yet. - function inherits (ctor, superCtor) { - ctor.super_ = superCtor; - var TempCtor = function () {}; - TempCtor.prototype = superCtor.prototype; - ctor.prototype = new TempCtor(); - ctor.prototype.constructor = ctor; - } - - // BN - - function BN (number, base, endian) { - if (BN.isBN(number)) { - return number; - } - - this.negative = 0; - this.words = null; - this.length = 0; - - // Reduction context - this.red = null; - - if (number !== null) { - if (base === 'le' || base === 'be') { - endian = base; - base = 10; - } - - this._init(number || 0, base || 10, endian || 'be'); - } - } - if (typeof module === 'object') { - module.exports = BN; - } else { - exports.BN = BN; - } - - BN.BN = BN; - BN.wordSize = 26; - - var Buffer; - try { - if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { - Buffer = window.Buffer; - } else { - Buffer = require('buffer').Buffer; - } - } catch (e) { - } - - BN.isBN = function isBN (num) { - if (num instanceof BN) { - return true; - } - - return num !== null && typeof num === 'object' && - num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); - }; - - BN.max = function max (left, right) { - if (left.cmp(right) > 0) return left; - return right; - }; - - BN.min = function min (left, right) { - if (left.cmp(right) < 0) return left; - return right; - }; - - BN.prototype._init = function init (number, base, endian) { - if (typeof number === 'number') { - return this._initNumber(number, base, endian); - } - - if (typeof number === 'object') { - return this._initArray(number, base, endian); - } - - if (base === 'hex') { - base = 16; - } - assert(base === (base | 0) && base >= 2 && base <= 36); - - number = number.toString().replace(/\s+/g, ''); - var start = 0; - if (number[0] === '-') { - start++; - this.negative = 1; - } - - if (start < number.length) { - if (base === 16) { - this._parseHex(number, start, endian); - } else { - this._parseBase(number, base, start); - if (endian === 'le') { - this._initArray(this.toArray(), base, endian); - } - } - } - }; - - BN.prototype._initNumber = function _initNumber (number, base, endian) { - if (number < 0) { - this.negative = 1; - number = -number; - } - if (number < 0x4000000) { - this.words = [ number & 0x3ffffff ]; - this.length = 1; - } else if (number < 0x10000000000000) { - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff - ]; - this.length = 2; - } else { - assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff, - 1 - ]; - this.length = 3; - } - - if (endian !== 'le') return; - - // Reverse the bytes - this._initArray(this.toArray(), base, endian); - }; - - BN.prototype._initArray = function _initArray (number, base, endian) { - // Perhaps a Uint8Array - assert(typeof number.length === 'number'); - if (number.length <= 0) { - this.words = [ 0 ]; - this.length = 1; - return this; - } - - this.length = Math.ceil(number.length / 3); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - var j, w; - var off = 0; - if (endian === 'be') { - for (i = number.length - 1, j = 0; i >= 0; i -= 3) { - w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } else if (endian === 'le') { - for (i = 0, j = 0; i < number.length; i += 3) { - w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } - return this.strip(); - }; - - function parseHex4Bits (string, index) { - var c = string.charCodeAt(index); - // 'A' - 'F' - if (c >= 65 && c <= 70) { - return c - 55; - // 'a' - 'f' - } else if (c >= 97 && c <= 102) { - return c - 87; - // '0' - '9' - } else { - return (c - 48) & 0xf; - } - } - - function parseHexByte (string, lowerBound, index) { - var r = parseHex4Bits(string, index); - if (index - 1 >= lowerBound) { - r |= parseHex4Bits(string, index - 1) << 4; - } - return r; - } - - BN.prototype._parseHex = function _parseHex (number, start, endian) { - // Create possibly bigger array to ensure that it fits the number - this.length = Math.ceil((number.length - start) / 6); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - // 24-bits chunks - var off = 0; - var j = 0; - - var w; - if (endian === 'be') { - for (i = number.length - 1; i >= start; i -= 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } else { - var parseLength = number.length - start; - for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } - - this.strip(); - }; - - function parseBase (str, start, end, mul) { - var r = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r *= mul; - - // 'a' - if (c >= 49) { - r += c - 49 + 0xa; - - // 'A' - } else if (c >= 17) { - r += c - 17 + 0xa; - - // '0' - '9' - } else { - r += c; - } - } - return r; - } - - BN.prototype._parseBase = function _parseBase (number, base, start) { - // Initialize as zero - this.words = [ 0 ]; - this.length = 1; - - // Find length of limb in base - for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { - limbLen++; - } - limbLen--; - limbPow = (limbPow / base) | 0; - - var total = number.length - start; - var mod = total % limbLen; - var end = Math.min(total, total - mod) + start; - - var word = 0; - for (var i = start; i < end; i += limbLen) { - word = parseBase(number, i, i + limbLen, base); - - this.imuln(limbPow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - if (mod !== 0) { - var pow = 1; - word = parseBase(number, i, number.length, base); - - for (i = 0; i < mod; i++) { - pow *= base; - } - - this.imuln(pow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - this.strip(); - }; - - BN.prototype.copy = function copy (dest) { - dest.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - dest.words[i] = this.words[i]; - } - dest.length = this.length; - dest.negative = this.negative; - dest.red = this.red; - }; - - BN.prototype.clone = function clone () { - var r = new BN(null); - this.copy(r); - return r; - }; - - BN.prototype._expand = function _expand (size) { - while (this.length < size) { - this.words[this.length++] = 0; - } - return this; - }; - - // Remove leading `0` from `this` - BN.prototype.strip = function strip () { - while (this.length > 1 && this.words[this.length - 1] === 0) { - this.length--; - } - return this._normSign(); - }; - - BN.prototype._normSign = function _normSign () { - // -0 = 0 - if (this.length === 1 && this.words[0] === 0) { - this.negative = 0; - } - return this; - }; - - BN.prototype.inspect = function inspect () { - return (this.red ? ''; - }; - - /* - - var zeros = []; - var groupSizes = []; - var groupBases = []; - - var s = ''; - var i = -1; - while (++i < BN.wordSize) { - zeros[i] = s; - s += '0'; - } - groupSizes[0] = 0; - groupSizes[1] = 0; - groupBases[0] = 0; - groupBases[1] = 0; - var base = 2 - 1; - while (++base < 36 + 1) { - var groupSize = 0; - var groupBase = 1; - while (groupBase < (1 << BN.wordSize) / base) { - groupBase *= base; - groupSize += 1; - } - groupSizes[base] = groupSize; - groupBases[base] = groupBase; - } - - */ - - var zeros = [ - '', - '0', - '00', - '000', - '0000', - '00000', - '000000', - '0000000', - '00000000', - '000000000', - '0000000000', - '00000000000', - '000000000000', - '0000000000000', - '00000000000000', - '000000000000000', - '0000000000000000', - '00000000000000000', - '000000000000000000', - '0000000000000000000', - '00000000000000000000', - '000000000000000000000', - '0000000000000000000000', - '00000000000000000000000', - '000000000000000000000000', - '0000000000000000000000000' - ]; - - var groupSizes = [ - 0, 0, - 25, 16, 12, 11, 10, 9, 8, - 8, 7, 7, 7, 7, 6, 6, - 6, 6, 6, 6, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5 - ]; - - var groupBases = [ - 0, 0, - 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, - 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, - 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, - 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, - 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 - ]; - - BN.prototype.toString = function toString (base, padding) { - base = base || 10; - padding = padding | 0 || 1; - - var out; - if (base === 16 || base === 'hex') { - out = ''; - var off = 0; - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = this.words[i]; - var word = (((w << off) | carry) & 0xffffff).toString(16); - carry = (w >>> (24 - off)) & 0xffffff; - if (carry !== 0 || i !== this.length - 1) { - out = zeros[6 - word.length] + word + out; - } else { - out = word + out; - } - off += 2; - if (off >= 26) { - off -= 26; - i--; - } - } - if (carry !== 0) { - out = carry.toString(16) + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - if (base === (base | 0) && base >= 2 && base <= 36) { - // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); - var groupSize = groupSizes[base]; - // var groupBase = Math.pow(base, groupSize); - var groupBase = groupBases[base]; - out = ''; - var c = this.clone(); - c.negative = 0; - while (!c.isZero()) { - var r = c.modn(groupBase).toString(base); - c = c.idivn(groupBase); - - if (!c.isZero()) { - out = zeros[groupSize - r.length] + r + out; - } else { - out = r + out; - } - } - if (this.isZero()) { - out = '0' + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - assert(false, 'Base should be between 2 and 36'); - }; - - BN.prototype.toNumber = function toNumber () { - var ret = this.words[0]; - if (this.length === 2) { - ret += this.words[1] * 0x4000000; - } else if (this.length === 3 && this.words[2] === 0x01) { - // NOTE: at this stage it is known that the top bit is set - ret += 0x10000000000000 + (this.words[1] * 0x4000000); - } else if (this.length > 2) { - assert(false, 'Number can only safely store up to 53 bits'); - } - return (this.negative !== 0) ? -ret : ret; - }; - - BN.prototype.toJSON = function toJSON () { - return this.toString(16); - }; - - BN.prototype.toBuffer = function toBuffer (endian, length) { - assert(typeof Buffer !== 'undefined'); - return this.toArrayLike(Buffer, endian, length); - }; - - BN.prototype.toArray = function toArray (endian, length) { - return this.toArrayLike(Array, endian, length); - }; - - BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { - var byteLength = this.byteLength(); - var reqLength = length || Math.max(1, byteLength); - assert(byteLength <= reqLength, 'byte array longer than desired length'); - assert(reqLength > 0, 'Requested array length <= 0'); - - this.strip(); - var littleEndian = endian === 'le'; - var res = new ArrayType(reqLength); - - var b, i; - var q = this.clone(); - if (!littleEndian) { - // Assume big-endian - for (i = 0; i < reqLength - byteLength; i++) { - res[i] = 0; - } - - for (i = 0; !q.isZero(); i++) { - b = q.andln(0xff); - q.iushrn(8); - - res[reqLength - i - 1] = b; - } - } else { - for (i = 0; !q.isZero(); i++) { - b = q.andln(0xff); - q.iushrn(8); - - res[i] = b; - } - - for (; i < reqLength; i++) { - res[i] = 0; - } - } - - return res; - }; - - if (Math.clz32) { - BN.prototype._countBits = function _countBits (w) { - return 32 - Math.clz32(w); - }; - } else { - BN.prototype._countBits = function _countBits (w) { - var t = w; - var r = 0; - if (t >= 0x1000) { - r += 13; - t >>>= 13; - } - if (t >= 0x40) { - r += 7; - t >>>= 7; - } - if (t >= 0x8) { - r += 4; - t >>>= 4; - } - if (t >= 0x02) { - r += 2; - t >>>= 2; - } - return r + t; - }; - } - - BN.prototype._zeroBits = function _zeroBits (w) { - // Short-cut - if (w === 0) return 26; - - var t = w; - var r = 0; - if ((t & 0x1fff) === 0) { - r += 13; - t >>>= 13; - } - if ((t & 0x7f) === 0) { - r += 7; - t >>>= 7; - } - if ((t & 0xf) === 0) { - r += 4; - t >>>= 4; - } - if ((t & 0x3) === 0) { - r += 2; - t >>>= 2; - } - if ((t & 0x1) === 0) { - r++; - } - return r; - }; - - // Return number of used bits in a BN - BN.prototype.bitLength = function bitLength () { - var w = this.words[this.length - 1]; - var hi = this._countBits(w); - return (this.length - 1) * 26 + hi; - }; - - function toBitArray (num) { - var w = new Array(num.bitLength()); - - for (var bit = 0; bit < w.length; bit++) { - var off = (bit / 26) | 0; - var wbit = bit % 26; - - w[bit] = (num.words[off] & (1 << wbit)) >>> wbit; - } - - return w; - } - - // Number of trailing zero bits - BN.prototype.zeroBits = function zeroBits () { - if (this.isZero()) return 0; - - var r = 0; - for (var i = 0; i < this.length; i++) { - var b = this._zeroBits(this.words[i]); - r += b; - if (b !== 26) break; - } - return r; - }; - - BN.prototype.byteLength = function byteLength () { - return Math.ceil(this.bitLength() / 8); - }; - - BN.prototype.toTwos = function toTwos (width) { - if (this.negative !== 0) { - return this.abs().inotn(width).iaddn(1); - } - return this.clone(); - }; - - BN.prototype.fromTwos = function fromTwos (width) { - if (this.testn(width - 1)) { - return this.notn(width).iaddn(1).ineg(); - } - return this.clone(); - }; - - BN.prototype.isNeg = function isNeg () { - return this.negative !== 0; - }; - - // Return negative clone of `this` - BN.prototype.neg = function neg () { - return this.clone().ineg(); - }; - - BN.prototype.ineg = function ineg () { - if (!this.isZero()) { - this.negative ^= 1; - } - - return this; - }; - - // Or `num` with `this` in-place - BN.prototype.iuor = function iuor (num) { - while (this.length < num.length) { - this.words[this.length++] = 0; - } - - for (var i = 0; i < num.length; i++) { - this.words[i] = this.words[i] | num.words[i]; - } - - return this.strip(); - }; - - BN.prototype.ior = function ior (num) { - assert((this.negative | num.negative) === 0); - return this.iuor(num); - }; - - // Or `num` with `this` - BN.prototype.or = function or (num) { - if (this.length > num.length) return this.clone().ior(num); - return num.clone().ior(this); - }; - - BN.prototype.uor = function uor (num) { - if (this.length > num.length) return this.clone().iuor(num); - return num.clone().iuor(this); - }; - - // And `num` with `this` in-place - BN.prototype.iuand = function iuand (num) { - // b = min-length(num, this) - var b; - if (this.length > num.length) { - b = num; - } else { - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = this.words[i] & num.words[i]; - } - - this.length = b.length; - - return this.strip(); - }; - - BN.prototype.iand = function iand (num) { - assert((this.negative | num.negative) === 0); - return this.iuand(num); - }; - - // And `num` with `this` - BN.prototype.and = function and (num) { - if (this.length > num.length) return this.clone().iand(num); - return num.clone().iand(this); - }; - - BN.prototype.uand = function uand (num) { - if (this.length > num.length) return this.clone().iuand(num); - return num.clone().iuand(this); - }; - - // Xor `num` with `this` in-place - BN.prototype.iuxor = function iuxor (num) { - // a.length > b.length - var a; - var b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = a.words[i] ^ b.words[i]; - } - - if (this !== a) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = a.length; - - return this.strip(); - }; - - BN.prototype.ixor = function ixor (num) { - assert((this.negative | num.negative) === 0); - return this.iuxor(num); - }; - - // Xor `num` with `this` - BN.prototype.xor = function xor (num) { - if (this.length > num.length) return this.clone().ixor(num); - return num.clone().ixor(this); - }; - - BN.prototype.uxor = function uxor (num) { - if (this.length > num.length) return this.clone().iuxor(num); - return num.clone().iuxor(this); - }; - - // Not ``this`` with ``width`` bitwidth - BN.prototype.inotn = function inotn (width) { - assert(typeof width === 'number' && width >= 0); - - var bytesNeeded = Math.ceil(width / 26) | 0; - var bitsLeft = width % 26; - - // Extend the buffer with leading zeroes - this._expand(bytesNeeded); - - if (bitsLeft > 0) { - bytesNeeded--; - } - - // Handle complete words - for (var i = 0; i < bytesNeeded; i++) { - this.words[i] = ~this.words[i] & 0x3ffffff; - } - - // Handle the residue - if (bitsLeft > 0) { - this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); - } - - // And remove leading zeroes - return this.strip(); - }; - - BN.prototype.notn = function notn (width) { - return this.clone().inotn(width); - }; - - // Set `bit` of `this` - BN.prototype.setn = function setn (bit, val) { - assert(typeof bit === 'number' && bit >= 0); - - var off = (bit / 26) | 0; - var wbit = bit % 26; - - this._expand(off + 1); - - if (val) { - this.words[off] = this.words[off] | (1 << wbit); - } else { - this.words[off] = this.words[off] & ~(1 << wbit); - } - - return this.strip(); - }; - - // Add `num` to `this` in-place - BN.prototype.iadd = function iadd (num) { - var r; - - // negative + positive - if (this.negative !== 0 && num.negative === 0) { - this.negative = 0; - r = this.isub(num); - this.negative ^= 1; - return this._normSign(); - - // positive + negative - } else if (this.negative === 0 && num.negative !== 0) { - num.negative = 0; - r = this.isub(num); - num.negative = 1; - return r._normSign(); - } - - // a.length > b.length - var a, b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) + (b.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - - this.length = a.length; - if (carry !== 0) { - this.words[this.length] = carry; - this.length++; - // Copy the rest of the words - } else if (a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - return this; - }; - - // Add `num` to `this` - BN.prototype.add = function add (num) { - var res; - if (num.negative !== 0 && this.negative === 0) { - num.negative = 0; - res = this.sub(num); - num.negative ^= 1; - return res; - } else if (num.negative === 0 && this.negative !== 0) { - this.negative = 0; - res = num.sub(this); - this.negative = 1; - return res; - } - - if (this.length > num.length) return this.clone().iadd(num); - - return num.clone().iadd(this); - }; - - // Subtract `num` from `this` in-place - BN.prototype.isub = function isub (num) { - // this - (-num) = this + num - if (num.negative !== 0) { - num.negative = 0; - var r = this.iadd(num); - num.negative = 1; - return r._normSign(); - - // -this - num = -(this + num) - } else if (this.negative !== 0) { - this.negative = 0; - this.iadd(num); - this.negative = 1; - return this._normSign(); - } - - // At this point both numbers are positive - var cmp = this.cmp(num); - - // Optimization - zeroify - if (cmp === 0) { - this.negative = 0; - this.length = 1; - this.words[0] = 0; - return this; - } - - // a > b - var a, b; - if (cmp > 0) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) - (b.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - - // Copy rest of the words - if (carry === 0 && i < a.length && a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = Math.max(this.length, i); - - if (a !== this) { - this.negative = 1; - } - - return this.strip(); - }; - - // Subtract `num` from `this` - BN.prototype.sub = function sub (num) { - return this.clone().isub(num); - }; - - function smallMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - var len = (self.length + num.length) | 0; - out.length = len; - len = (len - 1) | 0; - - // Peel one iteration (compiler can't do it, because of code complexity) - var a = self.words[0] | 0; - var b = num.words[0] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - var carry = (r / 0x4000000) | 0; - out.words[0] = lo; - - for (var k = 1; k < len; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = carry >>> 26; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = (k - j) | 0; - a = self.words[i] | 0; - b = num.words[j] | 0; - r = a * b + rword; - ncarry += (r / 0x4000000) | 0; - rword = r & 0x3ffffff; - } - out.words[k] = rword | 0; - carry = ncarry | 0; - } - if (carry !== 0) { - out.words[k] = carry | 0; - } else { - out.length--; - } - - return out.strip(); - } - - // TODO(indutny): it may be reasonable to omit it for users who don't need - // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit - // multiplication (like elliptic secp256k1). - var comb10MulTo = function comb10MulTo (self, num, out) { - var a = self.words; - var b = num.words; - var o = out.words; - var c = 0; - var lo; - var mid; - var hi; - var a0 = a[0] | 0; - var al0 = a0 & 0x1fff; - var ah0 = a0 >>> 13; - var a1 = a[1] | 0; - var al1 = a1 & 0x1fff; - var ah1 = a1 >>> 13; - var a2 = a[2] | 0; - var al2 = a2 & 0x1fff; - var ah2 = a2 >>> 13; - var a3 = a[3] | 0; - var al3 = a3 & 0x1fff; - var ah3 = a3 >>> 13; - var a4 = a[4] | 0; - var al4 = a4 & 0x1fff; - var ah4 = a4 >>> 13; - var a5 = a[5] | 0; - var al5 = a5 & 0x1fff; - var ah5 = a5 >>> 13; - var a6 = a[6] | 0; - var al6 = a6 & 0x1fff; - var ah6 = a6 >>> 13; - var a7 = a[7] | 0; - var al7 = a7 & 0x1fff; - var ah7 = a7 >>> 13; - var a8 = a[8] | 0; - var al8 = a8 & 0x1fff; - var ah8 = a8 >>> 13; - var a9 = a[9] | 0; - var al9 = a9 & 0x1fff; - var ah9 = a9 >>> 13; - var b0 = b[0] | 0; - var bl0 = b0 & 0x1fff; - var bh0 = b0 >>> 13; - var b1 = b[1] | 0; - var bl1 = b1 & 0x1fff; - var bh1 = b1 >>> 13; - var b2 = b[2] | 0; - var bl2 = b2 & 0x1fff; - var bh2 = b2 >>> 13; - var b3 = b[3] | 0; - var bl3 = b3 & 0x1fff; - var bh3 = b3 >>> 13; - var b4 = b[4] | 0; - var bl4 = b4 & 0x1fff; - var bh4 = b4 >>> 13; - var b5 = b[5] | 0; - var bl5 = b5 & 0x1fff; - var bh5 = b5 >>> 13; - var b6 = b[6] | 0; - var bl6 = b6 & 0x1fff; - var bh6 = b6 >>> 13; - var b7 = b[7] | 0; - var bl7 = b7 & 0x1fff; - var bh7 = b7 >>> 13; - var b8 = b[8] | 0; - var bl8 = b8 & 0x1fff; - var bh8 = b8 >>> 13; - var b9 = b[9] | 0; - var bl9 = b9 & 0x1fff; - var bh9 = b9 >>> 13; - - out.negative = self.negative ^ num.negative; - out.length = 19; - /* k = 0 */ - lo = Math.imul(al0, bl0); - mid = Math.imul(al0, bh0); - mid = (mid + Math.imul(ah0, bl0)) | 0; - hi = Math.imul(ah0, bh0); - var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; - w0 &= 0x3ffffff; - /* k = 1 */ - lo = Math.imul(al1, bl0); - mid = Math.imul(al1, bh0); - mid = (mid + Math.imul(ah1, bl0)) | 0; - hi = Math.imul(ah1, bh0); - lo = (lo + Math.imul(al0, bl1)) | 0; - mid = (mid + Math.imul(al0, bh1)) | 0; - mid = (mid + Math.imul(ah0, bl1)) | 0; - hi = (hi + Math.imul(ah0, bh1)) | 0; - var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; - w1 &= 0x3ffffff; - /* k = 2 */ - lo = Math.imul(al2, bl0); - mid = Math.imul(al2, bh0); - mid = (mid + Math.imul(ah2, bl0)) | 0; - hi = Math.imul(ah2, bh0); - lo = (lo + Math.imul(al1, bl1)) | 0; - mid = (mid + Math.imul(al1, bh1)) | 0; - mid = (mid + Math.imul(ah1, bl1)) | 0; - hi = (hi + Math.imul(ah1, bh1)) | 0; - lo = (lo + Math.imul(al0, bl2)) | 0; - mid = (mid + Math.imul(al0, bh2)) | 0; - mid = (mid + Math.imul(ah0, bl2)) | 0; - hi = (hi + Math.imul(ah0, bh2)) | 0; - var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; - w2 &= 0x3ffffff; - /* k = 3 */ - lo = Math.imul(al3, bl0); - mid = Math.imul(al3, bh0); - mid = (mid + Math.imul(ah3, bl0)) | 0; - hi = Math.imul(ah3, bh0); - lo = (lo + Math.imul(al2, bl1)) | 0; - mid = (mid + Math.imul(al2, bh1)) | 0; - mid = (mid + Math.imul(ah2, bl1)) | 0; - hi = (hi + Math.imul(ah2, bh1)) | 0; - lo = (lo + Math.imul(al1, bl2)) | 0; - mid = (mid + Math.imul(al1, bh2)) | 0; - mid = (mid + Math.imul(ah1, bl2)) | 0; - hi = (hi + Math.imul(ah1, bh2)) | 0; - lo = (lo + Math.imul(al0, bl3)) | 0; - mid = (mid + Math.imul(al0, bh3)) | 0; - mid = (mid + Math.imul(ah0, bl3)) | 0; - hi = (hi + Math.imul(ah0, bh3)) | 0; - var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; - w3 &= 0x3ffffff; - /* k = 4 */ - lo = Math.imul(al4, bl0); - mid = Math.imul(al4, bh0); - mid = (mid + Math.imul(ah4, bl0)) | 0; - hi = Math.imul(ah4, bh0); - lo = (lo + Math.imul(al3, bl1)) | 0; - mid = (mid + Math.imul(al3, bh1)) | 0; - mid = (mid + Math.imul(ah3, bl1)) | 0; - hi = (hi + Math.imul(ah3, bh1)) | 0; - lo = (lo + Math.imul(al2, bl2)) | 0; - mid = (mid + Math.imul(al2, bh2)) | 0; - mid = (mid + Math.imul(ah2, bl2)) | 0; - hi = (hi + Math.imul(ah2, bh2)) | 0; - lo = (lo + Math.imul(al1, bl3)) | 0; - mid = (mid + Math.imul(al1, bh3)) | 0; - mid = (mid + Math.imul(ah1, bl3)) | 0; - hi = (hi + Math.imul(ah1, bh3)) | 0; - lo = (lo + Math.imul(al0, bl4)) | 0; - mid = (mid + Math.imul(al0, bh4)) | 0; - mid = (mid + Math.imul(ah0, bl4)) | 0; - hi = (hi + Math.imul(ah0, bh4)) | 0; - var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; - w4 &= 0x3ffffff; - /* k = 5 */ - lo = Math.imul(al5, bl0); - mid = Math.imul(al5, bh0); - mid = (mid + Math.imul(ah5, bl0)) | 0; - hi = Math.imul(ah5, bh0); - lo = (lo + Math.imul(al4, bl1)) | 0; - mid = (mid + Math.imul(al4, bh1)) | 0; - mid = (mid + Math.imul(ah4, bl1)) | 0; - hi = (hi + Math.imul(ah4, bh1)) | 0; - lo = (lo + Math.imul(al3, bl2)) | 0; - mid = (mid + Math.imul(al3, bh2)) | 0; - mid = (mid + Math.imul(ah3, bl2)) | 0; - hi = (hi + Math.imul(ah3, bh2)) | 0; - lo = (lo + Math.imul(al2, bl3)) | 0; - mid = (mid + Math.imul(al2, bh3)) | 0; - mid = (mid + Math.imul(ah2, bl3)) | 0; - hi = (hi + Math.imul(ah2, bh3)) | 0; - lo = (lo + Math.imul(al1, bl4)) | 0; - mid = (mid + Math.imul(al1, bh4)) | 0; - mid = (mid + Math.imul(ah1, bl4)) | 0; - hi = (hi + Math.imul(ah1, bh4)) | 0; - lo = (lo + Math.imul(al0, bl5)) | 0; - mid = (mid + Math.imul(al0, bh5)) | 0; - mid = (mid + Math.imul(ah0, bl5)) | 0; - hi = (hi + Math.imul(ah0, bh5)) | 0; - var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; - w5 &= 0x3ffffff; - /* k = 6 */ - lo = Math.imul(al6, bl0); - mid = Math.imul(al6, bh0); - mid = (mid + Math.imul(ah6, bl0)) | 0; - hi = Math.imul(ah6, bh0); - lo = (lo + Math.imul(al5, bl1)) | 0; - mid = (mid + Math.imul(al5, bh1)) | 0; - mid = (mid + Math.imul(ah5, bl1)) | 0; - hi = (hi + Math.imul(ah5, bh1)) | 0; - lo = (lo + Math.imul(al4, bl2)) | 0; - mid = (mid + Math.imul(al4, bh2)) | 0; - mid = (mid + Math.imul(ah4, bl2)) | 0; - hi = (hi + Math.imul(ah4, bh2)) | 0; - lo = (lo + Math.imul(al3, bl3)) | 0; - mid = (mid + Math.imul(al3, bh3)) | 0; - mid = (mid + Math.imul(ah3, bl3)) | 0; - hi = (hi + Math.imul(ah3, bh3)) | 0; - lo = (lo + Math.imul(al2, bl4)) | 0; - mid = (mid + Math.imul(al2, bh4)) | 0; - mid = (mid + Math.imul(ah2, bl4)) | 0; - hi = (hi + Math.imul(ah2, bh4)) | 0; - lo = (lo + Math.imul(al1, bl5)) | 0; - mid = (mid + Math.imul(al1, bh5)) | 0; - mid = (mid + Math.imul(ah1, bl5)) | 0; - hi = (hi + Math.imul(ah1, bh5)) | 0; - lo = (lo + Math.imul(al0, bl6)) | 0; - mid = (mid + Math.imul(al0, bh6)) | 0; - mid = (mid + Math.imul(ah0, bl6)) | 0; - hi = (hi + Math.imul(ah0, bh6)) | 0; - var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; - w6 &= 0x3ffffff; - /* k = 7 */ - lo = Math.imul(al7, bl0); - mid = Math.imul(al7, bh0); - mid = (mid + Math.imul(ah7, bl0)) | 0; - hi = Math.imul(ah7, bh0); - lo = (lo + Math.imul(al6, bl1)) | 0; - mid = (mid + Math.imul(al6, bh1)) | 0; - mid = (mid + Math.imul(ah6, bl1)) | 0; - hi = (hi + Math.imul(ah6, bh1)) | 0; - lo = (lo + Math.imul(al5, bl2)) | 0; - mid = (mid + Math.imul(al5, bh2)) | 0; - mid = (mid + Math.imul(ah5, bl2)) | 0; - hi = (hi + Math.imul(ah5, bh2)) | 0; - lo = (lo + Math.imul(al4, bl3)) | 0; - mid = (mid + Math.imul(al4, bh3)) | 0; - mid = (mid + Math.imul(ah4, bl3)) | 0; - hi = (hi + Math.imul(ah4, bh3)) | 0; - lo = (lo + Math.imul(al3, bl4)) | 0; - mid = (mid + Math.imul(al3, bh4)) | 0; - mid = (mid + Math.imul(ah3, bl4)) | 0; - hi = (hi + Math.imul(ah3, bh4)) | 0; - lo = (lo + Math.imul(al2, bl5)) | 0; - mid = (mid + Math.imul(al2, bh5)) | 0; - mid = (mid + Math.imul(ah2, bl5)) | 0; - hi = (hi + Math.imul(ah2, bh5)) | 0; - lo = (lo + Math.imul(al1, bl6)) | 0; - mid = (mid + Math.imul(al1, bh6)) | 0; - mid = (mid + Math.imul(ah1, bl6)) | 0; - hi = (hi + Math.imul(ah1, bh6)) | 0; - lo = (lo + Math.imul(al0, bl7)) | 0; - mid = (mid + Math.imul(al0, bh7)) | 0; - mid = (mid + Math.imul(ah0, bl7)) | 0; - hi = (hi + Math.imul(ah0, bh7)) | 0; - var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; - w7 &= 0x3ffffff; - /* k = 8 */ - lo = Math.imul(al8, bl0); - mid = Math.imul(al8, bh0); - mid = (mid + Math.imul(ah8, bl0)) | 0; - hi = Math.imul(ah8, bh0); - lo = (lo + Math.imul(al7, bl1)) | 0; - mid = (mid + Math.imul(al7, bh1)) | 0; - mid = (mid + Math.imul(ah7, bl1)) | 0; - hi = (hi + Math.imul(ah7, bh1)) | 0; - lo = (lo + Math.imul(al6, bl2)) | 0; - mid = (mid + Math.imul(al6, bh2)) | 0; - mid = (mid + Math.imul(ah6, bl2)) | 0; - hi = (hi + Math.imul(ah6, bh2)) | 0; - lo = (lo + Math.imul(al5, bl3)) | 0; - mid = (mid + Math.imul(al5, bh3)) | 0; - mid = (mid + Math.imul(ah5, bl3)) | 0; - hi = (hi + Math.imul(ah5, bh3)) | 0; - lo = (lo + Math.imul(al4, bl4)) | 0; - mid = (mid + Math.imul(al4, bh4)) | 0; - mid = (mid + Math.imul(ah4, bl4)) | 0; - hi = (hi + Math.imul(ah4, bh4)) | 0; - lo = (lo + Math.imul(al3, bl5)) | 0; - mid = (mid + Math.imul(al3, bh5)) | 0; - mid = (mid + Math.imul(ah3, bl5)) | 0; - hi = (hi + Math.imul(ah3, bh5)) | 0; - lo = (lo + Math.imul(al2, bl6)) | 0; - mid = (mid + Math.imul(al2, bh6)) | 0; - mid = (mid + Math.imul(ah2, bl6)) | 0; - hi = (hi + Math.imul(ah2, bh6)) | 0; - lo = (lo + Math.imul(al1, bl7)) | 0; - mid = (mid + Math.imul(al1, bh7)) | 0; - mid = (mid + Math.imul(ah1, bl7)) | 0; - hi = (hi + Math.imul(ah1, bh7)) | 0; - lo = (lo + Math.imul(al0, bl8)) | 0; - mid = (mid + Math.imul(al0, bh8)) | 0; - mid = (mid + Math.imul(ah0, bl8)) | 0; - hi = (hi + Math.imul(ah0, bh8)) | 0; - var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; - w8 &= 0x3ffffff; - /* k = 9 */ - lo = Math.imul(al9, bl0); - mid = Math.imul(al9, bh0); - mid = (mid + Math.imul(ah9, bl0)) | 0; - hi = Math.imul(ah9, bh0); - lo = (lo + Math.imul(al8, bl1)) | 0; - mid = (mid + Math.imul(al8, bh1)) | 0; - mid = (mid + Math.imul(ah8, bl1)) | 0; - hi = (hi + Math.imul(ah8, bh1)) | 0; - lo = (lo + Math.imul(al7, bl2)) | 0; - mid = (mid + Math.imul(al7, bh2)) | 0; - mid = (mid + Math.imul(ah7, bl2)) | 0; - hi = (hi + Math.imul(ah7, bh2)) | 0; - lo = (lo + Math.imul(al6, bl3)) | 0; - mid = (mid + Math.imul(al6, bh3)) | 0; - mid = (mid + Math.imul(ah6, bl3)) | 0; - hi = (hi + Math.imul(ah6, bh3)) | 0; - lo = (lo + Math.imul(al5, bl4)) | 0; - mid = (mid + Math.imul(al5, bh4)) | 0; - mid = (mid + Math.imul(ah5, bl4)) | 0; - hi = (hi + Math.imul(ah5, bh4)) | 0; - lo = (lo + Math.imul(al4, bl5)) | 0; - mid = (mid + Math.imul(al4, bh5)) | 0; - mid = (mid + Math.imul(ah4, bl5)) | 0; - hi = (hi + Math.imul(ah4, bh5)) | 0; - lo = (lo + Math.imul(al3, bl6)) | 0; - mid = (mid + Math.imul(al3, bh6)) | 0; - mid = (mid + Math.imul(ah3, bl6)) | 0; - hi = (hi + Math.imul(ah3, bh6)) | 0; - lo = (lo + Math.imul(al2, bl7)) | 0; - mid = (mid + Math.imul(al2, bh7)) | 0; - mid = (mid + Math.imul(ah2, bl7)) | 0; - hi = (hi + Math.imul(ah2, bh7)) | 0; - lo = (lo + Math.imul(al1, bl8)) | 0; - mid = (mid + Math.imul(al1, bh8)) | 0; - mid = (mid + Math.imul(ah1, bl8)) | 0; - hi = (hi + Math.imul(ah1, bh8)) | 0; - lo = (lo + Math.imul(al0, bl9)) | 0; - mid = (mid + Math.imul(al0, bh9)) | 0; - mid = (mid + Math.imul(ah0, bl9)) | 0; - hi = (hi + Math.imul(ah0, bh9)) | 0; - var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; - w9 &= 0x3ffffff; - /* k = 10 */ - lo = Math.imul(al9, bl1); - mid = Math.imul(al9, bh1); - mid = (mid + Math.imul(ah9, bl1)) | 0; - hi = Math.imul(ah9, bh1); - lo = (lo + Math.imul(al8, bl2)) | 0; - mid = (mid + Math.imul(al8, bh2)) | 0; - mid = (mid + Math.imul(ah8, bl2)) | 0; - hi = (hi + Math.imul(ah8, bh2)) | 0; - lo = (lo + Math.imul(al7, bl3)) | 0; - mid = (mid + Math.imul(al7, bh3)) | 0; - mid = (mid + Math.imul(ah7, bl3)) | 0; - hi = (hi + Math.imul(ah7, bh3)) | 0; - lo = (lo + Math.imul(al6, bl4)) | 0; - mid = (mid + Math.imul(al6, bh4)) | 0; - mid = (mid + Math.imul(ah6, bl4)) | 0; - hi = (hi + Math.imul(ah6, bh4)) | 0; - lo = (lo + Math.imul(al5, bl5)) | 0; - mid = (mid + Math.imul(al5, bh5)) | 0; - mid = (mid + Math.imul(ah5, bl5)) | 0; - hi = (hi + Math.imul(ah5, bh5)) | 0; - lo = (lo + Math.imul(al4, bl6)) | 0; - mid = (mid + Math.imul(al4, bh6)) | 0; - mid = (mid + Math.imul(ah4, bl6)) | 0; - hi = (hi + Math.imul(ah4, bh6)) | 0; - lo = (lo + Math.imul(al3, bl7)) | 0; - mid = (mid + Math.imul(al3, bh7)) | 0; - mid = (mid + Math.imul(ah3, bl7)) | 0; - hi = (hi + Math.imul(ah3, bh7)) | 0; - lo = (lo + Math.imul(al2, bl8)) | 0; - mid = (mid + Math.imul(al2, bh8)) | 0; - mid = (mid + Math.imul(ah2, bl8)) | 0; - hi = (hi + Math.imul(ah2, bh8)) | 0; - lo = (lo + Math.imul(al1, bl9)) | 0; - mid = (mid + Math.imul(al1, bh9)) | 0; - mid = (mid + Math.imul(ah1, bl9)) | 0; - hi = (hi + Math.imul(ah1, bh9)) | 0; - var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; - w10 &= 0x3ffffff; - /* k = 11 */ - lo = Math.imul(al9, bl2); - mid = Math.imul(al9, bh2); - mid = (mid + Math.imul(ah9, bl2)) | 0; - hi = Math.imul(ah9, bh2); - lo = (lo + Math.imul(al8, bl3)) | 0; - mid = (mid + Math.imul(al8, bh3)) | 0; - mid = (mid + Math.imul(ah8, bl3)) | 0; - hi = (hi + Math.imul(ah8, bh3)) | 0; - lo = (lo + Math.imul(al7, bl4)) | 0; - mid = (mid + Math.imul(al7, bh4)) | 0; - mid = (mid + Math.imul(ah7, bl4)) | 0; - hi = (hi + Math.imul(ah7, bh4)) | 0; - lo = (lo + Math.imul(al6, bl5)) | 0; - mid = (mid + Math.imul(al6, bh5)) | 0; - mid = (mid + Math.imul(ah6, bl5)) | 0; - hi = (hi + Math.imul(ah6, bh5)) | 0; - lo = (lo + Math.imul(al5, bl6)) | 0; - mid = (mid + Math.imul(al5, bh6)) | 0; - mid = (mid + Math.imul(ah5, bl6)) | 0; - hi = (hi + Math.imul(ah5, bh6)) | 0; - lo = (lo + Math.imul(al4, bl7)) | 0; - mid = (mid + Math.imul(al4, bh7)) | 0; - mid = (mid + Math.imul(ah4, bl7)) | 0; - hi = (hi + Math.imul(ah4, bh7)) | 0; - lo = (lo + Math.imul(al3, bl8)) | 0; - mid = (mid + Math.imul(al3, bh8)) | 0; - mid = (mid + Math.imul(ah3, bl8)) | 0; - hi = (hi + Math.imul(ah3, bh8)) | 0; - lo = (lo + Math.imul(al2, bl9)) | 0; - mid = (mid + Math.imul(al2, bh9)) | 0; - mid = (mid + Math.imul(ah2, bl9)) | 0; - hi = (hi + Math.imul(ah2, bh9)) | 0; - var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; - w11 &= 0x3ffffff; - /* k = 12 */ - lo = Math.imul(al9, bl3); - mid = Math.imul(al9, bh3); - mid = (mid + Math.imul(ah9, bl3)) | 0; - hi = Math.imul(ah9, bh3); - lo = (lo + Math.imul(al8, bl4)) | 0; - mid = (mid + Math.imul(al8, bh4)) | 0; - mid = (mid + Math.imul(ah8, bl4)) | 0; - hi = (hi + Math.imul(ah8, bh4)) | 0; - lo = (lo + Math.imul(al7, bl5)) | 0; - mid = (mid + Math.imul(al7, bh5)) | 0; - mid = (mid + Math.imul(ah7, bl5)) | 0; - hi = (hi + Math.imul(ah7, bh5)) | 0; - lo = (lo + Math.imul(al6, bl6)) | 0; - mid = (mid + Math.imul(al6, bh6)) | 0; - mid = (mid + Math.imul(ah6, bl6)) | 0; - hi = (hi + Math.imul(ah6, bh6)) | 0; - lo = (lo + Math.imul(al5, bl7)) | 0; - mid = (mid + Math.imul(al5, bh7)) | 0; - mid = (mid + Math.imul(ah5, bl7)) | 0; - hi = (hi + Math.imul(ah5, bh7)) | 0; - lo = (lo + Math.imul(al4, bl8)) | 0; - mid = (mid + Math.imul(al4, bh8)) | 0; - mid = (mid + Math.imul(ah4, bl8)) | 0; - hi = (hi + Math.imul(ah4, bh8)) | 0; - lo = (lo + Math.imul(al3, bl9)) | 0; - mid = (mid + Math.imul(al3, bh9)) | 0; - mid = (mid + Math.imul(ah3, bl9)) | 0; - hi = (hi + Math.imul(ah3, bh9)) | 0; - var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; - w12 &= 0x3ffffff; - /* k = 13 */ - lo = Math.imul(al9, bl4); - mid = Math.imul(al9, bh4); - mid = (mid + Math.imul(ah9, bl4)) | 0; - hi = Math.imul(ah9, bh4); - lo = (lo + Math.imul(al8, bl5)) | 0; - mid = (mid + Math.imul(al8, bh5)) | 0; - mid = (mid + Math.imul(ah8, bl5)) | 0; - hi = (hi + Math.imul(ah8, bh5)) | 0; - lo = (lo + Math.imul(al7, bl6)) | 0; - mid = (mid + Math.imul(al7, bh6)) | 0; - mid = (mid + Math.imul(ah7, bl6)) | 0; - hi = (hi + Math.imul(ah7, bh6)) | 0; - lo = (lo + Math.imul(al6, bl7)) | 0; - mid = (mid + Math.imul(al6, bh7)) | 0; - mid = (mid + Math.imul(ah6, bl7)) | 0; - hi = (hi + Math.imul(ah6, bh7)) | 0; - lo = (lo + Math.imul(al5, bl8)) | 0; - mid = (mid + Math.imul(al5, bh8)) | 0; - mid = (mid + Math.imul(ah5, bl8)) | 0; - hi = (hi + Math.imul(ah5, bh8)) | 0; - lo = (lo + Math.imul(al4, bl9)) | 0; - mid = (mid + Math.imul(al4, bh9)) | 0; - mid = (mid + Math.imul(ah4, bl9)) | 0; - hi = (hi + Math.imul(ah4, bh9)) | 0; - var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; - w13 &= 0x3ffffff; - /* k = 14 */ - lo = Math.imul(al9, bl5); - mid = Math.imul(al9, bh5); - mid = (mid + Math.imul(ah9, bl5)) | 0; - hi = Math.imul(ah9, bh5); - lo = (lo + Math.imul(al8, bl6)) | 0; - mid = (mid + Math.imul(al8, bh6)) | 0; - mid = (mid + Math.imul(ah8, bl6)) | 0; - hi = (hi + Math.imul(ah8, bh6)) | 0; - lo = (lo + Math.imul(al7, bl7)) | 0; - mid = (mid + Math.imul(al7, bh7)) | 0; - mid = (mid + Math.imul(ah7, bl7)) | 0; - hi = (hi + Math.imul(ah7, bh7)) | 0; - lo = (lo + Math.imul(al6, bl8)) | 0; - mid = (mid + Math.imul(al6, bh8)) | 0; - mid = (mid + Math.imul(ah6, bl8)) | 0; - hi = (hi + Math.imul(ah6, bh8)) | 0; - lo = (lo + Math.imul(al5, bl9)) | 0; - mid = (mid + Math.imul(al5, bh9)) | 0; - mid = (mid + Math.imul(ah5, bl9)) | 0; - hi = (hi + Math.imul(ah5, bh9)) | 0; - var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; - w14 &= 0x3ffffff; - /* k = 15 */ - lo = Math.imul(al9, bl6); - mid = Math.imul(al9, bh6); - mid = (mid + Math.imul(ah9, bl6)) | 0; - hi = Math.imul(ah9, bh6); - lo = (lo + Math.imul(al8, bl7)) | 0; - mid = (mid + Math.imul(al8, bh7)) | 0; - mid = (mid + Math.imul(ah8, bl7)) | 0; - hi = (hi + Math.imul(ah8, bh7)) | 0; - lo = (lo + Math.imul(al7, bl8)) | 0; - mid = (mid + Math.imul(al7, bh8)) | 0; - mid = (mid + Math.imul(ah7, bl8)) | 0; - hi = (hi + Math.imul(ah7, bh8)) | 0; - lo = (lo + Math.imul(al6, bl9)) | 0; - mid = (mid + Math.imul(al6, bh9)) | 0; - mid = (mid + Math.imul(ah6, bl9)) | 0; - hi = (hi + Math.imul(ah6, bh9)) | 0; - var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; - w15 &= 0x3ffffff; - /* k = 16 */ - lo = Math.imul(al9, bl7); - mid = Math.imul(al9, bh7); - mid = (mid + Math.imul(ah9, bl7)) | 0; - hi = Math.imul(ah9, bh7); - lo = (lo + Math.imul(al8, bl8)) | 0; - mid = (mid + Math.imul(al8, bh8)) | 0; - mid = (mid + Math.imul(ah8, bl8)) | 0; - hi = (hi + Math.imul(ah8, bh8)) | 0; - lo = (lo + Math.imul(al7, bl9)) | 0; - mid = (mid + Math.imul(al7, bh9)) | 0; - mid = (mid + Math.imul(ah7, bl9)) | 0; - hi = (hi + Math.imul(ah7, bh9)) | 0; - var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; - w16 &= 0x3ffffff; - /* k = 17 */ - lo = Math.imul(al9, bl8); - mid = Math.imul(al9, bh8); - mid = (mid + Math.imul(ah9, bl8)) | 0; - hi = Math.imul(ah9, bh8); - lo = (lo + Math.imul(al8, bl9)) | 0; - mid = (mid + Math.imul(al8, bh9)) | 0; - mid = (mid + Math.imul(ah8, bl9)) | 0; - hi = (hi + Math.imul(ah8, bh9)) | 0; - var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; - w17 &= 0x3ffffff; - /* k = 18 */ - lo = Math.imul(al9, bl9); - mid = Math.imul(al9, bh9); - mid = (mid + Math.imul(ah9, bl9)) | 0; - hi = Math.imul(ah9, bh9); - var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; - w18 &= 0x3ffffff; - o[0] = w0; - o[1] = w1; - o[2] = w2; - o[3] = w3; - o[4] = w4; - o[5] = w5; - o[6] = w6; - o[7] = w7; - o[8] = w8; - o[9] = w9; - o[10] = w10; - o[11] = w11; - o[12] = w12; - o[13] = w13; - o[14] = w14; - o[15] = w15; - o[16] = w16; - o[17] = w17; - o[18] = w18; - if (c !== 0) { - o[19] = c; - out.length++; - } - return out; - }; - - // Polyfill comb - if (!Math.imul) { - comb10MulTo = smallMulTo; - } - - function bigMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - out.length = self.length + num.length; - - var carry = 0; - var hncarry = 0; - for (var k = 0; k < out.length - 1; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = hncarry; - hncarry = 0; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = k - j; - var a = self.words[i] | 0; - var b = num.words[j] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; - lo = (lo + rword) | 0; - rword = lo & 0x3ffffff; - ncarry = (ncarry + (lo >>> 26)) | 0; - - hncarry += ncarry >>> 26; - ncarry &= 0x3ffffff; - } - out.words[k] = rword; - carry = ncarry; - ncarry = hncarry; - } - if (carry !== 0) { - out.words[k] = carry; - } else { - out.length--; - } - - return out.strip(); - } - - function jumboMulTo (self, num, out) { - var fftm = new FFTM(); - return fftm.mulp(self, num, out); - } - - BN.prototype.mulTo = function mulTo (num, out) { - var res; - var len = this.length + num.length; - if (this.length === 10 && num.length === 10) { - res = comb10MulTo(this, num, out); - } else if (len < 63) { - res = smallMulTo(this, num, out); - } else if (len < 1024) { - res = bigMulTo(this, num, out); - } else { - res = jumboMulTo(this, num, out); - } - - return res; - }; - - // Cooley-Tukey algorithm for FFT - // slightly revisited to rely on looping instead of recursion - - function FFTM (x, y) { - this.x = x; - this.y = y; - } - - FFTM.prototype.makeRBT = function makeRBT (N) { - var t = new Array(N); - var l = BN.prototype._countBits(N) - 1; - for (var i = 0; i < N; i++) { - t[i] = this.revBin(i, l, N); - } - - return t; - }; - - // Returns binary-reversed representation of `x` - FFTM.prototype.revBin = function revBin (x, l, N) { - if (x === 0 || x === N - 1) return x; - - var rb = 0; - for (var i = 0; i < l; i++) { - rb |= (x & 1) << (l - i - 1); - x >>= 1; - } - - return rb; - }; - - // Performs "tweedling" phase, therefore 'emulating' - // behaviour of the recursive algorithm - FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { - for (var i = 0; i < N; i++) { - rtws[i] = rws[rbt[i]]; - itws[i] = iws[rbt[i]]; - } - }; - - FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { - this.permute(rbt, rws, iws, rtws, itws, N); - - for (var s = 1; s < N; s <<= 1) { - var l = s << 1; - - var rtwdf = Math.cos(2 * Math.PI / l); - var itwdf = Math.sin(2 * Math.PI / l); - - for (var p = 0; p < N; p += l) { - var rtwdf_ = rtwdf; - var itwdf_ = itwdf; - - for (var j = 0; j < s; j++) { - var re = rtws[p + j]; - var ie = itws[p + j]; - - var ro = rtws[p + j + s]; - var io = itws[p + j + s]; - - var rx = rtwdf_ * ro - itwdf_ * io; - - io = rtwdf_ * io + itwdf_ * ro; - ro = rx; - - rtws[p + j] = re + ro; - itws[p + j] = ie + io; - - rtws[p + j + s] = re - ro; - itws[p + j + s] = ie - io; - - /* jshint maxdepth : false */ - if (j !== l) { - rx = rtwdf * rtwdf_ - itwdf * itwdf_; - - itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; - rtwdf_ = rx; - } - } - } - } - }; - - FFTM.prototype.guessLen13b = function guessLen13b (n, m) { - var N = Math.max(m, n) | 1; - var odd = N & 1; - var i = 0; - for (N = N / 2 | 0; N; N = N >>> 1) { - i++; - } - - return 1 << i + 1 + odd; - }; - - FFTM.prototype.conjugate = function conjugate (rws, iws, N) { - if (N <= 1) return; - - for (var i = 0; i < N / 2; i++) { - var t = rws[i]; - - rws[i] = rws[N - i - 1]; - rws[N - i - 1] = t; - - t = iws[i]; - - iws[i] = -iws[N - i - 1]; - iws[N - i - 1] = -t; - } - }; - - FFTM.prototype.normalize13b = function normalize13b (ws, N) { - var carry = 0; - for (var i = 0; i < N / 2; i++) { - var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + - Math.round(ws[2 * i] / N) + - carry; - - ws[i] = w & 0x3ffffff; - - if (w < 0x4000000) { - carry = 0; - } else { - carry = w / 0x4000000 | 0; - } - } - - return ws; - }; - - FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { - var carry = 0; - for (var i = 0; i < len; i++) { - carry = carry + (ws[i] | 0); - - rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; - rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; - } - - // Pad with zeroes - for (i = 2 * len; i < N; ++i) { - rws[i] = 0; - } - - assert(carry === 0); - assert((carry & ~0x1fff) === 0); - }; - - FFTM.prototype.stub = function stub (N) { - var ph = new Array(N); - for (var i = 0; i < N; i++) { - ph[i] = 0; - } - - return ph; - }; - - FFTM.prototype.mulp = function mulp (x, y, out) { - var N = 2 * this.guessLen13b(x.length, y.length); - - var rbt = this.makeRBT(N); - - var _ = this.stub(N); - - var rws = new Array(N); - var rwst = new Array(N); - var iwst = new Array(N); - - var nrws = new Array(N); - var nrwst = new Array(N); - var niwst = new Array(N); - - var rmws = out.words; - rmws.length = N; - - this.convert13b(x.words, x.length, rws, N); - this.convert13b(y.words, y.length, nrws, N); - - this.transform(rws, _, rwst, iwst, N, rbt); - this.transform(nrws, _, nrwst, niwst, N, rbt); - - for (var i = 0; i < N; i++) { - var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; - iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; - rwst[i] = rx; - } - - this.conjugate(rwst, iwst, N); - this.transform(rwst, iwst, rmws, _, N, rbt); - this.conjugate(rmws, _, N); - this.normalize13b(rmws, N); - - out.negative = x.negative ^ y.negative; - out.length = x.length + y.length; - return out.strip(); - }; - - // Multiply `this` by `num` - BN.prototype.mul = function mul (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return this.mulTo(num, out); - }; - - // Multiply employing FFT - BN.prototype.mulf = function mulf (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return jumboMulTo(this, num, out); - }; - - // In-place Multiplication - BN.prototype.imul = function imul (num) { - return this.clone().mulTo(num, this); - }; - - BN.prototype.imuln = function imuln (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - - // Carry - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = (this.words[i] | 0) * num; - var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); - carry >>= 26; - carry += (w / 0x4000000) | 0; - // NOTE: lo is 27bit maximum - carry += lo >>> 26; - this.words[i] = lo & 0x3ffffff; - } - - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - - return this; - }; - - BN.prototype.muln = function muln (num) { - return this.clone().imuln(num); - }; - - // `this` * `this` - BN.prototype.sqr = function sqr () { - return this.mul(this); - }; - - // `this` * `this` in-place - BN.prototype.isqr = function isqr () { - return this.imul(this.clone()); - }; - - // Math.pow(`this`, `num`) - BN.prototype.pow = function pow (num) { - var w = toBitArray(num); - if (w.length === 0) return new BN(1); - - // Skip leading zeroes - var res = this; - for (var i = 0; i < w.length; i++, res = res.sqr()) { - if (w[i] !== 0) break; - } - - if (++i < w.length) { - for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { - if (w[i] === 0) continue; - - res = res.mul(q); - } - } - - return res; - }; - - // Shift-left in-place - BN.prototype.iushln = function iushln (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); - var i; - - if (r !== 0) { - var carry = 0; - - for (i = 0; i < this.length; i++) { - var newCarry = this.words[i] & carryMask; - var c = ((this.words[i] | 0) - newCarry) << r; - this.words[i] = c | carry; - carry = newCarry >>> (26 - r); - } - - if (carry) { - this.words[i] = carry; - this.length++; - } - } - - if (s !== 0) { - for (i = this.length - 1; i >= 0; i--) { - this.words[i + s] = this.words[i]; - } - - for (i = 0; i < s; i++) { - this.words[i] = 0; - } - - this.length += s; - } - - return this.strip(); - }; - - BN.prototype.ishln = function ishln (bits) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushln(bits); - }; - - // Shift-right in-place - // NOTE: `hint` is a lowest bit before trailing zeroes - // NOTE: if `extended` is present - it will be filled with destroyed bits - BN.prototype.iushrn = function iushrn (bits, hint, extended) { - assert(typeof bits === 'number' && bits >= 0); - var h; - if (hint) { - h = (hint - (hint % 26)) / 26; - } else { - h = 0; - } - - var r = bits % 26; - var s = Math.min((bits - r) / 26, this.length); - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - var maskedWords = extended; - - h -= s; - h = Math.max(0, h); - - // Extended mode, copy masked part - if (maskedWords) { - for (var i = 0; i < s; i++) { - maskedWords.words[i] = this.words[i]; - } - maskedWords.length = s; - } - - if (s === 0) { - // No-op, we should not move anything at all - } else if (this.length > s) { - this.length -= s; - for (i = 0; i < this.length; i++) { - this.words[i] = this.words[i + s]; - } - } else { - this.words[0] = 0; - this.length = 1; - } - - var carry = 0; - for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { - var word = this.words[i] | 0; - this.words[i] = (carry << (26 - r)) | (word >>> r); - carry = word & mask; - } - - // Push carried bits as a mask - if (maskedWords && carry !== 0) { - maskedWords.words[maskedWords.length++] = carry; - } - - if (this.length === 0) { - this.words[0] = 0; - this.length = 1; - } - - return this.strip(); - }; - - BN.prototype.ishrn = function ishrn (bits, hint, extended) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushrn(bits, hint, extended); - }; - - // Shift-left - BN.prototype.shln = function shln (bits) { - return this.clone().ishln(bits); - }; - - BN.prototype.ushln = function ushln (bits) { - return this.clone().iushln(bits); - }; - - // Shift-right - BN.prototype.shrn = function shrn (bits) { - return this.clone().ishrn(bits); - }; - - BN.prototype.ushrn = function ushrn (bits) { - return this.clone().iushrn(bits); - }; - - // Test if n bit is set - BN.prototype.testn = function testn (bit) { - assert(typeof bit === 'number' && bit >= 0); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) return false; - - // Check bit and return - var w = this.words[s]; - - return !!(w & q); - }; - - // Return only lowers bits of number (in-place) - BN.prototype.imaskn = function imaskn (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - - assert(this.negative === 0, 'imaskn works only with positive numbers'); - - if (this.length <= s) { - return this; - } - - if (r !== 0) { - s++; - } - this.length = Math.min(s, this.length); - - if (r !== 0) { - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - this.words[this.length - 1] &= mask; - } - - return this.strip(); - }; - - // Return only lowers bits of number - BN.prototype.maskn = function maskn (bits) { - return this.clone().imaskn(bits); - }; - - // Add plain number `num` to `this` - BN.prototype.iaddn = function iaddn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.isubn(-num); - - // Possible sign change - if (this.negative !== 0) { - if (this.length === 1 && (this.words[0] | 0) < num) { - this.words[0] = num - (this.words[0] | 0); - this.negative = 0; - return this; - } - - this.negative = 0; - this.isubn(num); - this.negative = 1; - return this; - } - - // Add without checks - return this._iaddn(num); - }; - - BN.prototype._iaddn = function _iaddn (num) { - this.words[0] += num; - - // Carry - for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { - this.words[i] -= 0x4000000; - if (i === this.length - 1) { - this.words[i + 1] = 1; - } else { - this.words[i + 1]++; - } - } - this.length = Math.max(this.length, i + 1); - - return this; - }; - - // Subtract plain number `num` from `this` - BN.prototype.isubn = function isubn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.iaddn(-num); - - if (this.negative !== 0) { - this.negative = 0; - this.iaddn(num); - this.negative = 1; - return this; - } - - this.words[0] -= num; - - if (this.length === 1 && this.words[0] < 0) { - this.words[0] = -this.words[0]; - this.negative = 1; - } else { - // Carry - for (var i = 0; i < this.length && this.words[i] < 0; i++) { - this.words[i] += 0x4000000; - this.words[i + 1] -= 1; - } - } - - return this.strip(); - }; - - BN.prototype.addn = function addn (num) { - return this.clone().iaddn(num); - }; - - BN.prototype.subn = function subn (num) { - return this.clone().isubn(num); - }; - - BN.prototype.iabs = function iabs () { - this.negative = 0; - - return this; - }; - - BN.prototype.abs = function abs () { - return this.clone().iabs(); - }; - - BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { - var len = num.length + shift; - var i; - - this._expand(len); - - var w; - var carry = 0; - for (i = 0; i < num.length; i++) { - w = (this.words[i + shift] | 0) + carry; - var right = (num.words[i] | 0) * mul; - w -= right & 0x3ffffff; - carry = (w >> 26) - ((right / 0x4000000) | 0); - this.words[i + shift] = w & 0x3ffffff; - } - for (; i < this.length - shift; i++) { - w = (this.words[i + shift] | 0) + carry; - carry = w >> 26; - this.words[i + shift] = w & 0x3ffffff; - } - - if (carry === 0) return this.strip(); - - // Subtraction overflow - assert(carry === -1); - carry = 0; - for (i = 0; i < this.length; i++) { - w = -(this.words[i] | 0) + carry; - carry = w >> 26; - this.words[i] = w & 0x3ffffff; - } - this.negative = 1; - - return this.strip(); - }; - - BN.prototype._wordDiv = function _wordDiv (num, mode) { - var shift = this.length - num.length; - - var a = this.clone(); - var b = num; - - // Normalize - var bhi = b.words[b.length - 1] | 0; - var bhiBits = this._countBits(bhi); - shift = 26 - bhiBits; - if (shift !== 0) { - b = b.ushln(shift); - a.iushln(shift); - bhi = b.words[b.length - 1] | 0; - } - - // Initialize quotient - var m = a.length - b.length; - var q; - - if (mode !== 'mod') { - q = new BN(null); - q.length = m + 1; - q.words = new Array(q.length); - for (var i = 0; i < q.length; i++) { - q.words[i] = 0; - } - } - - var diff = a.clone()._ishlnsubmul(b, 1, m); - if (diff.negative === 0) { - a = diff; - if (q) { - q.words[m] = 1; - } - } - - for (var j = m - 1; j >= 0; j--) { - var qj = (a.words[b.length + j] | 0) * 0x4000000 + - (a.words[b.length + j - 1] | 0); - - // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max - // (0x7ffffff) - qj = Math.min((qj / bhi) | 0, 0x3ffffff); - - a._ishlnsubmul(b, qj, j); - while (a.negative !== 0) { - qj--; - a.negative = 0; - a._ishlnsubmul(b, 1, j); - if (!a.isZero()) { - a.negative ^= 1; - } - } - if (q) { - q.words[j] = qj; - } - } - if (q) { - q.strip(); - } - a.strip(); - - // Denormalize - if (mode !== 'div' && shift !== 0) { - a.iushrn(shift); - } - - return { - div: q || null, - mod: a - }; - }; - - // NOTE: 1) `mode` can be set to `mod` to request mod only, - // to `div` to request div only, or be absent to - // request both div & mod - // 2) `positive` is true if unsigned mod is requested - BN.prototype.divmod = function divmod (num, mode, positive) { - assert(!num.isZero()); - - if (this.isZero()) { - return { - div: new BN(0), - mod: new BN(0) - }; - } - - var div, mod, res; - if (this.negative !== 0 && num.negative === 0) { - res = this.neg().divmod(num, mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.iadd(num); - } - } - - return { - div: div, - mod: mod - }; - } - - if (this.negative === 0 && num.negative !== 0) { - res = this.divmod(num.neg(), mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - return { - div: div, - mod: res.mod - }; - } - - if ((this.negative & num.negative) !== 0) { - res = this.neg().divmod(num.neg(), mode); - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.isub(num); - } - } - - return { - div: res.div, - mod: mod - }; - } - - // Both numbers are positive at this point - - // Strip both numbers to approximate shift value - if (num.length > this.length || this.cmp(num) < 0) { - return { - div: new BN(0), - mod: this - }; - } - - // Very short reduction - if (num.length === 1) { - if (mode === 'div') { - return { - div: this.divn(num.words[0]), - mod: null - }; - } - - if (mode === 'mod') { - return { - div: null, - mod: new BN(this.modn(num.words[0])) - }; - } - - return { - div: this.divn(num.words[0]), - mod: new BN(this.modn(num.words[0])) - }; - } - - return this._wordDiv(num, mode); - }; - - // Find `this` / `num` - BN.prototype.div = function div (num) { - return this.divmod(num, 'div', false).div; - }; - - // Find `this` % `num` - BN.prototype.mod = function mod (num) { - return this.divmod(num, 'mod', false).mod; - }; - - BN.prototype.umod = function umod (num) { - return this.divmod(num, 'mod', true).mod; - }; - - // Find Round(`this` / `num`) - BN.prototype.divRound = function divRound (num) { - var dm = this.divmod(num); - - // Fast case - exact division - if (dm.mod.isZero()) return dm.div; - - var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; - - var half = num.ushrn(1); - var r2 = num.andln(1); - var cmp = mod.cmp(half); - - // Round down - if (cmp < 0 || r2 === 1 && cmp === 0) return dm.div; - - // Round up - return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); - }; - - BN.prototype.modn = function modn (num) { - assert(num <= 0x3ffffff); - var p = (1 << 26) % num; - - var acc = 0; - for (var i = this.length - 1; i >= 0; i--) { - acc = (p * acc + (this.words[i] | 0)) % num; - } - - return acc; - }; - - // In-place division by number - BN.prototype.idivn = function idivn (num) { - assert(num <= 0x3ffffff); - - var carry = 0; - for (var i = this.length - 1; i >= 0; i--) { - var w = (this.words[i] | 0) + carry * 0x4000000; - this.words[i] = (w / num) | 0; - carry = w % num; - } - - return this.strip(); - }; - - BN.prototype.divn = function divn (num) { - return this.clone().idivn(num); - }; - - BN.prototype.egcd = function egcd (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var x = this; - var y = p.clone(); - - if (x.negative !== 0) { - x = x.umod(p); - } else { - x = x.clone(); - } - - // A * x + B * y = x - var A = new BN(1); - var B = new BN(0); - - // C * x + D * y = y - var C = new BN(0); - var D = new BN(1); - - var g = 0; - - while (x.isEven() && y.isEven()) { - x.iushrn(1); - y.iushrn(1); - ++g; - } - - var yp = y.clone(); - var xp = x.clone(); - - while (!x.isZero()) { - for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - x.iushrn(i); - while (i-- > 0) { - if (A.isOdd() || B.isOdd()) { - A.iadd(yp); - B.isub(xp); - } - - A.iushrn(1); - B.iushrn(1); - } - } - - for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - y.iushrn(j); - while (j-- > 0) { - if (C.isOdd() || D.isOdd()) { - C.iadd(yp); - D.isub(xp); - } - - C.iushrn(1); - D.iushrn(1); - } - } - - if (x.cmp(y) >= 0) { - x.isub(y); - A.isub(C); - B.isub(D); - } else { - y.isub(x); - C.isub(A); - D.isub(B); - } - } - - return { - a: C, - b: D, - gcd: y.iushln(g) - }; - }; - - // This is reduced incarnation of the binary EEA - // above, designated to invert members of the - // _prime_ fields F(p) at a maximal speed - BN.prototype._invmp = function _invmp (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var a = this; - var b = p.clone(); - - if (a.negative !== 0) { - a = a.umod(p); - } else { - a = a.clone(); - } - - var x1 = new BN(1); - var x2 = new BN(0); - - var delta = b.clone(); - - while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { - for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - a.iushrn(i); - while (i-- > 0) { - if (x1.isOdd()) { - x1.iadd(delta); - } - - x1.iushrn(1); - } - } - - for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - b.iushrn(j); - while (j-- > 0) { - if (x2.isOdd()) { - x2.iadd(delta); - } - - x2.iushrn(1); - } - } - - if (a.cmp(b) >= 0) { - a.isub(b); - x1.isub(x2); - } else { - b.isub(a); - x2.isub(x1); - } - } - - var res; - if (a.cmpn(1) === 0) { - res = x1; - } else { - res = x2; - } - - if (res.cmpn(0) < 0) { - res.iadd(p); - } - - return res; - }; - - BN.prototype.gcd = function gcd (num) { - if (this.isZero()) return num.abs(); - if (num.isZero()) return this.abs(); - - var a = this.clone(); - var b = num.clone(); - a.negative = 0; - b.negative = 0; - - // Remove common factor of two - for (var shift = 0; a.isEven() && b.isEven(); shift++) { - a.iushrn(1); - b.iushrn(1); - } - - do { - while (a.isEven()) { - a.iushrn(1); - } - while (b.isEven()) { - b.iushrn(1); - } - - var r = a.cmp(b); - if (r < 0) { - // Swap `a` and `b` to make `a` always bigger than `b` - var t = a; - a = b; - b = t; - } else if (r === 0 || b.cmpn(1) === 0) { - break; - } - - a.isub(b); - } while (true); - - return b.iushln(shift); - }; - - // Invert number in the field F(num) - BN.prototype.invm = function invm (num) { - return this.egcd(num).a.umod(num); - }; - - BN.prototype.isEven = function isEven () { - return (this.words[0] & 1) === 0; - }; - - BN.prototype.isOdd = function isOdd () { - return (this.words[0] & 1) === 1; - }; - - // And first word and num - BN.prototype.andln = function andln (num) { - return this.words[0] & num; - }; - - // Increment at the bit position in-line - BN.prototype.bincn = function bincn (bit) { - assert(typeof bit === 'number'); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) { - this._expand(s + 1); - this.words[s] |= q; - return this; - } - - // Add bit and propagate, if needed - var carry = q; - for (var i = s; carry !== 0 && i < this.length; i++) { - var w = this.words[i] | 0; - w += carry; - carry = w >>> 26; - w &= 0x3ffffff; - this.words[i] = w; - } - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - return this; - }; - - BN.prototype.isZero = function isZero () { - return this.length === 1 && this.words[0] === 0; - }; - - BN.prototype.cmpn = function cmpn (num) { - var negative = num < 0; - - if (this.negative !== 0 && !negative) return -1; - if (this.negative === 0 && negative) return 1; - - this.strip(); - - var res; - if (this.length > 1) { - res = 1; - } else { - if (negative) { - num = -num; - } - - assert(num <= 0x3ffffff, 'Number is too big'); - - var w = this.words[0] | 0; - res = w === num ? 0 : w < num ? -1 : 1; - } - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Compare two numbers and return: - // 1 - if `this` > `num` - // 0 - if `this` == `num` - // -1 - if `this` < `num` - BN.prototype.cmp = function cmp (num) { - if (this.negative !== 0 && num.negative === 0) return -1; - if (this.negative === 0 && num.negative !== 0) return 1; - - var res = this.ucmp(num); - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Unsigned comparison - BN.prototype.ucmp = function ucmp (num) { - // At this point both numbers have the same sign - if (this.length > num.length) return 1; - if (this.length < num.length) return -1; - - var res = 0; - for (var i = this.length - 1; i >= 0; i--) { - var a = this.words[i] | 0; - var b = num.words[i] | 0; - - if (a === b) continue; - if (a < b) { - res = -1; - } else if (a > b) { - res = 1; - } - break; - } - return res; - }; - - BN.prototype.gtn = function gtn (num) { - return this.cmpn(num) === 1; - }; - - BN.prototype.gt = function gt (num) { - return this.cmp(num) === 1; - }; - - BN.prototype.gten = function gten (num) { - return this.cmpn(num) >= 0; - }; - - BN.prototype.gte = function gte (num) { - return this.cmp(num) >= 0; - }; - - BN.prototype.ltn = function ltn (num) { - return this.cmpn(num) === -1; - }; - - BN.prototype.lt = function lt (num) { - return this.cmp(num) === -1; - }; - - BN.prototype.lten = function lten (num) { - return this.cmpn(num) <= 0; - }; - - BN.prototype.lte = function lte (num) { - return this.cmp(num) <= 0; - }; - - BN.prototype.eqn = function eqn (num) { - return this.cmpn(num) === 0; - }; - - BN.prototype.eq = function eq (num) { - return this.cmp(num) === 0; - }; - - // - // A reduce context, could be using montgomery or something better, depending - // on the `m` itself. - // - BN.red = function red (num) { - return new Red(num); - }; - - BN.prototype.toRed = function toRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - assert(this.negative === 0, 'red works only with positives'); - return ctx.convertTo(this)._forceRed(ctx); - }; - - BN.prototype.fromRed = function fromRed () { - assert(this.red, 'fromRed works only with numbers in reduction context'); - return this.red.convertFrom(this); - }; - - BN.prototype._forceRed = function _forceRed (ctx) { - this.red = ctx; - return this; - }; - - BN.prototype.forceRed = function forceRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - return this._forceRed(ctx); - }; - - BN.prototype.redAdd = function redAdd (num) { - assert(this.red, 'redAdd works only with red numbers'); - return this.red.add(this, num); - }; - - BN.prototype.redIAdd = function redIAdd (num) { - assert(this.red, 'redIAdd works only with red numbers'); - return this.red.iadd(this, num); - }; - - BN.prototype.redSub = function redSub (num) { - assert(this.red, 'redSub works only with red numbers'); - return this.red.sub(this, num); - }; - - BN.prototype.redISub = function redISub (num) { - assert(this.red, 'redISub works only with red numbers'); - return this.red.isub(this, num); - }; - - BN.prototype.redShl = function redShl (num) { - assert(this.red, 'redShl works only with red numbers'); - return this.red.shl(this, num); - }; - - BN.prototype.redMul = function redMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.mul(this, num); - }; - - BN.prototype.redIMul = function redIMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.imul(this, num); - }; - - BN.prototype.redSqr = function redSqr () { - assert(this.red, 'redSqr works only with red numbers'); - this.red._verify1(this); - return this.red.sqr(this); - }; - - BN.prototype.redISqr = function redISqr () { - assert(this.red, 'redISqr works only with red numbers'); - this.red._verify1(this); - return this.red.isqr(this); - }; - - // Square root over p - BN.prototype.redSqrt = function redSqrt () { - assert(this.red, 'redSqrt works only with red numbers'); - this.red._verify1(this); - return this.red.sqrt(this); - }; - - BN.prototype.redInvm = function redInvm () { - assert(this.red, 'redInvm works only with red numbers'); - this.red._verify1(this); - return this.red.invm(this); - }; - - // Return negative clone of `this` % `red modulo` - BN.prototype.redNeg = function redNeg () { - assert(this.red, 'redNeg works only with red numbers'); - this.red._verify1(this); - return this.red.neg(this); - }; - - BN.prototype.redPow = function redPow (num) { - assert(this.red && !num.red, 'redPow(normalNum)'); - this.red._verify1(this); - return this.red.pow(this, num); - }; - - // Prime numbers with efficient reduction - var primes = { - k256: null, - p224: null, - p192: null, - p25519: null - }; - - // Pseudo-Mersenne prime - function MPrime (name, p) { - // P = 2 ^ N - K - this.name = name; - this.p = new BN(p, 16); - this.n = this.p.bitLength(); - this.k = new BN(1).iushln(this.n).isub(this.p); - - this.tmp = this._tmp(); - } - - MPrime.prototype._tmp = function _tmp () { - var tmp = new BN(null); - tmp.words = new Array(Math.ceil(this.n / 13)); - return tmp; - }; - - MPrime.prototype.ireduce = function ireduce (num) { - // Assumes that `num` is less than `P^2` - // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) - var r = num; - var rlen; - - do { - this.split(r, this.tmp); - r = this.imulK(r); - r = r.iadd(this.tmp); - rlen = r.bitLength(); - } while (rlen > this.n); - - var cmp = rlen < this.n ? -1 : r.ucmp(this.p); - if (cmp === 0) { - r.words[0] = 0; - r.length = 1; - } else if (cmp > 0) { - r.isub(this.p); - } else { - if (r.strip !== undefined) { - // r is BN v4 instance - r.strip(); - } else { - // r is BN v5 instance - r._strip(); - } - } - - return r; - }; - - MPrime.prototype.split = function split (input, out) { - input.iushrn(this.n, 0, out); - }; - - MPrime.prototype.imulK = function imulK (num) { - return num.imul(this.k); - }; - - function K256 () { - MPrime.call( - this, - 'k256', - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); - } - inherits(K256, MPrime); - - K256.prototype.split = function split (input, output) { - // 256 = 9 * 26 + 22 - var mask = 0x3fffff; - - var outLen = Math.min(input.length, 9); - for (var i = 0; i < outLen; i++) { - output.words[i] = input.words[i]; - } - output.length = outLen; - - if (input.length <= 9) { - input.words[0] = 0; - input.length = 1; - return; - } - - // Shift by 9 limbs - var prev = input.words[9]; - output.words[output.length++] = prev & mask; - - for (i = 10; i < input.length; i++) { - var next = input.words[i] | 0; - input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); - prev = next; - } - prev >>>= 22; - input.words[i - 10] = prev; - if (prev === 0 && input.length > 10) { - input.length -= 10; - } else { - input.length -= 9; - } - }; - - K256.prototype.imulK = function imulK (num) { - // K = 0x1000003d1 = [ 0x40, 0x3d1 ] - num.words[num.length] = 0; - num.words[num.length + 1] = 0; - num.length += 2; - - // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 - var lo = 0; - for (var i = 0; i < num.length; i++) { - var w = num.words[i] | 0; - lo += w * 0x3d1; - num.words[i] = lo & 0x3ffffff; - lo = w * 0x40 + ((lo / 0x4000000) | 0); - } - - // Fast length reduction - if (num.words[num.length - 1] === 0) { - num.length--; - if (num.words[num.length - 1] === 0) { - num.length--; - } - } - return num; - }; - - function P224 () { - MPrime.call( - this, - 'p224', - 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); - } - inherits(P224, MPrime); - - function P192 () { - MPrime.call( - this, - 'p192', - 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); - } - inherits(P192, MPrime); - - function P25519 () { - // 2 ^ 255 - 19 - MPrime.call( - this, - '25519', - '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); - } - inherits(P25519, MPrime); - - P25519.prototype.imulK = function imulK (num) { - // K = 0x13 - var carry = 0; - for (var i = 0; i < num.length; i++) { - var hi = (num.words[i] | 0) * 0x13 + carry; - var lo = hi & 0x3ffffff; - hi >>>= 26; - - num.words[i] = lo; - carry = hi; - } - if (carry !== 0) { - num.words[num.length++] = carry; - } - return num; - }; - - // Exported mostly for testing purposes, use plain name instead - BN._prime = function prime (name) { - // Cached version of prime - if (primes[name]) return primes[name]; - - var prime; - if (name === 'k256') { - prime = new K256(); - } else if (name === 'p224') { - prime = new P224(); - } else if (name === 'p192') { - prime = new P192(); - } else if (name === 'p25519') { - prime = new P25519(); - } else { - throw new Error('Unknown prime ' + name); - } - primes[name] = prime; - - return prime; - }; - - // - // Base reduction engine - // - function Red (m) { - if (typeof m === 'string') { - var prime = BN._prime(m); - this.m = prime.p; - this.prime = prime; - } else { - assert(m.gtn(1), 'modulus must be greater than 1'); - this.m = m; - this.prime = null; - } - } - - Red.prototype._verify1 = function _verify1 (a) { - assert(a.negative === 0, 'red works only with positives'); - assert(a.red, 'red works only with red numbers'); - }; - - Red.prototype._verify2 = function _verify2 (a, b) { - assert((a.negative | b.negative) === 0, 'red works only with positives'); - assert(a.red && a.red === b.red, - 'red works only with red numbers'); - }; - - Red.prototype.imod = function imod (a) { - if (this.prime) return this.prime.ireduce(a)._forceRed(this); - return a.umod(this.m)._forceRed(this); - }; - - Red.prototype.neg = function neg (a) { - if (a.isZero()) { - return a.clone(); - } - - return this.m.sub(a)._forceRed(this); - }; - - Red.prototype.add = function add (a, b) { - this._verify2(a, b); - - var res = a.add(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.iadd = function iadd (a, b) { - this._verify2(a, b); - - var res = a.iadd(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res; - }; - - Red.prototype.sub = function sub (a, b) { - this._verify2(a, b); - - var res = a.sub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.isub = function isub (a, b) { - this._verify2(a, b); - - var res = a.isub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res; - }; - - Red.prototype.shl = function shl (a, num) { - this._verify1(a); - return this.imod(a.ushln(num)); - }; - - Red.prototype.imul = function imul (a, b) { - this._verify2(a, b); - return this.imod(a.imul(b)); - }; - - Red.prototype.mul = function mul (a, b) { - this._verify2(a, b); - return this.imod(a.mul(b)); - }; - - Red.prototype.isqr = function isqr (a) { - return this.imul(a, a.clone()); - }; - - Red.prototype.sqr = function sqr (a) { - return this.mul(a, a); - }; - - Red.prototype.sqrt = function sqrt (a) { - if (a.isZero()) return a.clone(); - - var mod3 = this.m.andln(3); - assert(mod3 % 2 === 1); - - // Fast case - if (mod3 === 3) { - var pow = this.m.add(new BN(1)).iushrn(2); - return this.pow(a, pow); - } - - // Tonelli-Shanks algorithm (Totally unoptimized and slow) - // - // Find Q and S, that Q * 2 ^ S = (P - 1) - var q = this.m.subn(1); - var s = 0; - while (!q.isZero() && q.andln(1) === 0) { - s++; - q.iushrn(1); - } - assert(!q.isZero()); - - var one = new BN(1).toRed(this); - var nOne = one.redNeg(); - - // Find quadratic non-residue - // NOTE: Max is such because of generalized Riemann hypothesis. - var lpow = this.m.subn(1).iushrn(1); - var z = this.m.bitLength(); - z = new BN(2 * z * z).toRed(this); - - while (this.pow(z, lpow).cmp(nOne) !== 0) { - z.redIAdd(nOne); - } - - var c = this.pow(z, q); - var r = this.pow(a, q.addn(1).iushrn(1)); - var t = this.pow(a, q); - var m = s; - while (t.cmp(one) !== 0) { - var tmp = t; - for (var i = 0; tmp.cmp(one) !== 0; i++) { - tmp = tmp.redSqr(); - } - assert(i < m); - var b = this.pow(c, new BN(1).iushln(m - i - 1)); - - r = r.redMul(b); - c = b.redSqr(); - t = t.redMul(c); - m = i; - } - - return r; - }; - - Red.prototype.invm = function invm (a) { - var inv = a._invmp(this.m); - if (inv.negative !== 0) { - inv.negative = 0; - return this.imod(inv).redNeg(); - } else { - return this.imod(inv); - } - }; - - Red.prototype.pow = function pow (a, num) { - if (num.isZero()) return new BN(1).toRed(this); - if (num.cmpn(1) === 0) return a.clone(); - - var windowSize = 4; - var wnd = new Array(1 << windowSize); - wnd[0] = new BN(1).toRed(this); - wnd[1] = a; - for (var i = 2; i < wnd.length; i++) { - wnd[i] = this.mul(wnd[i - 1], a); - } - - var res = wnd[0]; - var current = 0; - var currentLen = 0; - var start = num.bitLength() % 26; - if (start === 0) { - start = 26; - } - - for (i = num.length - 1; i >= 0; i--) { - var word = num.words[i]; - for (var j = start - 1; j >= 0; j--) { - var bit = (word >> j) & 1; - if (res !== wnd[0]) { - res = this.sqr(res); - } - - if (bit === 0 && current === 0) { - currentLen = 0; - continue; - } - - current <<= 1; - current |= bit; - currentLen++; - if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; - - res = this.mul(res, wnd[current]); - currentLen = 0; - current = 0; - } - start = 26; - } - - return res; - }; - - Red.prototype.convertTo = function convertTo (num) { - var r = num.umod(this.m); - - return r === num ? r.clone() : r; - }; - - Red.prototype.convertFrom = function convertFrom (num) { - var res = num.clone(); - res.red = null; - return res; - }; - - // - // Montgomery method engine - // - - BN.mont = function mont (num) { - return new Mont(num); - }; - - function Mont (m) { - Red.call(this, m); - - this.shift = this.m.bitLength(); - if (this.shift % 26 !== 0) { - this.shift += 26 - (this.shift % 26); - } - - this.r = new BN(1).iushln(this.shift); - this.r2 = this.imod(this.r.sqr()); - this.rinv = this.r._invmp(this.m); - - this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); - this.minv = this.minv.umod(this.r); - this.minv = this.r.sub(this.minv); - } - inherits(Mont, Red); - - Mont.prototype.convertTo = function convertTo (num) { - return this.imod(num.ushln(this.shift)); - }; - - Mont.prototype.convertFrom = function convertFrom (num) { - var r = this.imod(num.mul(this.rinv)); - r.red = null; - return r; - }; - - Mont.prototype.imul = function imul (a, b) { - if (a.isZero() || b.isZero()) { - a.words[0] = 0; - a.length = 1; - return a; - } - - var t = a.imul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.mul = function mul (a, b) { - if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); - - var t = a.mul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.invm = function invm (a) { - // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R - var res = this.imod(a._invmp(this.m).mul(this.r2)); - return res._forceRed(this); - }; -})(typeof module === 'undefined' || module, this); - -},{"buffer":20}],19:[function(require,module,exports){ -var r; - -module.exports = function rand(len) { - if (!r) - r = new Rand(null); - - return r.generate(len); -}; - -function Rand(rand) { - this.rand = rand; -} -module.exports.Rand = Rand; - -Rand.prototype.generate = function generate(len) { - return this._rand(len); -}; - -// Emulate crypto API using randy -Rand.prototype._rand = function _rand(n) { - if (this.rand.getBytes) - return this.rand.getBytes(n); - - var res = new Uint8Array(n); - for (var i = 0; i < res.length; i++) - res[i] = this.rand.getByte(); - return res; -}; - -if (typeof self === 'object') { - if (self.crypto && self.crypto.getRandomValues) { - // Modern browsers - Rand.prototype._rand = function _rand(n) { - var arr = new Uint8Array(n); - self.crypto.getRandomValues(arr); - return arr; - }; - } else if (self.msCrypto && self.msCrypto.getRandomValues) { - // IE - Rand.prototype._rand = function _rand(n) { - var arr = new Uint8Array(n); - self.msCrypto.getRandomValues(arr); - return arr; - }; - - // Safari's WebWorkers do not have `crypto` - } else if (typeof window === 'object') { - // Old junk - Rand.prototype._rand = function() { - throw new Error('Not implemented yet'); - }; - } -} else { - // Node.js or Web worker with no crypto support - try { - var crypto = require('crypto'); - if (typeof crypto.randomBytes !== 'function') - throw new Error('Not supported'); - - Rand.prototype._rand = function _rand(n) { - return crypto.randomBytes(n); - }; - } catch (e) { - } -} - -},{"crypto":20}],20:[function(require,module,exports){ - -},{}],21:[function(require,module,exports){ -// based on the aes implimentation in triple sec -// https://github.com/keybase/triplesec -// which is in turn based on the one from crypto-js -// https://code.google.com/p/crypto-js/ - -var Buffer = require('safe-buffer').Buffer - -function asUInt32Array (buf) { - if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf) - - var len = (buf.length / 4) | 0 - var out = new Array(len) - - for (var i = 0; i < len; i++) { - out[i] = buf.readUInt32BE(i * 4) - } - - return out -} - -function scrubVec (v) { - for (var i = 0; i < v.length; v++) { - v[i] = 0 - } -} - -function cryptBlock (M, keySchedule, SUB_MIX, SBOX, nRounds) { - var SUB_MIX0 = SUB_MIX[0] - var SUB_MIX1 = SUB_MIX[1] - var SUB_MIX2 = SUB_MIX[2] - var SUB_MIX3 = SUB_MIX[3] - - var s0 = M[0] ^ keySchedule[0] - var s1 = M[1] ^ keySchedule[1] - var s2 = M[2] ^ keySchedule[2] - var s3 = M[3] ^ keySchedule[3] - var t0, t1, t2, t3 - var ksRow = 4 - - for (var round = 1; round < nRounds; round++) { - t0 = SUB_MIX0[s0 >>> 24] ^ SUB_MIX1[(s1 >>> 16) & 0xff] ^ SUB_MIX2[(s2 >>> 8) & 0xff] ^ SUB_MIX3[s3 & 0xff] ^ keySchedule[ksRow++] - t1 = SUB_MIX0[s1 >>> 24] ^ SUB_MIX1[(s2 >>> 16) & 0xff] ^ SUB_MIX2[(s3 >>> 8) & 0xff] ^ SUB_MIX3[s0 & 0xff] ^ keySchedule[ksRow++] - t2 = SUB_MIX0[s2 >>> 24] ^ SUB_MIX1[(s3 >>> 16) & 0xff] ^ SUB_MIX2[(s0 >>> 8) & 0xff] ^ SUB_MIX3[s1 & 0xff] ^ keySchedule[ksRow++] - t3 = SUB_MIX0[s3 >>> 24] ^ SUB_MIX1[(s0 >>> 16) & 0xff] ^ SUB_MIX2[(s1 >>> 8) & 0xff] ^ SUB_MIX3[s2 & 0xff] ^ keySchedule[ksRow++] - s0 = t0 - s1 = t1 - s2 = t2 - s3 = t3 - } - - t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++] - t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++] - t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++] - t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++] - t0 = t0 >>> 0 - t1 = t1 >>> 0 - t2 = t2 >>> 0 - t3 = t3 >>> 0 - - return [t0, t1, t2, t3] -} - -// AES constants -var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36] -var G = (function () { - // Compute double table - var d = new Array(256) - for (var j = 0; j < 256; j++) { - if (j < 128) { - d[j] = j << 1 - } else { - d[j] = (j << 1) ^ 0x11b - } - } - - var SBOX = [] - var INV_SBOX = [] - var SUB_MIX = [[], [], [], []] - var INV_SUB_MIX = [[], [], [], []] - - // Walk GF(2^8) - var x = 0 - var xi = 0 - for (var i = 0; i < 256; ++i) { - // Compute sbox - var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4) - sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63 - SBOX[x] = sx - INV_SBOX[sx] = x - - // Compute multiplication - var x2 = d[x] - var x4 = d[x2] - var x8 = d[x4] - - // Compute sub bytes, mix columns tables - var t = (d[sx] * 0x101) ^ (sx * 0x1010100) - SUB_MIX[0][x] = (t << 24) | (t >>> 8) - SUB_MIX[1][x] = (t << 16) | (t >>> 16) - SUB_MIX[2][x] = (t << 8) | (t >>> 24) - SUB_MIX[3][x] = t - - // Compute inv sub bytes, inv mix columns tables - t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100) - INV_SUB_MIX[0][sx] = (t << 24) | (t >>> 8) - INV_SUB_MIX[1][sx] = (t << 16) | (t >>> 16) - INV_SUB_MIX[2][sx] = (t << 8) | (t >>> 24) - INV_SUB_MIX[3][sx] = t - - if (x === 0) { - x = xi = 1 - } else { - x = x2 ^ d[d[d[x8 ^ x2]]] - xi ^= d[d[xi]] - } - } - - return { - SBOX: SBOX, - INV_SBOX: INV_SBOX, - SUB_MIX: SUB_MIX, - INV_SUB_MIX: INV_SUB_MIX - } -})() - -function AES (key) { - this._key = asUInt32Array(key) - this._reset() -} - -AES.blockSize = 4 * 4 -AES.keySize = 256 / 8 -AES.prototype.blockSize = AES.blockSize -AES.prototype.keySize = AES.keySize -AES.prototype._reset = function () { - var keyWords = this._key - var keySize = keyWords.length - var nRounds = keySize + 6 - var ksRows = (nRounds + 1) * 4 - - var keySchedule = [] - for (var k = 0; k < keySize; k++) { - keySchedule[k] = keyWords[k] - } - - for (k = keySize; k < ksRows; k++) { - var t = keySchedule[k - 1] - - if (k % keySize === 0) { - t = (t << 8) | (t >>> 24) - t = - (G.SBOX[t >>> 24] << 24) | - (G.SBOX[(t >>> 16) & 0xff] << 16) | - (G.SBOX[(t >>> 8) & 0xff] << 8) | - (G.SBOX[t & 0xff]) - - t ^= RCON[(k / keySize) | 0] << 24 - } else if (keySize > 6 && k % keySize === 4) { - t = - (G.SBOX[t >>> 24] << 24) | - (G.SBOX[(t >>> 16) & 0xff] << 16) | - (G.SBOX[(t >>> 8) & 0xff] << 8) | - (G.SBOX[t & 0xff]) - } - - keySchedule[k] = keySchedule[k - keySize] ^ t - } - - var invKeySchedule = [] - for (var ik = 0; ik < ksRows; ik++) { - var ksR = ksRows - ik - var tt = keySchedule[ksR - (ik % 4 ? 0 : 4)] - - if (ik < 4 || ksR <= 4) { - invKeySchedule[ik] = tt - } else { - invKeySchedule[ik] = - G.INV_SUB_MIX[0][G.SBOX[tt >>> 24]] ^ - G.INV_SUB_MIX[1][G.SBOX[(tt >>> 16) & 0xff]] ^ - G.INV_SUB_MIX[2][G.SBOX[(tt >>> 8) & 0xff]] ^ - G.INV_SUB_MIX[3][G.SBOX[tt & 0xff]] - } - } - - this._nRounds = nRounds - this._keySchedule = keySchedule - this._invKeySchedule = invKeySchedule -} - -AES.prototype.encryptBlockRaw = function (M) { - M = asUInt32Array(M) - return cryptBlock(M, this._keySchedule, G.SUB_MIX, G.SBOX, this._nRounds) -} - -AES.prototype.encryptBlock = function (M) { - var out = this.encryptBlockRaw(M) - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0], 0) - buf.writeUInt32BE(out[1], 4) - buf.writeUInt32BE(out[2], 8) - buf.writeUInt32BE(out[3], 12) - return buf -} - -AES.prototype.decryptBlock = function (M) { - M = asUInt32Array(M) - - // swap - var m1 = M[1] - M[1] = M[3] - M[3] = m1 - - var out = cryptBlock(M, this._invKeySchedule, G.INV_SUB_MIX, G.INV_SBOX, this._nRounds) - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0], 0) - buf.writeUInt32BE(out[3], 4) - buf.writeUInt32BE(out[2], 8) - buf.writeUInt32BE(out[1], 12) - return buf -} - -AES.prototype.scrub = function () { - scrubVec(this._keySchedule) - scrubVec(this._invKeySchedule) - scrubVec(this._key) -} - -module.exports.AES = AES - -},{"safe-buffer":250}],22:[function(require,module,exports){ -var aes = require('./aes') -var Buffer = require('safe-buffer').Buffer -var Transform = require('cipher-base') -var inherits = require('inherits') -var GHASH = require('./ghash') -var xor = require('buffer-xor') -var incr32 = require('./incr32') - -function xorTest (a, b) { - var out = 0 - if (a.length !== b.length) out++ - - var len = Math.min(a.length, b.length) - for (var i = 0; i < len; ++i) { - out += (a[i] ^ b[i]) - } - - return out -} - -function calcIv (self, iv, ck) { - if (iv.length === 12) { - self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])]) - return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])]) - } - var ghash = new GHASH(ck) - var len = iv.length - var toPad = len % 16 - ghash.update(iv) - if (toPad) { - toPad = 16 - toPad - ghash.update(Buffer.alloc(toPad, 0)) - } - ghash.update(Buffer.alloc(8, 0)) - var ivBits = len * 8 - var tail = Buffer.alloc(8) - tail.writeUIntBE(ivBits, 0, 8) - ghash.update(tail) - self._finID = ghash.state - var out = Buffer.from(self._finID) - incr32(out) - return out -} -function StreamCipher (mode, key, iv, decrypt) { - Transform.call(this) - - var h = Buffer.alloc(4, 0) - - this._cipher = new aes.AES(key) - var ck = this._cipher.encryptBlock(h) - this._ghash = new GHASH(ck) - iv = calcIv(this, iv, ck) - - this._prev = Buffer.from(iv) - this._cache = Buffer.allocUnsafe(0) - this._secCache = Buffer.allocUnsafe(0) - this._decrypt = decrypt - this._alen = 0 - this._len = 0 - this._mode = mode - - this._authTag = null - this._called = false -} - -inherits(StreamCipher, Transform) - -StreamCipher.prototype._update = function (chunk) { - if (!this._called && this._alen) { - var rump = 16 - (this._alen % 16) - if (rump < 16) { - rump = Buffer.alloc(rump, 0) - this._ghash.update(rump) - } - } - - this._called = true - var out = this._mode.encrypt(this, chunk) - if (this._decrypt) { - this._ghash.update(chunk) - } else { - this._ghash.update(out) - } - this._len += chunk.length - return out -} - -StreamCipher.prototype._final = function () { - if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data') - - var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID)) - if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data') - - this._authTag = tag - this._cipher.scrub() -} - -StreamCipher.prototype.getAuthTag = function getAuthTag () { - if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state') - - return this._authTag -} - -StreamCipher.prototype.setAuthTag = function setAuthTag (tag) { - if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state') - - this._authTag = tag -} - -StreamCipher.prototype.setAAD = function setAAD (buf) { - if (this._called) throw new Error('Attempting to set AAD in unsupported state') - - this._ghash.update(buf) - this._alen += buf.length -} - -module.exports = StreamCipher - -},{"./aes":21,"./ghash":26,"./incr32":27,"buffer-xor":67,"cipher-base":71,"inherits":146,"safe-buffer":250}],23:[function(require,module,exports){ -var ciphers = require('./encrypter') -var deciphers = require('./decrypter') -var modes = require('./modes/list.json') - -function getCiphers () { - return Object.keys(modes) -} - -exports.createCipher = exports.Cipher = ciphers.createCipher -exports.createCipheriv = exports.Cipheriv = ciphers.createCipheriv -exports.createDecipher = exports.Decipher = deciphers.createDecipher -exports.createDecipheriv = exports.Decipheriv = deciphers.createDecipheriv -exports.listCiphers = exports.getCiphers = getCiphers - -},{"./decrypter":24,"./encrypter":25,"./modes/list.json":35}],24:[function(require,module,exports){ -var AuthCipher = require('./authCipher') -var Buffer = require('safe-buffer').Buffer -var MODES = require('./modes') -var StreamCipher = require('./streamCipher') -var Transform = require('cipher-base') -var aes = require('./aes') -var ebtk = require('evp_bytestokey') -var inherits = require('inherits') - -function Decipher (mode, key, iv) { - Transform.call(this) - - this._cache = new Splitter() - this._last = void 0 - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._mode = mode - this._autopadding = true -} - -inherits(Decipher, Transform) - -Decipher.prototype._update = function (data) { - this._cache.add(data) - var chunk - var thing - var out = [] - while ((chunk = this._cache.get(this._autopadding))) { - thing = this._mode.decrypt(this, chunk) - out.push(thing) - } - return Buffer.concat(out) -} - -Decipher.prototype._final = function () { - var chunk = this._cache.flush() - if (this._autopadding) { - return unpad(this._mode.decrypt(this, chunk)) - } else if (chunk) { - throw new Error('data not multiple of block length') - } -} - -Decipher.prototype.setAutoPadding = function (setTo) { - this._autopadding = !!setTo - return this -} - -function Splitter () { - this.cache = Buffer.allocUnsafe(0) -} - -Splitter.prototype.add = function (data) { - this.cache = Buffer.concat([this.cache, data]) -} - -Splitter.prototype.get = function (autoPadding) { - var out - if (autoPadding) { - if (this.cache.length > 16) { - out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - } else { - if (this.cache.length >= 16) { - out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - } - - return null -} - -Splitter.prototype.flush = function () { - if (this.cache.length) return this.cache -} - -function unpad (last) { - var padded = last[15] - if (padded < 1 || padded > 16) { - throw new Error('unable to decrypt data') - } - var i = -1 - while (++i < padded) { - if (last[(i + (16 - padded))] !== padded) { - throw new Error('unable to decrypt data') - } - } - if (padded === 16) return - - return last.slice(0, 16 - padded) -} - -function createDecipheriv (suite, password, iv) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - if (typeof iv === 'string') iv = Buffer.from(iv) - if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) - - if (typeof password === 'string') password = Buffer.from(password) - if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) - - if (config.type === 'stream') { - return new StreamCipher(config.module, password, iv, true) - } else if (config.type === 'auth') { - return new AuthCipher(config.module, password, iv, true) - } - - return new Decipher(config.module, password, iv) -} - -function createDecipher (suite, password) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - var keys = ebtk(password, false, config.key, config.iv) - return createDecipheriv(suite, keys.key, keys.iv) -} - -exports.createDecipher = createDecipher -exports.createDecipheriv = createDecipheriv - -},{"./aes":21,"./authCipher":22,"./modes":34,"./streamCipher":37,"cipher-base":71,"evp_bytestokey":106,"inherits":146,"safe-buffer":250}],25:[function(require,module,exports){ -var MODES = require('./modes') -var AuthCipher = require('./authCipher') -var Buffer = require('safe-buffer').Buffer -var StreamCipher = require('./streamCipher') -var Transform = require('cipher-base') -var aes = require('./aes') -var ebtk = require('evp_bytestokey') -var inherits = require('inherits') - -function Cipher (mode, key, iv) { - Transform.call(this) - - this._cache = new Splitter() - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._mode = mode - this._autopadding = true -} - -inherits(Cipher, Transform) - -Cipher.prototype._update = function (data) { - this._cache.add(data) - var chunk - var thing - var out = [] - - while ((chunk = this._cache.get())) { - thing = this._mode.encrypt(this, chunk) - out.push(thing) - } - - return Buffer.concat(out) -} - -var PADDING = Buffer.alloc(16, 0x10) - -Cipher.prototype._final = function () { - var chunk = this._cache.flush() - if (this._autopadding) { - chunk = this._mode.encrypt(this, chunk) - this._cipher.scrub() - return chunk - } - - if (!chunk.equals(PADDING)) { - this._cipher.scrub() - throw new Error('data not multiple of block length') - } -} - -Cipher.prototype.setAutoPadding = function (setTo) { - this._autopadding = !!setTo - return this -} - -function Splitter () { - this.cache = Buffer.allocUnsafe(0) -} - -Splitter.prototype.add = function (data) { - this.cache = Buffer.concat([this.cache, data]) -} - -Splitter.prototype.get = function () { - if (this.cache.length > 15) { - var out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - return null -} - -Splitter.prototype.flush = function () { - var len = 16 - this.cache.length - var padBuff = Buffer.allocUnsafe(len) - - var i = -1 - while (++i < len) { - padBuff.writeUInt8(len, i) - } - - return Buffer.concat([this.cache, padBuff]) -} - -function createCipheriv (suite, password, iv) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - if (typeof password === 'string') password = Buffer.from(password) - if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) - - if (typeof iv === 'string') iv = Buffer.from(iv) - if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) - - if (config.type === 'stream') { - return new StreamCipher(config.module, password, iv) - } else if (config.type === 'auth') { - return new AuthCipher(config.module, password, iv) - } - - return new Cipher(config.module, password, iv) -} - -function createCipher (suite, password) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - var keys = ebtk(password, false, config.key, config.iv) - return createCipheriv(suite, keys.key, keys.iv) -} - -exports.createCipheriv = createCipheriv -exports.createCipher = createCipher - -},{"./aes":21,"./authCipher":22,"./modes":34,"./streamCipher":37,"cipher-base":71,"evp_bytestokey":106,"inherits":146,"safe-buffer":250}],26:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var ZEROES = Buffer.alloc(16, 0) - -function toArray (buf) { - return [ - buf.readUInt32BE(0), - buf.readUInt32BE(4), - buf.readUInt32BE(8), - buf.readUInt32BE(12) - ] -} - -function fromArray (out) { - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0] >>> 0, 0) - buf.writeUInt32BE(out[1] >>> 0, 4) - buf.writeUInt32BE(out[2] >>> 0, 8) - buf.writeUInt32BE(out[3] >>> 0, 12) - return buf -} - -function GHASH (key) { - this.h = key - this.state = Buffer.alloc(16, 0) - this.cache = Buffer.allocUnsafe(0) -} - -// from http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_gcm.js.html -// by Juho Vähä-Herttua -GHASH.prototype.ghash = function (block) { - var i = -1 - while (++i < block.length) { - this.state[i] ^= block[i] - } - this._multiply() -} - -GHASH.prototype._multiply = function () { - var Vi = toArray(this.h) - var Zi = [0, 0, 0, 0] - var j, xi, lsbVi - var i = -1 - while (++i < 128) { - xi = (this.state[~~(i / 8)] & (1 << (7 - (i % 8)))) !== 0 - if (xi) { - // Z_i+1 = Z_i ^ V_i - Zi[0] ^= Vi[0] - Zi[1] ^= Vi[1] - Zi[2] ^= Vi[2] - Zi[3] ^= Vi[3] - } - - // Store the value of LSB(V_i) - lsbVi = (Vi[3] & 1) !== 0 - - // V_i+1 = V_i >> 1 - for (j = 3; j > 0; j--) { - Vi[j] = (Vi[j] >>> 1) | ((Vi[j - 1] & 1) << 31) - } - Vi[0] = Vi[0] >>> 1 - - // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R - if (lsbVi) { - Vi[0] = Vi[0] ^ (0xe1 << 24) - } - } - this.state = fromArray(Zi) -} - -GHASH.prototype.update = function (buf) { - this.cache = Buffer.concat([this.cache, buf]) - var chunk - while (this.cache.length >= 16) { - chunk = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - this.ghash(chunk) - } -} - -GHASH.prototype.final = function (abl, bl) { - if (this.cache.length) { - this.ghash(Buffer.concat([this.cache, ZEROES], 16)) - } - - this.ghash(fromArray([0, abl, 0, bl])) - return this.state -} - -module.exports = GHASH - -},{"safe-buffer":250}],27:[function(require,module,exports){ -function incr32 (iv) { - var len = iv.length - var item - while (len--) { - item = iv.readUInt8(len) - if (item === 255) { - iv.writeUInt8(0, len) - } else { - item++ - iv.writeUInt8(item, len) - break - } - } -} -module.exports = incr32 - -},{}],28:[function(require,module,exports){ -var xor = require('buffer-xor') - -exports.encrypt = function (self, block) { - var data = xor(block, self._prev) - - self._prev = self._cipher.encryptBlock(data) - return self._prev -} - -exports.decrypt = function (self, block) { - var pad = self._prev - - self._prev = block - var out = self._cipher.decryptBlock(block) - - return xor(out, pad) -} - -},{"buffer-xor":67}],29:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var xor = require('buffer-xor') - -function encryptStart (self, data, decrypt) { - var len = data.length - var out = xor(data, self._cache) - self._cache = self._cache.slice(len) - self._prev = Buffer.concat([self._prev, decrypt ? data : out]) - return out -} - -exports.encrypt = function (self, data, decrypt) { - var out = Buffer.allocUnsafe(0) - var len - - while (data.length) { - if (self._cache.length === 0) { - self._cache = self._cipher.encryptBlock(self._prev) - self._prev = Buffer.allocUnsafe(0) - } - - if (self._cache.length <= data.length) { - len = self._cache.length - out = Buffer.concat([out, encryptStart(self, data.slice(0, len), decrypt)]) - data = data.slice(len) - } else { - out = Buffer.concat([out, encryptStart(self, data, decrypt)]) - break - } - } - - return out -} - -},{"buffer-xor":67,"safe-buffer":250}],30:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -function encryptByte (self, byteParam, decrypt) { - var pad - var i = -1 - var len = 8 - var out = 0 - var bit, value - while (++i < len) { - pad = self._cipher.encryptBlock(self._prev) - bit = (byteParam & (1 << (7 - i))) ? 0x80 : 0 - value = pad[0] ^ bit - out += ((value & 0x80) >> (i % 8)) - self._prev = shiftIn(self._prev, decrypt ? bit : value) - } - return out -} - -function shiftIn (buffer, value) { - var len = buffer.length - var i = -1 - var out = Buffer.allocUnsafe(buffer.length) - buffer = Buffer.concat([buffer, Buffer.from([value])]) - - while (++i < len) { - out[i] = buffer[i] << 1 | buffer[i + 1] >> (7) - } - - return out -} - -exports.encrypt = function (self, chunk, decrypt) { - var len = chunk.length - var out = Buffer.allocUnsafe(len) - var i = -1 - - while (++i < len) { - out[i] = encryptByte(self, chunk[i], decrypt) - } - - return out -} - -},{"safe-buffer":250}],31:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -function encryptByte (self, byteParam, decrypt) { - var pad = self._cipher.encryptBlock(self._prev) - var out = pad[0] ^ byteParam - - self._prev = Buffer.concat([ - self._prev.slice(1), - Buffer.from([decrypt ? byteParam : out]) - ]) - - return out -} - -exports.encrypt = function (self, chunk, decrypt) { - var len = chunk.length - var out = Buffer.allocUnsafe(len) - var i = -1 - - while (++i < len) { - out[i] = encryptByte(self, chunk[i], decrypt) - } - - return out -} - -},{"safe-buffer":250}],32:[function(require,module,exports){ -var xor = require('buffer-xor') -var Buffer = require('safe-buffer').Buffer -var incr32 = require('../incr32') - -function getBlock (self) { - var out = self._cipher.encryptBlockRaw(self._prev) - incr32(self._prev) - return out -} - -var blockSize = 16 -exports.encrypt = function (self, chunk) { - var chunkNum = Math.ceil(chunk.length / blockSize) - var start = self._cache.length - self._cache = Buffer.concat([ - self._cache, - Buffer.allocUnsafe(chunkNum * blockSize) - ]) - for (var i = 0; i < chunkNum; i++) { - var out = getBlock(self) - var offset = start + i * blockSize - self._cache.writeUInt32BE(out[0], offset + 0) - self._cache.writeUInt32BE(out[1], offset + 4) - self._cache.writeUInt32BE(out[2], offset + 8) - self._cache.writeUInt32BE(out[3], offset + 12) - } - var pad = self._cache.slice(0, chunk.length) - self._cache = self._cache.slice(chunk.length) - return xor(chunk, pad) -} - -},{"../incr32":27,"buffer-xor":67,"safe-buffer":250}],33:[function(require,module,exports){ -exports.encrypt = function (self, block) { - return self._cipher.encryptBlock(block) -} - -exports.decrypt = function (self, block) { - return self._cipher.decryptBlock(block) -} - -},{}],34:[function(require,module,exports){ -var modeModules = { - ECB: require('./ecb'), - CBC: require('./cbc'), - CFB: require('./cfb'), - CFB8: require('./cfb8'), - CFB1: require('./cfb1'), - OFB: require('./ofb'), - CTR: require('./ctr'), - GCM: require('./ctr') -} - -var modes = require('./list.json') - -for (var key in modes) { - modes[key].module = modeModules[modes[key].mode] -} - -module.exports = modes - -},{"./cbc":28,"./cfb":29,"./cfb1":30,"./cfb8":31,"./ctr":32,"./ecb":33,"./list.json":35,"./ofb":36}],35:[function(require,module,exports){ -module.exports={ - "aes-128-ecb": { - "cipher": "AES", - "key": 128, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-192-ecb": { - "cipher": "AES", - "key": 192, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-256-ecb": { - "cipher": "AES", - "key": 256, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-128-cbc": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-192-cbc": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-256-cbc": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes128": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes192": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes256": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-128-cfb": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-192-cfb": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-256-cfb": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-128-cfb8": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-192-cfb8": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-256-cfb8": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-128-cfb1": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-192-cfb1": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-256-cfb1": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-128-ofb": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-192-ofb": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-256-ofb": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-128-ctr": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-192-ctr": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-256-ctr": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-128-gcm": { - "cipher": "AES", - "key": 128, - "iv": 12, - "mode": "GCM", - "type": "auth" - }, - "aes-192-gcm": { - "cipher": "AES", - "key": 192, - "iv": 12, - "mode": "GCM", - "type": "auth" - }, - "aes-256-gcm": { - "cipher": "AES", - "key": 256, - "iv": 12, - "mode": "GCM", - "type": "auth" - } -} - -},{}],36:[function(require,module,exports){ -(function (Buffer){(function (){ -var xor = require('buffer-xor') - -function getBlock (self) { - self._prev = self._cipher.encryptBlock(self._prev) - return self._prev -} - -exports.encrypt = function (self, chunk) { - while (self._cache.length < chunk.length) { - self._cache = Buffer.concat([self._cache, getBlock(self)]) - } - - var pad = self._cache.slice(0, chunk.length) - self._cache = self._cache.slice(chunk.length) - return xor(chunk, pad) -} - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"buffer":68,"buffer-xor":67}],37:[function(require,module,exports){ -var aes = require('./aes') -var Buffer = require('safe-buffer').Buffer -var Transform = require('cipher-base') -var inherits = require('inherits') - -function StreamCipher (mode, key, iv, decrypt) { - Transform.call(this) - - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._cache = Buffer.allocUnsafe(0) - this._secCache = Buffer.allocUnsafe(0) - this._decrypt = decrypt - this._mode = mode -} - -inherits(StreamCipher, Transform) - -StreamCipher.prototype._update = function (chunk) { - return this._mode.encrypt(this, chunk, this._decrypt) -} - -StreamCipher.prototype._final = function () { - this._cipher.scrub() -} - -module.exports = StreamCipher - -},{"./aes":21,"cipher-base":71,"inherits":146,"safe-buffer":250}],38:[function(require,module,exports){ -var DES = require('browserify-des') -var aes = require('browserify-aes/browser') -var aesModes = require('browserify-aes/modes') -var desModes = require('browserify-des/modes') -var ebtk = require('evp_bytestokey') - -function createCipher (suite, password) { - suite = suite.toLowerCase() - - var keyLen, ivLen - if (aesModes[suite]) { - keyLen = aesModes[suite].key - ivLen = aesModes[suite].iv - } else if (desModes[suite]) { - keyLen = desModes[suite].key * 8 - ivLen = desModes[suite].iv - } else { - throw new TypeError('invalid suite type') - } - - var keys = ebtk(password, false, keyLen, ivLen) - return createCipheriv(suite, keys.key, keys.iv) -} - -function createDecipher (suite, password) { - suite = suite.toLowerCase() - - var keyLen, ivLen - if (aesModes[suite]) { - keyLen = aesModes[suite].key - ivLen = aesModes[suite].iv - } else if (desModes[suite]) { - keyLen = desModes[suite].key * 8 - ivLen = desModes[suite].iv - } else { - throw new TypeError('invalid suite type') - } - - var keys = ebtk(password, false, keyLen, ivLen) - return createDecipheriv(suite, keys.key, keys.iv) -} - -function createCipheriv (suite, key, iv) { - suite = suite.toLowerCase() - if (aesModes[suite]) return aes.createCipheriv(suite, key, iv) - if (desModes[suite]) return new DES({ key: key, iv: iv, mode: suite }) - - throw new TypeError('invalid suite type') -} - -function createDecipheriv (suite, key, iv) { - suite = suite.toLowerCase() - if (aesModes[suite]) return aes.createDecipheriv(suite, key, iv) - if (desModes[suite]) return new DES({ key: key, iv: iv, mode: suite, decrypt: true }) - - throw new TypeError('invalid suite type') -} - -function getCiphers () { - return Object.keys(desModes).concat(aes.getCiphers()) -} - -exports.createCipher = exports.Cipher = createCipher -exports.createCipheriv = exports.Cipheriv = createCipheriv -exports.createDecipher = exports.Decipher = createDecipher -exports.createDecipheriv = exports.Decipheriv = createDecipheriv -exports.listCiphers = exports.getCiphers = getCiphers - -},{"browserify-aes/browser":23,"browserify-aes/modes":34,"browserify-des":39,"browserify-des/modes":40,"evp_bytestokey":106}],39:[function(require,module,exports){ -var CipherBase = require('cipher-base') -var des = require('des.js') -var inherits = require('inherits') -var Buffer = require('safe-buffer').Buffer - -var modes = { - 'des-ede3-cbc': des.CBC.instantiate(des.EDE), - 'des-ede3': des.EDE, - 'des-ede-cbc': des.CBC.instantiate(des.EDE), - 'des-ede': des.EDE, - 'des-cbc': des.CBC.instantiate(des.DES), - 'des-ecb': des.DES -} -modes.des = modes['des-cbc'] -modes.des3 = modes['des-ede3-cbc'] -module.exports = DES -inherits(DES, CipherBase) -function DES (opts) { - CipherBase.call(this) - var modeName = opts.mode.toLowerCase() - var mode = modes[modeName] - var type - if (opts.decrypt) { - type = 'decrypt' - } else { - type = 'encrypt' - } - var key = opts.key - if (!Buffer.isBuffer(key)) { - key = Buffer.from(key) - } - if (modeName === 'des-ede' || modeName === 'des-ede-cbc') { - key = Buffer.concat([key, key.slice(0, 8)]) - } - var iv = opts.iv - if (!Buffer.isBuffer(iv)) { - iv = Buffer.from(iv) - } - this._des = mode.create({ - key: key, - iv: iv, - type: type - }) -} -DES.prototype._update = function (data) { - return Buffer.from(this._des.update(data)) -} -DES.prototype._final = function () { - return Buffer.from(this._des.final()) -} - -},{"cipher-base":71,"des.js":79,"inherits":146,"safe-buffer":250}],40:[function(require,module,exports){ -exports['des-ecb'] = { - key: 8, - iv: 0 -} -exports['des-cbc'] = exports.des = { - key: 8, - iv: 8 -} -exports['des-ede3-cbc'] = exports.des3 = { - key: 24, - iv: 8 -} -exports['des-ede3'] = { - key: 24, - iv: 0 -} -exports['des-ede-cbc'] = { - key: 16, - iv: 8 -} -exports['des-ede'] = { - key: 16, - iv: 0 -} - -},{}],41:[function(require,module,exports){ -(function (Buffer){(function (){ -var BN = require('bn.js') -var randomBytes = require('randombytes') - -function blind (priv) { - var r = getr(priv) - var blinder = r.toRed(BN.mont(priv.modulus)).redPow(new BN(priv.publicExponent)).fromRed() - return { blinder: blinder, unblinder: r.invm(priv.modulus) } -} - -function getr (priv) { - var len = priv.modulus.byteLength() - var r - do { - r = new BN(randomBytes(len)) - } while (r.cmp(priv.modulus) >= 0 || !r.umod(priv.prime1) || !r.umod(priv.prime2)) - return r -} - -function crt (msg, priv) { - var blinds = blind(priv) - var len = priv.modulus.byteLength() - var blinded = new BN(msg).mul(blinds.blinder).umod(priv.modulus) - var c1 = blinded.toRed(BN.mont(priv.prime1)) - var c2 = blinded.toRed(BN.mont(priv.prime2)) - var qinv = priv.coefficient - var p = priv.prime1 - var q = priv.prime2 - var m1 = c1.redPow(priv.exponent1).fromRed() - var m2 = c2.redPow(priv.exponent2).fromRed() - var h = m1.isub(m2).imul(qinv).umod(p).imul(q) - return m2.iadd(h).imul(blinds.unblinder).umod(priv.modulus).toArrayLike(Buffer, 'be', len) -} -crt.getr = getr - -module.exports = crt - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"bn.js":42,"buffer":68,"randombytes":244}],42:[function(require,module,exports){ -(function (module, exports) { - 'use strict'; - - // Utils - function assert (val, msg) { - if (!val) throw new Error(msg || 'Assertion failed'); - } - - // Could use `inherits` module, but don't want to move from single file - // architecture yet. - function inherits (ctor, superCtor) { - ctor.super_ = superCtor; - var TempCtor = function () {}; - TempCtor.prototype = superCtor.prototype; - ctor.prototype = new TempCtor(); - ctor.prototype.constructor = ctor; - } - - // BN - - function BN (number, base, endian) { - if (BN.isBN(number)) { - return number; - } - - this.negative = 0; - this.words = null; - this.length = 0; - - // Reduction context - this.red = null; - - if (number !== null) { - if (base === 'le' || base === 'be') { - endian = base; - base = 10; - } - - this._init(number || 0, base || 10, endian || 'be'); - } - } - if (typeof module === 'object') { - module.exports = BN; - } else { - exports.BN = BN; - } - - BN.BN = BN; - BN.wordSize = 26; - - var Buffer; - try { - if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { - Buffer = window.Buffer; - } else { - Buffer = require('buffer').Buffer; - } - } catch (e) { - } - - BN.isBN = function isBN (num) { - if (num instanceof BN) { - return true; - } - - return num !== null && typeof num === 'object' && - num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); - }; - - BN.max = function max (left, right) { - if (left.cmp(right) > 0) return left; - return right; - }; - - BN.min = function min (left, right) { - if (left.cmp(right) < 0) return left; - return right; - }; - - BN.prototype._init = function init (number, base, endian) { - if (typeof number === 'number') { - return this._initNumber(number, base, endian); - } - - if (typeof number === 'object') { - return this._initArray(number, base, endian); - } - - if (base === 'hex') { - base = 16; - } - assert(base === (base | 0) && base >= 2 && base <= 36); - - number = number.toString().replace(/\s+/g, ''); - var start = 0; - if (number[0] === '-') { - start++; - this.negative = 1; - } - - if (start < number.length) { - if (base === 16) { - this._parseHex(number, start, endian); - } else { - this._parseBase(number, base, start); - if (endian === 'le') { - this._initArray(this.toArray(), base, endian); - } - } - } - }; - - BN.prototype._initNumber = function _initNumber (number, base, endian) { - if (number < 0) { - this.negative = 1; - number = -number; - } - if (number < 0x4000000) { - this.words = [number & 0x3ffffff]; - this.length = 1; - } else if (number < 0x10000000000000) { - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff - ]; - this.length = 2; - } else { - assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff, - 1 - ]; - this.length = 3; - } - - if (endian !== 'le') return; - - // Reverse the bytes - this._initArray(this.toArray(), base, endian); - }; - - BN.prototype._initArray = function _initArray (number, base, endian) { - // Perhaps a Uint8Array - assert(typeof number.length === 'number'); - if (number.length <= 0) { - this.words = [0]; - this.length = 1; - return this; - } - - this.length = Math.ceil(number.length / 3); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - var j, w; - var off = 0; - if (endian === 'be') { - for (i = number.length - 1, j = 0; i >= 0; i -= 3) { - w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } else if (endian === 'le') { - for (i = 0, j = 0; i < number.length; i += 3) { - w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } - return this._strip(); - }; - - function parseHex4Bits (string, index) { - var c = string.charCodeAt(index); - // '0' - '9' - if (c >= 48 && c <= 57) { - return c - 48; - // 'A' - 'F' - } else if (c >= 65 && c <= 70) { - return c - 55; - // 'a' - 'f' - } else if (c >= 97 && c <= 102) { - return c - 87; - } else { - assert(false, 'Invalid character in ' + string); - } - } - - function parseHexByte (string, lowerBound, index) { - var r = parseHex4Bits(string, index); - if (index - 1 >= lowerBound) { - r |= parseHex4Bits(string, index - 1) << 4; - } - return r; - } - - BN.prototype._parseHex = function _parseHex (number, start, endian) { - // Create possibly bigger array to ensure that it fits the number - this.length = Math.ceil((number.length - start) / 6); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - // 24-bits chunks - var off = 0; - var j = 0; - - var w; - if (endian === 'be') { - for (i = number.length - 1; i >= start; i -= 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } else { - var parseLength = number.length - start; - for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } - - this._strip(); - }; - - function parseBase (str, start, end, mul) { - var r = 0; - var b = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r *= mul; - - // 'a' - if (c >= 49) { - b = c - 49 + 0xa; - - // 'A' - } else if (c >= 17) { - b = c - 17 + 0xa; - - // '0' - '9' - } else { - b = c; - } - assert(c >= 0 && b < mul, 'Invalid character'); - r += b; - } - return r; - } - - BN.prototype._parseBase = function _parseBase (number, base, start) { - // Initialize as zero - this.words = [0]; - this.length = 1; - - // Find length of limb in base - for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { - limbLen++; - } - limbLen--; - limbPow = (limbPow / base) | 0; - - var total = number.length - start; - var mod = total % limbLen; - var end = Math.min(total, total - mod) + start; - - var word = 0; - for (var i = start; i < end; i += limbLen) { - word = parseBase(number, i, i + limbLen, base); - - this.imuln(limbPow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - if (mod !== 0) { - var pow = 1; - word = parseBase(number, i, number.length, base); - - for (i = 0; i < mod; i++) { - pow *= base; - } - - this.imuln(pow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - this._strip(); - }; - - BN.prototype.copy = function copy (dest) { - dest.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - dest.words[i] = this.words[i]; - } - dest.length = this.length; - dest.negative = this.negative; - dest.red = this.red; - }; - - function move (dest, src) { - dest.words = src.words; - dest.length = src.length; - dest.negative = src.negative; - dest.red = src.red; - } - - BN.prototype._move = function _move (dest) { - move(dest, this); - }; - - BN.prototype.clone = function clone () { - var r = new BN(null); - this.copy(r); - return r; - }; - - BN.prototype._expand = function _expand (size) { - while (this.length < size) { - this.words[this.length++] = 0; - } - return this; - }; - - // Remove leading `0` from `this` - BN.prototype._strip = function strip () { - while (this.length > 1 && this.words[this.length - 1] === 0) { - this.length--; - } - return this._normSign(); - }; - - BN.prototype._normSign = function _normSign () { - // -0 = 0 - if (this.length === 1 && this.words[0] === 0) { - this.negative = 0; - } - return this; - }; - - // Check Symbol.for because not everywhere where Symbol defined - // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility - if (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function') { - try { - BN.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspect; - } catch (e) { - BN.prototype.inspect = inspect; - } - } else { - BN.prototype.inspect = inspect; - } - - function inspect () { - return (this.red ? ''; - } - - /* - - var zeros = []; - var groupSizes = []; - var groupBases = []; - - var s = ''; - var i = -1; - while (++i < BN.wordSize) { - zeros[i] = s; - s += '0'; - } - groupSizes[0] = 0; - groupSizes[1] = 0; - groupBases[0] = 0; - groupBases[1] = 0; - var base = 2 - 1; - while (++base < 36 + 1) { - var groupSize = 0; - var groupBase = 1; - while (groupBase < (1 << BN.wordSize) / base) { - groupBase *= base; - groupSize += 1; - } - groupSizes[base] = groupSize; - groupBases[base] = groupBase; - } - - */ - - var zeros = [ - '', - '0', - '00', - '000', - '0000', - '00000', - '000000', - '0000000', - '00000000', - '000000000', - '0000000000', - '00000000000', - '000000000000', - '0000000000000', - '00000000000000', - '000000000000000', - '0000000000000000', - '00000000000000000', - '000000000000000000', - '0000000000000000000', - '00000000000000000000', - '000000000000000000000', - '0000000000000000000000', - '00000000000000000000000', - '000000000000000000000000', - '0000000000000000000000000' - ]; - - var groupSizes = [ - 0, 0, - 25, 16, 12, 11, 10, 9, 8, - 8, 7, 7, 7, 7, 6, 6, - 6, 6, 6, 6, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5 - ]; - - var groupBases = [ - 0, 0, - 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, - 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, - 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, - 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, - 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 - ]; - - BN.prototype.toString = function toString (base, padding) { - base = base || 10; - padding = padding | 0 || 1; - - var out; - if (base === 16 || base === 'hex') { - out = ''; - var off = 0; - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = this.words[i]; - var word = (((w << off) | carry) & 0xffffff).toString(16); - carry = (w >>> (24 - off)) & 0xffffff; - off += 2; - if (off >= 26) { - off -= 26; - i--; - } - if (carry !== 0 || i !== this.length - 1) { - out = zeros[6 - word.length] + word + out; - } else { - out = word + out; - } - } - if (carry !== 0) { - out = carry.toString(16) + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - if (base === (base | 0) && base >= 2 && base <= 36) { - // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); - var groupSize = groupSizes[base]; - // var groupBase = Math.pow(base, groupSize); - var groupBase = groupBases[base]; - out = ''; - var c = this.clone(); - c.negative = 0; - while (!c.isZero()) { - var r = c.modrn(groupBase).toString(base); - c = c.idivn(groupBase); - - if (!c.isZero()) { - out = zeros[groupSize - r.length] + r + out; - } else { - out = r + out; - } - } - if (this.isZero()) { - out = '0' + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - assert(false, 'Base should be between 2 and 36'); - }; - - BN.prototype.toNumber = function toNumber () { - var ret = this.words[0]; - if (this.length === 2) { - ret += this.words[1] * 0x4000000; - } else if (this.length === 3 && this.words[2] === 0x01) { - // NOTE: at this stage it is known that the top bit is set - ret += 0x10000000000000 + (this.words[1] * 0x4000000); - } else if (this.length > 2) { - assert(false, 'Number can only safely store up to 53 bits'); - } - return (this.negative !== 0) ? -ret : ret; - }; - - BN.prototype.toJSON = function toJSON () { - return this.toString(16, 2); - }; - - if (Buffer) { - BN.prototype.toBuffer = function toBuffer (endian, length) { - return this.toArrayLike(Buffer, endian, length); - }; - } - - BN.prototype.toArray = function toArray (endian, length) { - return this.toArrayLike(Array, endian, length); - }; - - var allocate = function allocate (ArrayType, size) { - if (ArrayType.allocUnsafe) { - return ArrayType.allocUnsafe(size); - } - return new ArrayType(size); - }; - - BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { - this._strip(); - - var byteLength = this.byteLength(); - var reqLength = length || Math.max(1, byteLength); - assert(byteLength <= reqLength, 'byte array longer than desired length'); - assert(reqLength > 0, 'Requested array length <= 0'); - - var res = allocate(ArrayType, reqLength); - var postfix = endian === 'le' ? 'LE' : 'BE'; - this['_toArrayLike' + postfix](res, byteLength); - return res; - }; - - BN.prototype._toArrayLikeLE = function _toArrayLikeLE (res, byteLength) { - var position = 0; - var carry = 0; - - for (var i = 0, shift = 0; i < this.length; i++) { - var word = (this.words[i] << shift) | carry; - - res[position++] = word & 0xff; - if (position < res.length) { - res[position++] = (word >> 8) & 0xff; - } - if (position < res.length) { - res[position++] = (word >> 16) & 0xff; - } - - if (shift === 6) { - if (position < res.length) { - res[position++] = (word >> 24) & 0xff; - } - carry = 0; - shift = 0; - } else { - carry = word >>> 24; - shift += 2; - } - } - - if (position < res.length) { - res[position++] = carry; - - while (position < res.length) { - res[position++] = 0; - } - } - }; - - BN.prototype._toArrayLikeBE = function _toArrayLikeBE (res, byteLength) { - var position = res.length - 1; - var carry = 0; - - for (var i = 0, shift = 0; i < this.length; i++) { - var word = (this.words[i] << shift) | carry; - - res[position--] = word & 0xff; - if (position >= 0) { - res[position--] = (word >> 8) & 0xff; - } - if (position >= 0) { - res[position--] = (word >> 16) & 0xff; - } - - if (shift === 6) { - if (position >= 0) { - res[position--] = (word >> 24) & 0xff; - } - carry = 0; - shift = 0; - } else { - carry = word >>> 24; - shift += 2; - } - } - - if (position >= 0) { - res[position--] = carry; - - while (position >= 0) { - res[position--] = 0; - } - } - }; - - if (Math.clz32) { - BN.prototype._countBits = function _countBits (w) { - return 32 - Math.clz32(w); - }; - } else { - BN.prototype._countBits = function _countBits (w) { - var t = w; - var r = 0; - if (t >= 0x1000) { - r += 13; - t >>>= 13; - } - if (t >= 0x40) { - r += 7; - t >>>= 7; - } - if (t >= 0x8) { - r += 4; - t >>>= 4; - } - if (t >= 0x02) { - r += 2; - t >>>= 2; - } - return r + t; - }; - } - - BN.prototype._zeroBits = function _zeroBits (w) { - // Short-cut - if (w === 0) return 26; - - var t = w; - var r = 0; - if ((t & 0x1fff) === 0) { - r += 13; - t >>>= 13; - } - if ((t & 0x7f) === 0) { - r += 7; - t >>>= 7; - } - if ((t & 0xf) === 0) { - r += 4; - t >>>= 4; - } - if ((t & 0x3) === 0) { - r += 2; - t >>>= 2; - } - if ((t & 0x1) === 0) { - r++; - } - return r; - }; - - // Return number of used bits in a BN - BN.prototype.bitLength = function bitLength () { - var w = this.words[this.length - 1]; - var hi = this._countBits(w); - return (this.length - 1) * 26 + hi; - }; - - function toBitArray (num) { - var w = new Array(num.bitLength()); - - for (var bit = 0; bit < w.length; bit++) { - var off = (bit / 26) | 0; - var wbit = bit % 26; - - w[bit] = (num.words[off] >>> wbit) & 0x01; - } - - return w; - } - - // Number of trailing zero bits - BN.prototype.zeroBits = function zeroBits () { - if (this.isZero()) return 0; - - var r = 0; - for (var i = 0; i < this.length; i++) { - var b = this._zeroBits(this.words[i]); - r += b; - if (b !== 26) break; - } - return r; - }; - - BN.prototype.byteLength = function byteLength () { - return Math.ceil(this.bitLength() / 8); - }; - - BN.prototype.toTwos = function toTwos (width) { - if (this.negative !== 0) { - return this.abs().inotn(width).iaddn(1); - } - return this.clone(); - }; - - BN.prototype.fromTwos = function fromTwos (width) { - if (this.testn(width - 1)) { - return this.notn(width).iaddn(1).ineg(); - } - return this.clone(); - }; - - BN.prototype.isNeg = function isNeg () { - return this.negative !== 0; - }; - - // Return negative clone of `this` - BN.prototype.neg = function neg () { - return this.clone().ineg(); - }; - - BN.prototype.ineg = function ineg () { - if (!this.isZero()) { - this.negative ^= 1; - } - - return this; - }; - - // Or `num` with `this` in-place - BN.prototype.iuor = function iuor (num) { - while (this.length < num.length) { - this.words[this.length++] = 0; - } - - for (var i = 0; i < num.length; i++) { - this.words[i] = this.words[i] | num.words[i]; - } - - return this._strip(); - }; - - BN.prototype.ior = function ior (num) { - assert((this.negative | num.negative) === 0); - return this.iuor(num); - }; - - // Or `num` with `this` - BN.prototype.or = function or (num) { - if (this.length > num.length) return this.clone().ior(num); - return num.clone().ior(this); - }; - - BN.prototype.uor = function uor (num) { - if (this.length > num.length) return this.clone().iuor(num); - return num.clone().iuor(this); - }; - - // And `num` with `this` in-place - BN.prototype.iuand = function iuand (num) { - // b = min-length(num, this) - var b; - if (this.length > num.length) { - b = num; - } else { - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = this.words[i] & num.words[i]; - } - - this.length = b.length; - - return this._strip(); - }; - - BN.prototype.iand = function iand (num) { - assert((this.negative | num.negative) === 0); - return this.iuand(num); - }; - - // And `num` with `this` - BN.prototype.and = function and (num) { - if (this.length > num.length) return this.clone().iand(num); - return num.clone().iand(this); - }; - - BN.prototype.uand = function uand (num) { - if (this.length > num.length) return this.clone().iuand(num); - return num.clone().iuand(this); - }; - - // Xor `num` with `this` in-place - BN.prototype.iuxor = function iuxor (num) { - // a.length > b.length - var a; - var b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = a.words[i] ^ b.words[i]; - } - - if (this !== a) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = a.length; - - return this._strip(); - }; - - BN.prototype.ixor = function ixor (num) { - assert((this.negative | num.negative) === 0); - return this.iuxor(num); - }; - - // Xor `num` with `this` - BN.prototype.xor = function xor (num) { - if (this.length > num.length) return this.clone().ixor(num); - return num.clone().ixor(this); - }; - - BN.prototype.uxor = function uxor (num) { - if (this.length > num.length) return this.clone().iuxor(num); - return num.clone().iuxor(this); - }; - - // Not ``this`` with ``width`` bitwidth - BN.prototype.inotn = function inotn (width) { - assert(typeof width === 'number' && width >= 0); - - var bytesNeeded = Math.ceil(width / 26) | 0; - var bitsLeft = width % 26; - - // Extend the buffer with leading zeroes - this._expand(bytesNeeded); - - if (bitsLeft > 0) { - bytesNeeded--; - } - - // Handle complete words - for (var i = 0; i < bytesNeeded; i++) { - this.words[i] = ~this.words[i] & 0x3ffffff; - } - - // Handle the residue - if (bitsLeft > 0) { - this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); - } - - // And remove leading zeroes - return this._strip(); - }; - - BN.prototype.notn = function notn (width) { - return this.clone().inotn(width); - }; - - // Set `bit` of `this` - BN.prototype.setn = function setn (bit, val) { - assert(typeof bit === 'number' && bit >= 0); - - var off = (bit / 26) | 0; - var wbit = bit % 26; - - this._expand(off + 1); - - if (val) { - this.words[off] = this.words[off] | (1 << wbit); - } else { - this.words[off] = this.words[off] & ~(1 << wbit); - } - - return this._strip(); - }; - - // Add `num` to `this` in-place - BN.prototype.iadd = function iadd (num) { - var r; - - // negative + positive - if (this.negative !== 0 && num.negative === 0) { - this.negative = 0; - r = this.isub(num); - this.negative ^= 1; - return this._normSign(); - - // positive + negative - } else if (this.negative === 0 && num.negative !== 0) { - num.negative = 0; - r = this.isub(num); - num.negative = 1; - return r._normSign(); - } - - // a.length > b.length - var a, b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) + (b.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - - this.length = a.length; - if (carry !== 0) { - this.words[this.length] = carry; - this.length++; - // Copy the rest of the words - } else if (a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - return this; - }; - - // Add `num` to `this` - BN.prototype.add = function add (num) { - var res; - if (num.negative !== 0 && this.negative === 0) { - num.negative = 0; - res = this.sub(num); - num.negative ^= 1; - return res; - } else if (num.negative === 0 && this.negative !== 0) { - this.negative = 0; - res = num.sub(this); - this.negative = 1; - return res; - } - - if (this.length > num.length) return this.clone().iadd(num); - - return num.clone().iadd(this); - }; - - // Subtract `num` from `this` in-place - BN.prototype.isub = function isub (num) { - // this - (-num) = this + num - if (num.negative !== 0) { - num.negative = 0; - var r = this.iadd(num); - num.negative = 1; - return r._normSign(); - - // -this - num = -(this + num) - } else if (this.negative !== 0) { - this.negative = 0; - this.iadd(num); - this.negative = 1; - return this._normSign(); - } - - // At this point both numbers are positive - var cmp = this.cmp(num); - - // Optimization - zeroify - if (cmp === 0) { - this.negative = 0; - this.length = 1; - this.words[0] = 0; - return this; - } - - // a > b - var a, b; - if (cmp > 0) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) - (b.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - - // Copy rest of the words - if (carry === 0 && i < a.length && a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = Math.max(this.length, i); - - if (a !== this) { - this.negative = 1; - } - - return this._strip(); - }; - - // Subtract `num` from `this` - BN.prototype.sub = function sub (num) { - return this.clone().isub(num); - }; - - function smallMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - var len = (self.length + num.length) | 0; - out.length = len; - len = (len - 1) | 0; - - // Peel one iteration (compiler can't do it, because of code complexity) - var a = self.words[0] | 0; - var b = num.words[0] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - var carry = (r / 0x4000000) | 0; - out.words[0] = lo; - - for (var k = 1; k < len; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = carry >>> 26; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = (k - j) | 0; - a = self.words[i] | 0; - b = num.words[j] | 0; - r = a * b + rword; - ncarry += (r / 0x4000000) | 0; - rword = r & 0x3ffffff; - } - out.words[k] = rword | 0; - carry = ncarry | 0; - } - if (carry !== 0) { - out.words[k] = carry | 0; - } else { - out.length--; - } - - return out._strip(); - } - - // TODO(indutny): it may be reasonable to omit it for users who don't need - // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit - // multiplication (like elliptic secp256k1). - var comb10MulTo = function comb10MulTo (self, num, out) { - var a = self.words; - var b = num.words; - var o = out.words; - var c = 0; - var lo; - var mid; - var hi; - var a0 = a[0] | 0; - var al0 = a0 & 0x1fff; - var ah0 = a0 >>> 13; - var a1 = a[1] | 0; - var al1 = a1 & 0x1fff; - var ah1 = a1 >>> 13; - var a2 = a[2] | 0; - var al2 = a2 & 0x1fff; - var ah2 = a2 >>> 13; - var a3 = a[3] | 0; - var al3 = a3 & 0x1fff; - var ah3 = a3 >>> 13; - var a4 = a[4] | 0; - var al4 = a4 & 0x1fff; - var ah4 = a4 >>> 13; - var a5 = a[5] | 0; - var al5 = a5 & 0x1fff; - var ah5 = a5 >>> 13; - var a6 = a[6] | 0; - var al6 = a6 & 0x1fff; - var ah6 = a6 >>> 13; - var a7 = a[7] | 0; - var al7 = a7 & 0x1fff; - var ah7 = a7 >>> 13; - var a8 = a[8] | 0; - var al8 = a8 & 0x1fff; - var ah8 = a8 >>> 13; - var a9 = a[9] | 0; - var al9 = a9 & 0x1fff; - var ah9 = a9 >>> 13; - var b0 = b[0] | 0; - var bl0 = b0 & 0x1fff; - var bh0 = b0 >>> 13; - var b1 = b[1] | 0; - var bl1 = b1 & 0x1fff; - var bh1 = b1 >>> 13; - var b2 = b[2] | 0; - var bl2 = b2 & 0x1fff; - var bh2 = b2 >>> 13; - var b3 = b[3] | 0; - var bl3 = b3 & 0x1fff; - var bh3 = b3 >>> 13; - var b4 = b[4] | 0; - var bl4 = b4 & 0x1fff; - var bh4 = b4 >>> 13; - var b5 = b[5] | 0; - var bl5 = b5 & 0x1fff; - var bh5 = b5 >>> 13; - var b6 = b[6] | 0; - var bl6 = b6 & 0x1fff; - var bh6 = b6 >>> 13; - var b7 = b[7] | 0; - var bl7 = b7 & 0x1fff; - var bh7 = b7 >>> 13; - var b8 = b[8] | 0; - var bl8 = b8 & 0x1fff; - var bh8 = b8 >>> 13; - var b9 = b[9] | 0; - var bl9 = b9 & 0x1fff; - var bh9 = b9 >>> 13; - - out.negative = self.negative ^ num.negative; - out.length = 19; - /* k = 0 */ - lo = Math.imul(al0, bl0); - mid = Math.imul(al0, bh0); - mid = (mid + Math.imul(ah0, bl0)) | 0; - hi = Math.imul(ah0, bh0); - var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; - w0 &= 0x3ffffff; - /* k = 1 */ - lo = Math.imul(al1, bl0); - mid = Math.imul(al1, bh0); - mid = (mid + Math.imul(ah1, bl0)) | 0; - hi = Math.imul(ah1, bh0); - lo = (lo + Math.imul(al0, bl1)) | 0; - mid = (mid + Math.imul(al0, bh1)) | 0; - mid = (mid + Math.imul(ah0, bl1)) | 0; - hi = (hi + Math.imul(ah0, bh1)) | 0; - var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; - w1 &= 0x3ffffff; - /* k = 2 */ - lo = Math.imul(al2, bl0); - mid = Math.imul(al2, bh0); - mid = (mid + Math.imul(ah2, bl0)) | 0; - hi = Math.imul(ah2, bh0); - lo = (lo + Math.imul(al1, bl1)) | 0; - mid = (mid + Math.imul(al1, bh1)) | 0; - mid = (mid + Math.imul(ah1, bl1)) | 0; - hi = (hi + Math.imul(ah1, bh1)) | 0; - lo = (lo + Math.imul(al0, bl2)) | 0; - mid = (mid + Math.imul(al0, bh2)) | 0; - mid = (mid + Math.imul(ah0, bl2)) | 0; - hi = (hi + Math.imul(ah0, bh2)) | 0; - var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; - w2 &= 0x3ffffff; - /* k = 3 */ - lo = Math.imul(al3, bl0); - mid = Math.imul(al3, bh0); - mid = (mid + Math.imul(ah3, bl0)) | 0; - hi = Math.imul(ah3, bh0); - lo = (lo + Math.imul(al2, bl1)) | 0; - mid = (mid + Math.imul(al2, bh1)) | 0; - mid = (mid + Math.imul(ah2, bl1)) | 0; - hi = (hi + Math.imul(ah2, bh1)) | 0; - lo = (lo + Math.imul(al1, bl2)) | 0; - mid = (mid + Math.imul(al1, bh2)) | 0; - mid = (mid + Math.imul(ah1, bl2)) | 0; - hi = (hi + Math.imul(ah1, bh2)) | 0; - lo = (lo + Math.imul(al0, bl3)) | 0; - mid = (mid + Math.imul(al0, bh3)) | 0; - mid = (mid + Math.imul(ah0, bl3)) | 0; - hi = (hi + Math.imul(ah0, bh3)) | 0; - var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; - w3 &= 0x3ffffff; - /* k = 4 */ - lo = Math.imul(al4, bl0); - mid = Math.imul(al4, bh0); - mid = (mid + Math.imul(ah4, bl0)) | 0; - hi = Math.imul(ah4, bh0); - lo = (lo + Math.imul(al3, bl1)) | 0; - mid = (mid + Math.imul(al3, bh1)) | 0; - mid = (mid + Math.imul(ah3, bl1)) | 0; - hi = (hi + Math.imul(ah3, bh1)) | 0; - lo = (lo + Math.imul(al2, bl2)) | 0; - mid = (mid + Math.imul(al2, bh2)) | 0; - mid = (mid + Math.imul(ah2, bl2)) | 0; - hi = (hi + Math.imul(ah2, bh2)) | 0; - lo = (lo + Math.imul(al1, bl3)) | 0; - mid = (mid + Math.imul(al1, bh3)) | 0; - mid = (mid + Math.imul(ah1, bl3)) | 0; - hi = (hi + Math.imul(ah1, bh3)) | 0; - lo = (lo + Math.imul(al0, bl4)) | 0; - mid = (mid + Math.imul(al0, bh4)) | 0; - mid = (mid + Math.imul(ah0, bl4)) | 0; - hi = (hi + Math.imul(ah0, bh4)) | 0; - var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; - w4 &= 0x3ffffff; - /* k = 5 */ - lo = Math.imul(al5, bl0); - mid = Math.imul(al5, bh0); - mid = (mid + Math.imul(ah5, bl0)) | 0; - hi = Math.imul(ah5, bh0); - lo = (lo + Math.imul(al4, bl1)) | 0; - mid = (mid + Math.imul(al4, bh1)) | 0; - mid = (mid + Math.imul(ah4, bl1)) | 0; - hi = (hi + Math.imul(ah4, bh1)) | 0; - lo = (lo + Math.imul(al3, bl2)) | 0; - mid = (mid + Math.imul(al3, bh2)) | 0; - mid = (mid + Math.imul(ah3, bl2)) | 0; - hi = (hi + Math.imul(ah3, bh2)) | 0; - lo = (lo + Math.imul(al2, bl3)) | 0; - mid = (mid + Math.imul(al2, bh3)) | 0; - mid = (mid + Math.imul(ah2, bl3)) | 0; - hi = (hi + Math.imul(ah2, bh3)) | 0; - lo = (lo + Math.imul(al1, bl4)) | 0; - mid = (mid + Math.imul(al1, bh4)) | 0; - mid = (mid + Math.imul(ah1, bl4)) | 0; - hi = (hi + Math.imul(ah1, bh4)) | 0; - lo = (lo + Math.imul(al0, bl5)) | 0; - mid = (mid + Math.imul(al0, bh5)) | 0; - mid = (mid + Math.imul(ah0, bl5)) | 0; - hi = (hi + Math.imul(ah0, bh5)) | 0; - var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; - w5 &= 0x3ffffff; - /* k = 6 */ - lo = Math.imul(al6, bl0); - mid = Math.imul(al6, bh0); - mid = (mid + Math.imul(ah6, bl0)) | 0; - hi = Math.imul(ah6, bh0); - lo = (lo + Math.imul(al5, bl1)) | 0; - mid = (mid + Math.imul(al5, bh1)) | 0; - mid = (mid + Math.imul(ah5, bl1)) | 0; - hi = (hi + Math.imul(ah5, bh1)) | 0; - lo = (lo + Math.imul(al4, bl2)) | 0; - mid = (mid + Math.imul(al4, bh2)) | 0; - mid = (mid + Math.imul(ah4, bl2)) | 0; - hi = (hi + Math.imul(ah4, bh2)) | 0; - lo = (lo + Math.imul(al3, bl3)) | 0; - mid = (mid + Math.imul(al3, bh3)) | 0; - mid = (mid + Math.imul(ah3, bl3)) | 0; - hi = (hi + Math.imul(ah3, bh3)) | 0; - lo = (lo + Math.imul(al2, bl4)) | 0; - mid = (mid + Math.imul(al2, bh4)) | 0; - mid = (mid + Math.imul(ah2, bl4)) | 0; - hi = (hi + Math.imul(ah2, bh4)) | 0; - lo = (lo + Math.imul(al1, bl5)) | 0; - mid = (mid + Math.imul(al1, bh5)) | 0; - mid = (mid + Math.imul(ah1, bl5)) | 0; - hi = (hi + Math.imul(ah1, bh5)) | 0; - lo = (lo + Math.imul(al0, bl6)) | 0; - mid = (mid + Math.imul(al0, bh6)) | 0; - mid = (mid + Math.imul(ah0, bl6)) | 0; - hi = (hi + Math.imul(ah0, bh6)) | 0; - var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; - w6 &= 0x3ffffff; - /* k = 7 */ - lo = Math.imul(al7, bl0); - mid = Math.imul(al7, bh0); - mid = (mid + Math.imul(ah7, bl0)) | 0; - hi = Math.imul(ah7, bh0); - lo = (lo + Math.imul(al6, bl1)) | 0; - mid = (mid + Math.imul(al6, bh1)) | 0; - mid = (mid + Math.imul(ah6, bl1)) | 0; - hi = (hi + Math.imul(ah6, bh1)) | 0; - lo = (lo + Math.imul(al5, bl2)) | 0; - mid = (mid + Math.imul(al5, bh2)) | 0; - mid = (mid + Math.imul(ah5, bl2)) | 0; - hi = (hi + Math.imul(ah5, bh2)) | 0; - lo = (lo + Math.imul(al4, bl3)) | 0; - mid = (mid + Math.imul(al4, bh3)) | 0; - mid = (mid + Math.imul(ah4, bl3)) | 0; - hi = (hi + Math.imul(ah4, bh3)) | 0; - lo = (lo + Math.imul(al3, bl4)) | 0; - mid = (mid + Math.imul(al3, bh4)) | 0; - mid = (mid + Math.imul(ah3, bl4)) | 0; - hi = (hi + Math.imul(ah3, bh4)) | 0; - lo = (lo + Math.imul(al2, bl5)) | 0; - mid = (mid + Math.imul(al2, bh5)) | 0; - mid = (mid + Math.imul(ah2, bl5)) | 0; - hi = (hi + Math.imul(ah2, bh5)) | 0; - lo = (lo + Math.imul(al1, bl6)) | 0; - mid = (mid + Math.imul(al1, bh6)) | 0; - mid = (mid + Math.imul(ah1, bl6)) | 0; - hi = (hi + Math.imul(ah1, bh6)) | 0; - lo = (lo + Math.imul(al0, bl7)) | 0; - mid = (mid + Math.imul(al0, bh7)) | 0; - mid = (mid + Math.imul(ah0, bl7)) | 0; - hi = (hi + Math.imul(ah0, bh7)) | 0; - var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; - w7 &= 0x3ffffff; - /* k = 8 */ - lo = Math.imul(al8, bl0); - mid = Math.imul(al8, bh0); - mid = (mid + Math.imul(ah8, bl0)) | 0; - hi = Math.imul(ah8, bh0); - lo = (lo + Math.imul(al7, bl1)) | 0; - mid = (mid + Math.imul(al7, bh1)) | 0; - mid = (mid + Math.imul(ah7, bl1)) | 0; - hi = (hi + Math.imul(ah7, bh1)) | 0; - lo = (lo + Math.imul(al6, bl2)) | 0; - mid = (mid + Math.imul(al6, bh2)) | 0; - mid = (mid + Math.imul(ah6, bl2)) | 0; - hi = (hi + Math.imul(ah6, bh2)) | 0; - lo = (lo + Math.imul(al5, bl3)) | 0; - mid = (mid + Math.imul(al5, bh3)) | 0; - mid = (mid + Math.imul(ah5, bl3)) | 0; - hi = (hi + Math.imul(ah5, bh3)) | 0; - lo = (lo + Math.imul(al4, bl4)) | 0; - mid = (mid + Math.imul(al4, bh4)) | 0; - mid = (mid + Math.imul(ah4, bl4)) | 0; - hi = (hi + Math.imul(ah4, bh4)) | 0; - lo = (lo + Math.imul(al3, bl5)) | 0; - mid = (mid + Math.imul(al3, bh5)) | 0; - mid = (mid + Math.imul(ah3, bl5)) | 0; - hi = (hi + Math.imul(ah3, bh5)) | 0; - lo = (lo + Math.imul(al2, bl6)) | 0; - mid = (mid + Math.imul(al2, bh6)) | 0; - mid = (mid + Math.imul(ah2, bl6)) | 0; - hi = (hi + Math.imul(ah2, bh6)) | 0; - lo = (lo + Math.imul(al1, bl7)) | 0; - mid = (mid + Math.imul(al1, bh7)) | 0; - mid = (mid + Math.imul(ah1, bl7)) | 0; - hi = (hi + Math.imul(ah1, bh7)) | 0; - lo = (lo + Math.imul(al0, bl8)) | 0; - mid = (mid + Math.imul(al0, bh8)) | 0; - mid = (mid + Math.imul(ah0, bl8)) | 0; - hi = (hi + Math.imul(ah0, bh8)) | 0; - var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; - w8 &= 0x3ffffff; - /* k = 9 */ - lo = Math.imul(al9, bl0); - mid = Math.imul(al9, bh0); - mid = (mid + Math.imul(ah9, bl0)) | 0; - hi = Math.imul(ah9, bh0); - lo = (lo + Math.imul(al8, bl1)) | 0; - mid = (mid + Math.imul(al8, bh1)) | 0; - mid = (mid + Math.imul(ah8, bl1)) | 0; - hi = (hi + Math.imul(ah8, bh1)) | 0; - lo = (lo + Math.imul(al7, bl2)) | 0; - mid = (mid + Math.imul(al7, bh2)) | 0; - mid = (mid + Math.imul(ah7, bl2)) | 0; - hi = (hi + Math.imul(ah7, bh2)) | 0; - lo = (lo + Math.imul(al6, bl3)) | 0; - mid = (mid + Math.imul(al6, bh3)) | 0; - mid = (mid + Math.imul(ah6, bl3)) | 0; - hi = (hi + Math.imul(ah6, bh3)) | 0; - lo = (lo + Math.imul(al5, bl4)) | 0; - mid = (mid + Math.imul(al5, bh4)) | 0; - mid = (mid + Math.imul(ah5, bl4)) | 0; - hi = (hi + Math.imul(ah5, bh4)) | 0; - lo = (lo + Math.imul(al4, bl5)) | 0; - mid = (mid + Math.imul(al4, bh5)) | 0; - mid = (mid + Math.imul(ah4, bl5)) | 0; - hi = (hi + Math.imul(ah4, bh5)) | 0; - lo = (lo + Math.imul(al3, bl6)) | 0; - mid = (mid + Math.imul(al3, bh6)) | 0; - mid = (mid + Math.imul(ah3, bl6)) | 0; - hi = (hi + Math.imul(ah3, bh6)) | 0; - lo = (lo + Math.imul(al2, bl7)) | 0; - mid = (mid + Math.imul(al2, bh7)) | 0; - mid = (mid + Math.imul(ah2, bl7)) | 0; - hi = (hi + Math.imul(ah2, bh7)) | 0; - lo = (lo + Math.imul(al1, bl8)) | 0; - mid = (mid + Math.imul(al1, bh8)) | 0; - mid = (mid + Math.imul(ah1, bl8)) | 0; - hi = (hi + Math.imul(ah1, bh8)) | 0; - lo = (lo + Math.imul(al0, bl9)) | 0; - mid = (mid + Math.imul(al0, bh9)) | 0; - mid = (mid + Math.imul(ah0, bl9)) | 0; - hi = (hi + Math.imul(ah0, bh9)) | 0; - var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; - w9 &= 0x3ffffff; - /* k = 10 */ - lo = Math.imul(al9, bl1); - mid = Math.imul(al9, bh1); - mid = (mid + Math.imul(ah9, bl1)) | 0; - hi = Math.imul(ah9, bh1); - lo = (lo + Math.imul(al8, bl2)) | 0; - mid = (mid + Math.imul(al8, bh2)) | 0; - mid = (mid + Math.imul(ah8, bl2)) | 0; - hi = (hi + Math.imul(ah8, bh2)) | 0; - lo = (lo + Math.imul(al7, bl3)) | 0; - mid = (mid + Math.imul(al7, bh3)) | 0; - mid = (mid + Math.imul(ah7, bl3)) | 0; - hi = (hi + Math.imul(ah7, bh3)) | 0; - lo = (lo + Math.imul(al6, bl4)) | 0; - mid = (mid + Math.imul(al6, bh4)) | 0; - mid = (mid + Math.imul(ah6, bl4)) | 0; - hi = (hi + Math.imul(ah6, bh4)) | 0; - lo = (lo + Math.imul(al5, bl5)) | 0; - mid = (mid + Math.imul(al5, bh5)) | 0; - mid = (mid + Math.imul(ah5, bl5)) | 0; - hi = (hi + Math.imul(ah5, bh5)) | 0; - lo = (lo + Math.imul(al4, bl6)) | 0; - mid = (mid + Math.imul(al4, bh6)) | 0; - mid = (mid + Math.imul(ah4, bl6)) | 0; - hi = (hi + Math.imul(ah4, bh6)) | 0; - lo = (lo + Math.imul(al3, bl7)) | 0; - mid = (mid + Math.imul(al3, bh7)) | 0; - mid = (mid + Math.imul(ah3, bl7)) | 0; - hi = (hi + Math.imul(ah3, bh7)) | 0; - lo = (lo + Math.imul(al2, bl8)) | 0; - mid = (mid + Math.imul(al2, bh8)) | 0; - mid = (mid + Math.imul(ah2, bl8)) | 0; - hi = (hi + Math.imul(ah2, bh8)) | 0; - lo = (lo + Math.imul(al1, bl9)) | 0; - mid = (mid + Math.imul(al1, bh9)) | 0; - mid = (mid + Math.imul(ah1, bl9)) | 0; - hi = (hi + Math.imul(ah1, bh9)) | 0; - var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; - w10 &= 0x3ffffff; - /* k = 11 */ - lo = Math.imul(al9, bl2); - mid = Math.imul(al9, bh2); - mid = (mid + Math.imul(ah9, bl2)) | 0; - hi = Math.imul(ah9, bh2); - lo = (lo + Math.imul(al8, bl3)) | 0; - mid = (mid + Math.imul(al8, bh3)) | 0; - mid = (mid + Math.imul(ah8, bl3)) | 0; - hi = (hi + Math.imul(ah8, bh3)) | 0; - lo = (lo + Math.imul(al7, bl4)) | 0; - mid = (mid + Math.imul(al7, bh4)) | 0; - mid = (mid + Math.imul(ah7, bl4)) | 0; - hi = (hi + Math.imul(ah7, bh4)) | 0; - lo = (lo + Math.imul(al6, bl5)) | 0; - mid = (mid + Math.imul(al6, bh5)) | 0; - mid = (mid + Math.imul(ah6, bl5)) | 0; - hi = (hi + Math.imul(ah6, bh5)) | 0; - lo = (lo + Math.imul(al5, bl6)) | 0; - mid = (mid + Math.imul(al5, bh6)) | 0; - mid = (mid + Math.imul(ah5, bl6)) | 0; - hi = (hi + Math.imul(ah5, bh6)) | 0; - lo = (lo + Math.imul(al4, bl7)) | 0; - mid = (mid + Math.imul(al4, bh7)) | 0; - mid = (mid + Math.imul(ah4, bl7)) | 0; - hi = (hi + Math.imul(ah4, bh7)) | 0; - lo = (lo + Math.imul(al3, bl8)) | 0; - mid = (mid + Math.imul(al3, bh8)) | 0; - mid = (mid + Math.imul(ah3, bl8)) | 0; - hi = (hi + Math.imul(ah3, bh8)) | 0; - lo = (lo + Math.imul(al2, bl9)) | 0; - mid = (mid + Math.imul(al2, bh9)) | 0; - mid = (mid + Math.imul(ah2, bl9)) | 0; - hi = (hi + Math.imul(ah2, bh9)) | 0; - var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; - w11 &= 0x3ffffff; - /* k = 12 */ - lo = Math.imul(al9, bl3); - mid = Math.imul(al9, bh3); - mid = (mid + Math.imul(ah9, bl3)) | 0; - hi = Math.imul(ah9, bh3); - lo = (lo + Math.imul(al8, bl4)) | 0; - mid = (mid + Math.imul(al8, bh4)) | 0; - mid = (mid + Math.imul(ah8, bl4)) | 0; - hi = (hi + Math.imul(ah8, bh4)) | 0; - lo = (lo + Math.imul(al7, bl5)) | 0; - mid = (mid + Math.imul(al7, bh5)) | 0; - mid = (mid + Math.imul(ah7, bl5)) | 0; - hi = (hi + Math.imul(ah7, bh5)) | 0; - lo = (lo + Math.imul(al6, bl6)) | 0; - mid = (mid + Math.imul(al6, bh6)) | 0; - mid = (mid + Math.imul(ah6, bl6)) | 0; - hi = (hi + Math.imul(ah6, bh6)) | 0; - lo = (lo + Math.imul(al5, bl7)) | 0; - mid = (mid + Math.imul(al5, bh7)) | 0; - mid = (mid + Math.imul(ah5, bl7)) | 0; - hi = (hi + Math.imul(ah5, bh7)) | 0; - lo = (lo + Math.imul(al4, bl8)) | 0; - mid = (mid + Math.imul(al4, bh8)) | 0; - mid = (mid + Math.imul(ah4, bl8)) | 0; - hi = (hi + Math.imul(ah4, bh8)) | 0; - lo = (lo + Math.imul(al3, bl9)) | 0; - mid = (mid + Math.imul(al3, bh9)) | 0; - mid = (mid + Math.imul(ah3, bl9)) | 0; - hi = (hi + Math.imul(ah3, bh9)) | 0; - var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; - w12 &= 0x3ffffff; - /* k = 13 */ - lo = Math.imul(al9, bl4); - mid = Math.imul(al9, bh4); - mid = (mid + Math.imul(ah9, bl4)) | 0; - hi = Math.imul(ah9, bh4); - lo = (lo + Math.imul(al8, bl5)) | 0; - mid = (mid + Math.imul(al8, bh5)) | 0; - mid = (mid + Math.imul(ah8, bl5)) | 0; - hi = (hi + Math.imul(ah8, bh5)) | 0; - lo = (lo + Math.imul(al7, bl6)) | 0; - mid = (mid + Math.imul(al7, bh6)) | 0; - mid = (mid + Math.imul(ah7, bl6)) | 0; - hi = (hi + Math.imul(ah7, bh6)) | 0; - lo = (lo + Math.imul(al6, bl7)) | 0; - mid = (mid + Math.imul(al6, bh7)) | 0; - mid = (mid + Math.imul(ah6, bl7)) | 0; - hi = (hi + Math.imul(ah6, bh7)) | 0; - lo = (lo + Math.imul(al5, bl8)) | 0; - mid = (mid + Math.imul(al5, bh8)) | 0; - mid = (mid + Math.imul(ah5, bl8)) | 0; - hi = (hi + Math.imul(ah5, bh8)) | 0; - lo = (lo + Math.imul(al4, bl9)) | 0; - mid = (mid + Math.imul(al4, bh9)) | 0; - mid = (mid + Math.imul(ah4, bl9)) | 0; - hi = (hi + Math.imul(ah4, bh9)) | 0; - var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; - w13 &= 0x3ffffff; - /* k = 14 */ - lo = Math.imul(al9, bl5); - mid = Math.imul(al9, bh5); - mid = (mid + Math.imul(ah9, bl5)) | 0; - hi = Math.imul(ah9, bh5); - lo = (lo + Math.imul(al8, bl6)) | 0; - mid = (mid + Math.imul(al8, bh6)) | 0; - mid = (mid + Math.imul(ah8, bl6)) | 0; - hi = (hi + Math.imul(ah8, bh6)) | 0; - lo = (lo + Math.imul(al7, bl7)) | 0; - mid = (mid + Math.imul(al7, bh7)) | 0; - mid = (mid + Math.imul(ah7, bl7)) | 0; - hi = (hi + Math.imul(ah7, bh7)) | 0; - lo = (lo + Math.imul(al6, bl8)) | 0; - mid = (mid + Math.imul(al6, bh8)) | 0; - mid = (mid + Math.imul(ah6, bl8)) | 0; - hi = (hi + Math.imul(ah6, bh8)) | 0; - lo = (lo + Math.imul(al5, bl9)) | 0; - mid = (mid + Math.imul(al5, bh9)) | 0; - mid = (mid + Math.imul(ah5, bl9)) | 0; - hi = (hi + Math.imul(ah5, bh9)) | 0; - var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; - w14 &= 0x3ffffff; - /* k = 15 */ - lo = Math.imul(al9, bl6); - mid = Math.imul(al9, bh6); - mid = (mid + Math.imul(ah9, bl6)) | 0; - hi = Math.imul(ah9, bh6); - lo = (lo + Math.imul(al8, bl7)) | 0; - mid = (mid + Math.imul(al8, bh7)) | 0; - mid = (mid + Math.imul(ah8, bl7)) | 0; - hi = (hi + Math.imul(ah8, bh7)) | 0; - lo = (lo + Math.imul(al7, bl8)) | 0; - mid = (mid + Math.imul(al7, bh8)) | 0; - mid = (mid + Math.imul(ah7, bl8)) | 0; - hi = (hi + Math.imul(ah7, bh8)) | 0; - lo = (lo + Math.imul(al6, bl9)) | 0; - mid = (mid + Math.imul(al6, bh9)) | 0; - mid = (mid + Math.imul(ah6, bl9)) | 0; - hi = (hi + Math.imul(ah6, bh9)) | 0; - var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; - w15 &= 0x3ffffff; - /* k = 16 */ - lo = Math.imul(al9, bl7); - mid = Math.imul(al9, bh7); - mid = (mid + Math.imul(ah9, bl7)) | 0; - hi = Math.imul(ah9, bh7); - lo = (lo + Math.imul(al8, bl8)) | 0; - mid = (mid + Math.imul(al8, bh8)) | 0; - mid = (mid + Math.imul(ah8, bl8)) | 0; - hi = (hi + Math.imul(ah8, bh8)) | 0; - lo = (lo + Math.imul(al7, bl9)) | 0; - mid = (mid + Math.imul(al7, bh9)) | 0; - mid = (mid + Math.imul(ah7, bl9)) | 0; - hi = (hi + Math.imul(ah7, bh9)) | 0; - var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; - w16 &= 0x3ffffff; - /* k = 17 */ - lo = Math.imul(al9, bl8); - mid = Math.imul(al9, bh8); - mid = (mid + Math.imul(ah9, bl8)) | 0; - hi = Math.imul(ah9, bh8); - lo = (lo + Math.imul(al8, bl9)) | 0; - mid = (mid + Math.imul(al8, bh9)) | 0; - mid = (mid + Math.imul(ah8, bl9)) | 0; - hi = (hi + Math.imul(ah8, bh9)) | 0; - var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; - w17 &= 0x3ffffff; - /* k = 18 */ - lo = Math.imul(al9, bl9); - mid = Math.imul(al9, bh9); - mid = (mid + Math.imul(ah9, bl9)) | 0; - hi = Math.imul(ah9, bh9); - var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; - w18 &= 0x3ffffff; - o[0] = w0; - o[1] = w1; - o[2] = w2; - o[3] = w3; - o[4] = w4; - o[5] = w5; - o[6] = w6; - o[7] = w7; - o[8] = w8; - o[9] = w9; - o[10] = w10; - o[11] = w11; - o[12] = w12; - o[13] = w13; - o[14] = w14; - o[15] = w15; - o[16] = w16; - o[17] = w17; - o[18] = w18; - if (c !== 0) { - o[19] = c; - out.length++; - } - return out; - }; - - // Polyfill comb - if (!Math.imul) { - comb10MulTo = smallMulTo; - } - - function bigMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - out.length = self.length + num.length; - - var carry = 0; - var hncarry = 0; - for (var k = 0; k < out.length - 1; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = hncarry; - hncarry = 0; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = k - j; - var a = self.words[i] | 0; - var b = num.words[j] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; - lo = (lo + rword) | 0; - rword = lo & 0x3ffffff; - ncarry = (ncarry + (lo >>> 26)) | 0; - - hncarry += ncarry >>> 26; - ncarry &= 0x3ffffff; - } - out.words[k] = rword; - carry = ncarry; - ncarry = hncarry; - } - if (carry !== 0) { - out.words[k] = carry; - } else { - out.length--; - } - - return out._strip(); - } - - function jumboMulTo (self, num, out) { - // Temporary disable, see https://github.com/indutny/bn.js/issues/211 - // var fftm = new FFTM(); - // return fftm.mulp(self, num, out); - return bigMulTo(self, num, out); - } - - BN.prototype.mulTo = function mulTo (num, out) { - var res; - var len = this.length + num.length; - if (this.length === 10 && num.length === 10) { - res = comb10MulTo(this, num, out); - } else if (len < 63) { - res = smallMulTo(this, num, out); - } else if (len < 1024) { - res = bigMulTo(this, num, out); - } else { - res = jumboMulTo(this, num, out); - } - - return res; - }; - - // Cooley-Tukey algorithm for FFT - // slightly revisited to rely on looping instead of recursion - - function FFTM (x, y) { - this.x = x; - this.y = y; - } - - FFTM.prototype.makeRBT = function makeRBT (N) { - var t = new Array(N); - var l = BN.prototype._countBits(N) - 1; - for (var i = 0; i < N; i++) { - t[i] = this.revBin(i, l, N); - } - - return t; - }; - - // Returns binary-reversed representation of `x` - FFTM.prototype.revBin = function revBin (x, l, N) { - if (x === 0 || x === N - 1) return x; - - var rb = 0; - for (var i = 0; i < l; i++) { - rb |= (x & 1) << (l - i - 1); - x >>= 1; - } - - return rb; - }; - - // Performs "tweedling" phase, therefore 'emulating' - // behaviour of the recursive algorithm - FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { - for (var i = 0; i < N; i++) { - rtws[i] = rws[rbt[i]]; - itws[i] = iws[rbt[i]]; - } - }; - - FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { - this.permute(rbt, rws, iws, rtws, itws, N); - - for (var s = 1; s < N; s <<= 1) { - var l = s << 1; - - var rtwdf = Math.cos(2 * Math.PI / l); - var itwdf = Math.sin(2 * Math.PI / l); - - for (var p = 0; p < N; p += l) { - var rtwdf_ = rtwdf; - var itwdf_ = itwdf; - - for (var j = 0; j < s; j++) { - var re = rtws[p + j]; - var ie = itws[p + j]; - - var ro = rtws[p + j + s]; - var io = itws[p + j + s]; - - var rx = rtwdf_ * ro - itwdf_ * io; - - io = rtwdf_ * io + itwdf_ * ro; - ro = rx; - - rtws[p + j] = re + ro; - itws[p + j] = ie + io; - - rtws[p + j + s] = re - ro; - itws[p + j + s] = ie - io; - - /* jshint maxdepth : false */ - if (j !== l) { - rx = rtwdf * rtwdf_ - itwdf * itwdf_; - - itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; - rtwdf_ = rx; - } - } - } - } - }; - - FFTM.prototype.guessLen13b = function guessLen13b (n, m) { - var N = Math.max(m, n) | 1; - var odd = N & 1; - var i = 0; - for (N = N / 2 | 0; N; N = N >>> 1) { - i++; - } - - return 1 << i + 1 + odd; - }; - - FFTM.prototype.conjugate = function conjugate (rws, iws, N) { - if (N <= 1) return; - - for (var i = 0; i < N / 2; i++) { - var t = rws[i]; - - rws[i] = rws[N - i - 1]; - rws[N - i - 1] = t; - - t = iws[i]; - - iws[i] = -iws[N - i - 1]; - iws[N - i - 1] = -t; - } - }; - - FFTM.prototype.normalize13b = function normalize13b (ws, N) { - var carry = 0; - for (var i = 0; i < N / 2; i++) { - var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + - Math.round(ws[2 * i] / N) + - carry; - - ws[i] = w & 0x3ffffff; - - if (w < 0x4000000) { - carry = 0; - } else { - carry = w / 0x4000000 | 0; - } - } - - return ws; - }; - - FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { - var carry = 0; - for (var i = 0; i < len; i++) { - carry = carry + (ws[i] | 0); - - rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; - rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; - } - - // Pad with zeroes - for (i = 2 * len; i < N; ++i) { - rws[i] = 0; - } - - assert(carry === 0); - assert((carry & ~0x1fff) === 0); - }; - - FFTM.prototype.stub = function stub (N) { - var ph = new Array(N); - for (var i = 0; i < N; i++) { - ph[i] = 0; - } - - return ph; - }; - - FFTM.prototype.mulp = function mulp (x, y, out) { - var N = 2 * this.guessLen13b(x.length, y.length); - - var rbt = this.makeRBT(N); - - var _ = this.stub(N); - - var rws = new Array(N); - var rwst = new Array(N); - var iwst = new Array(N); - - var nrws = new Array(N); - var nrwst = new Array(N); - var niwst = new Array(N); - - var rmws = out.words; - rmws.length = N; - - this.convert13b(x.words, x.length, rws, N); - this.convert13b(y.words, y.length, nrws, N); - - this.transform(rws, _, rwst, iwst, N, rbt); - this.transform(nrws, _, nrwst, niwst, N, rbt); - - for (var i = 0; i < N; i++) { - var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; - iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; - rwst[i] = rx; - } - - this.conjugate(rwst, iwst, N); - this.transform(rwst, iwst, rmws, _, N, rbt); - this.conjugate(rmws, _, N); - this.normalize13b(rmws, N); - - out.negative = x.negative ^ y.negative; - out.length = x.length + y.length; - return out._strip(); - }; - - // Multiply `this` by `num` - BN.prototype.mul = function mul (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return this.mulTo(num, out); - }; - - // Multiply employing FFT - BN.prototype.mulf = function mulf (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return jumboMulTo(this, num, out); - }; - - // In-place Multiplication - BN.prototype.imul = function imul (num) { - return this.clone().mulTo(num, this); - }; - - BN.prototype.imuln = function imuln (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(typeof num === 'number'); - assert(num < 0x4000000); - - // Carry - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = (this.words[i] | 0) * num; - var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); - carry >>= 26; - carry += (w / 0x4000000) | 0; - // NOTE: lo is 27bit maximum - carry += lo >>> 26; - this.words[i] = lo & 0x3ffffff; - } - - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - - return isNegNum ? this.ineg() : this; - }; - - BN.prototype.muln = function muln (num) { - return this.clone().imuln(num); - }; - - // `this` * `this` - BN.prototype.sqr = function sqr () { - return this.mul(this); - }; - - // `this` * `this` in-place - BN.prototype.isqr = function isqr () { - return this.imul(this.clone()); - }; - - // Math.pow(`this`, `num`) - BN.prototype.pow = function pow (num) { - var w = toBitArray(num); - if (w.length === 0) return new BN(1); - - // Skip leading zeroes - var res = this; - for (var i = 0; i < w.length; i++, res = res.sqr()) { - if (w[i] !== 0) break; - } - - if (++i < w.length) { - for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { - if (w[i] === 0) continue; - - res = res.mul(q); - } - } - - return res; - }; - - // Shift-left in-place - BN.prototype.iushln = function iushln (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); - var i; - - if (r !== 0) { - var carry = 0; - - for (i = 0; i < this.length; i++) { - var newCarry = this.words[i] & carryMask; - var c = ((this.words[i] | 0) - newCarry) << r; - this.words[i] = c | carry; - carry = newCarry >>> (26 - r); - } - - if (carry) { - this.words[i] = carry; - this.length++; - } - } - - if (s !== 0) { - for (i = this.length - 1; i >= 0; i--) { - this.words[i + s] = this.words[i]; - } - - for (i = 0; i < s; i++) { - this.words[i] = 0; - } - - this.length += s; - } - - return this._strip(); - }; - - BN.prototype.ishln = function ishln (bits) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushln(bits); - }; - - // Shift-right in-place - // NOTE: `hint` is a lowest bit before trailing zeroes - // NOTE: if `extended` is present - it will be filled with destroyed bits - BN.prototype.iushrn = function iushrn (bits, hint, extended) { - assert(typeof bits === 'number' && bits >= 0); - var h; - if (hint) { - h = (hint - (hint % 26)) / 26; - } else { - h = 0; - } - - var r = bits % 26; - var s = Math.min((bits - r) / 26, this.length); - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - var maskedWords = extended; - - h -= s; - h = Math.max(0, h); - - // Extended mode, copy masked part - if (maskedWords) { - for (var i = 0; i < s; i++) { - maskedWords.words[i] = this.words[i]; - } - maskedWords.length = s; - } - - if (s === 0) { - // No-op, we should not move anything at all - } else if (this.length > s) { - this.length -= s; - for (i = 0; i < this.length; i++) { - this.words[i] = this.words[i + s]; - } - } else { - this.words[0] = 0; - this.length = 1; - } - - var carry = 0; - for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { - var word = this.words[i] | 0; - this.words[i] = (carry << (26 - r)) | (word >>> r); - carry = word & mask; - } - - // Push carried bits as a mask - if (maskedWords && carry !== 0) { - maskedWords.words[maskedWords.length++] = carry; - } - - if (this.length === 0) { - this.words[0] = 0; - this.length = 1; - } - - return this._strip(); - }; - - BN.prototype.ishrn = function ishrn (bits, hint, extended) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushrn(bits, hint, extended); - }; - - // Shift-left - BN.prototype.shln = function shln (bits) { - return this.clone().ishln(bits); - }; - - BN.prototype.ushln = function ushln (bits) { - return this.clone().iushln(bits); - }; - - // Shift-right - BN.prototype.shrn = function shrn (bits) { - return this.clone().ishrn(bits); - }; - - BN.prototype.ushrn = function ushrn (bits) { - return this.clone().iushrn(bits); - }; - - // Test if n bit is set - BN.prototype.testn = function testn (bit) { - assert(typeof bit === 'number' && bit >= 0); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) return false; - - // Check bit and return - var w = this.words[s]; - - return !!(w & q); - }; - - // Return only lowers bits of number (in-place) - BN.prototype.imaskn = function imaskn (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - - assert(this.negative === 0, 'imaskn works only with positive numbers'); - - if (this.length <= s) { - return this; - } - - if (r !== 0) { - s++; - } - this.length = Math.min(s, this.length); - - if (r !== 0) { - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - this.words[this.length - 1] &= mask; - } - - return this._strip(); - }; - - // Return only lowers bits of number - BN.prototype.maskn = function maskn (bits) { - return this.clone().imaskn(bits); - }; - - // Add plain number `num` to `this` - BN.prototype.iaddn = function iaddn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.isubn(-num); - - // Possible sign change - if (this.negative !== 0) { - if (this.length === 1 && (this.words[0] | 0) <= num) { - this.words[0] = num - (this.words[0] | 0); - this.negative = 0; - return this; - } - - this.negative = 0; - this.isubn(num); - this.negative = 1; - return this; - } - - // Add without checks - return this._iaddn(num); - }; - - BN.prototype._iaddn = function _iaddn (num) { - this.words[0] += num; - - // Carry - for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { - this.words[i] -= 0x4000000; - if (i === this.length - 1) { - this.words[i + 1] = 1; - } else { - this.words[i + 1]++; - } - } - this.length = Math.max(this.length, i + 1); - - return this; - }; - - // Subtract plain number `num` from `this` - BN.prototype.isubn = function isubn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.iaddn(-num); - - if (this.negative !== 0) { - this.negative = 0; - this.iaddn(num); - this.negative = 1; - return this; - } - - this.words[0] -= num; - - if (this.length === 1 && this.words[0] < 0) { - this.words[0] = -this.words[0]; - this.negative = 1; - } else { - // Carry - for (var i = 0; i < this.length && this.words[i] < 0; i++) { - this.words[i] += 0x4000000; - this.words[i + 1] -= 1; - } - } - - return this._strip(); - }; - - BN.prototype.addn = function addn (num) { - return this.clone().iaddn(num); - }; - - BN.prototype.subn = function subn (num) { - return this.clone().isubn(num); - }; - - BN.prototype.iabs = function iabs () { - this.negative = 0; - - return this; - }; - - BN.prototype.abs = function abs () { - return this.clone().iabs(); - }; - - BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { - var len = num.length + shift; - var i; - - this._expand(len); - - var w; - var carry = 0; - for (i = 0; i < num.length; i++) { - w = (this.words[i + shift] | 0) + carry; - var right = (num.words[i] | 0) * mul; - w -= right & 0x3ffffff; - carry = (w >> 26) - ((right / 0x4000000) | 0); - this.words[i + shift] = w & 0x3ffffff; - } - for (; i < this.length - shift; i++) { - w = (this.words[i + shift] | 0) + carry; - carry = w >> 26; - this.words[i + shift] = w & 0x3ffffff; - } - - if (carry === 0) return this._strip(); - - // Subtraction overflow - assert(carry === -1); - carry = 0; - for (i = 0; i < this.length; i++) { - w = -(this.words[i] | 0) + carry; - carry = w >> 26; - this.words[i] = w & 0x3ffffff; - } - this.negative = 1; - - return this._strip(); - }; - - BN.prototype._wordDiv = function _wordDiv (num, mode) { - var shift = this.length - num.length; - - var a = this.clone(); - var b = num; - - // Normalize - var bhi = b.words[b.length - 1] | 0; - var bhiBits = this._countBits(bhi); - shift = 26 - bhiBits; - if (shift !== 0) { - b = b.ushln(shift); - a.iushln(shift); - bhi = b.words[b.length - 1] | 0; - } - - // Initialize quotient - var m = a.length - b.length; - var q; - - if (mode !== 'mod') { - q = new BN(null); - q.length = m + 1; - q.words = new Array(q.length); - for (var i = 0; i < q.length; i++) { - q.words[i] = 0; - } - } - - var diff = a.clone()._ishlnsubmul(b, 1, m); - if (diff.negative === 0) { - a = diff; - if (q) { - q.words[m] = 1; - } - } - - for (var j = m - 1; j >= 0; j--) { - var qj = (a.words[b.length + j] | 0) * 0x4000000 + - (a.words[b.length + j - 1] | 0); - - // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max - // (0x7ffffff) - qj = Math.min((qj / bhi) | 0, 0x3ffffff); - - a._ishlnsubmul(b, qj, j); - while (a.negative !== 0) { - qj--; - a.negative = 0; - a._ishlnsubmul(b, 1, j); - if (!a.isZero()) { - a.negative ^= 1; - } - } - if (q) { - q.words[j] = qj; - } - } - if (q) { - q._strip(); - } - a._strip(); - - // Denormalize - if (mode !== 'div' && shift !== 0) { - a.iushrn(shift); - } - - return { - div: q || null, - mod: a - }; - }; - - // NOTE: 1) `mode` can be set to `mod` to request mod only, - // to `div` to request div only, or be absent to - // request both div & mod - // 2) `positive` is true if unsigned mod is requested - BN.prototype.divmod = function divmod (num, mode, positive) { - assert(!num.isZero()); - - if (this.isZero()) { - return { - div: new BN(0), - mod: new BN(0) - }; - } - - var div, mod, res; - if (this.negative !== 0 && num.negative === 0) { - res = this.neg().divmod(num, mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.iadd(num); - } - } - - return { - div: div, - mod: mod - }; - } - - if (this.negative === 0 && num.negative !== 0) { - res = this.divmod(num.neg(), mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - return { - div: div, - mod: res.mod - }; - } - - if ((this.negative & num.negative) !== 0) { - res = this.neg().divmod(num.neg(), mode); - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.isub(num); - } - } - - return { - div: res.div, - mod: mod - }; - } - - // Both numbers are positive at this point - - // Strip both numbers to approximate shift value - if (num.length > this.length || this.cmp(num) < 0) { - return { - div: new BN(0), - mod: this - }; - } - - // Very short reduction - if (num.length === 1) { - if (mode === 'div') { - return { - div: this.divn(num.words[0]), - mod: null - }; - } - - if (mode === 'mod') { - return { - div: null, - mod: new BN(this.modrn(num.words[0])) - }; - } - - return { - div: this.divn(num.words[0]), - mod: new BN(this.modrn(num.words[0])) - }; - } - - return this._wordDiv(num, mode); - }; - - // Find `this` / `num` - BN.prototype.div = function div (num) { - return this.divmod(num, 'div', false).div; - }; - - // Find `this` % `num` - BN.prototype.mod = function mod (num) { - return this.divmod(num, 'mod', false).mod; - }; - - BN.prototype.umod = function umod (num) { - return this.divmod(num, 'mod', true).mod; - }; - - // Find Round(`this` / `num`) - BN.prototype.divRound = function divRound (num) { - var dm = this.divmod(num); - - // Fast case - exact division - if (dm.mod.isZero()) return dm.div; - - var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; - - var half = num.ushrn(1); - var r2 = num.andln(1); - var cmp = mod.cmp(half); - - // Round down - if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; - - // Round up - return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); - }; - - BN.prototype.modrn = function modrn (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(num <= 0x3ffffff); - var p = (1 << 26) % num; - - var acc = 0; - for (var i = this.length - 1; i >= 0; i--) { - acc = (p * acc + (this.words[i] | 0)) % num; - } - - return isNegNum ? -acc : acc; - }; - - // WARNING: DEPRECATED - BN.prototype.modn = function modn (num) { - return this.modrn(num); - }; - - // In-place division by number - BN.prototype.idivn = function idivn (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(num <= 0x3ffffff); - - var carry = 0; - for (var i = this.length - 1; i >= 0; i--) { - var w = (this.words[i] | 0) + carry * 0x4000000; - this.words[i] = (w / num) | 0; - carry = w % num; - } - - this._strip(); - return isNegNum ? this.ineg() : this; - }; - - BN.prototype.divn = function divn (num) { - return this.clone().idivn(num); - }; - - BN.prototype.egcd = function egcd (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var x = this; - var y = p.clone(); - - if (x.negative !== 0) { - x = x.umod(p); - } else { - x = x.clone(); - } - - // A * x + B * y = x - var A = new BN(1); - var B = new BN(0); - - // C * x + D * y = y - var C = new BN(0); - var D = new BN(1); - - var g = 0; - - while (x.isEven() && y.isEven()) { - x.iushrn(1); - y.iushrn(1); - ++g; - } - - var yp = y.clone(); - var xp = x.clone(); - - while (!x.isZero()) { - for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - x.iushrn(i); - while (i-- > 0) { - if (A.isOdd() || B.isOdd()) { - A.iadd(yp); - B.isub(xp); - } - - A.iushrn(1); - B.iushrn(1); - } - } - - for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - y.iushrn(j); - while (j-- > 0) { - if (C.isOdd() || D.isOdd()) { - C.iadd(yp); - D.isub(xp); - } - - C.iushrn(1); - D.iushrn(1); - } - } - - if (x.cmp(y) >= 0) { - x.isub(y); - A.isub(C); - B.isub(D); - } else { - y.isub(x); - C.isub(A); - D.isub(B); - } - } - - return { - a: C, - b: D, - gcd: y.iushln(g) - }; - }; - - // This is reduced incarnation of the binary EEA - // above, designated to invert members of the - // _prime_ fields F(p) at a maximal speed - BN.prototype._invmp = function _invmp (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var a = this; - var b = p.clone(); - - if (a.negative !== 0) { - a = a.umod(p); - } else { - a = a.clone(); - } - - var x1 = new BN(1); - var x2 = new BN(0); - - var delta = b.clone(); - - while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { - for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - a.iushrn(i); - while (i-- > 0) { - if (x1.isOdd()) { - x1.iadd(delta); - } - - x1.iushrn(1); - } - } - - for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - b.iushrn(j); - while (j-- > 0) { - if (x2.isOdd()) { - x2.iadd(delta); - } - - x2.iushrn(1); - } - } - - if (a.cmp(b) >= 0) { - a.isub(b); - x1.isub(x2); - } else { - b.isub(a); - x2.isub(x1); - } - } - - var res; - if (a.cmpn(1) === 0) { - res = x1; - } else { - res = x2; - } - - if (res.cmpn(0) < 0) { - res.iadd(p); - } - - return res; - }; - - BN.prototype.gcd = function gcd (num) { - if (this.isZero()) return num.abs(); - if (num.isZero()) return this.abs(); - - var a = this.clone(); - var b = num.clone(); - a.negative = 0; - b.negative = 0; - - // Remove common factor of two - for (var shift = 0; a.isEven() && b.isEven(); shift++) { - a.iushrn(1); - b.iushrn(1); - } - - do { - while (a.isEven()) { - a.iushrn(1); - } - while (b.isEven()) { - b.iushrn(1); - } - - var r = a.cmp(b); - if (r < 0) { - // Swap `a` and `b` to make `a` always bigger than `b` - var t = a; - a = b; - b = t; - } else if (r === 0 || b.cmpn(1) === 0) { - break; - } - - a.isub(b); - } while (true); - - return b.iushln(shift); - }; - - // Invert number in the field F(num) - BN.prototype.invm = function invm (num) { - return this.egcd(num).a.umod(num); - }; - - BN.prototype.isEven = function isEven () { - return (this.words[0] & 1) === 0; - }; - - BN.prototype.isOdd = function isOdd () { - return (this.words[0] & 1) === 1; - }; - - // And first word and num - BN.prototype.andln = function andln (num) { - return this.words[0] & num; - }; - - // Increment at the bit position in-line - BN.prototype.bincn = function bincn (bit) { - assert(typeof bit === 'number'); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) { - this._expand(s + 1); - this.words[s] |= q; - return this; - } - - // Add bit and propagate, if needed - var carry = q; - for (var i = s; carry !== 0 && i < this.length; i++) { - var w = this.words[i] | 0; - w += carry; - carry = w >>> 26; - w &= 0x3ffffff; - this.words[i] = w; - } - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - return this; - }; - - BN.prototype.isZero = function isZero () { - return this.length === 1 && this.words[0] === 0; - }; - - BN.prototype.cmpn = function cmpn (num) { - var negative = num < 0; - - if (this.negative !== 0 && !negative) return -1; - if (this.negative === 0 && negative) return 1; - - this._strip(); - - var res; - if (this.length > 1) { - res = 1; - } else { - if (negative) { - num = -num; - } - - assert(num <= 0x3ffffff, 'Number is too big'); - - var w = this.words[0] | 0; - res = w === num ? 0 : w < num ? -1 : 1; - } - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Compare two numbers and return: - // 1 - if `this` > `num` - // 0 - if `this` == `num` - // -1 - if `this` < `num` - BN.prototype.cmp = function cmp (num) { - if (this.negative !== 0 && num.negative === 0) return -1; - if (this.negative === 0 && num.negative !== 0) return 1; - - var res = this.ucmp(num); - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Unsigned comparison - BN.prototype.ucmp = function ucmp (num) { - // At this point both numbers have the same sign - if (this.length > num.length) return 1; - if (this.length < num.length) return -1; - - var res = 0; - for (var i = this.length - 1; i >= 0; i--) { - var a = this.words[i] | 0; - var b = num.words[i] | 0; - - if (a === b) continue; - if (a < b) { - res = -1; - } else if (a > b) { - res = 1; - } - break; - } - return res; - }; - - BN.prototype.gtn = function gtn (num) { - return this.cmpn(num) === 1; - }; - - BN.prototype.gt = function gt (num) { - return this.cmp(num) === 1; - }; - - BN.prototype.gten = function gten (num) { - return this.cmpn(num) >= 0; - }; - - BN.prototype.gte = function gte (num) { - return this.cmp(num) >= 0; - }; - - BN.prototype.ltn = function ltn (num) { - return this.cmpn(num) === -1; - }; - - BN.prototype.lt = function lt (num) { - return this.cmp(num) === -1; - }; - - BN.prototype.lten = function lten (num) { - return this.cmpn(num) <= 0; - }; - - BN.prototype.lte = function lte (num) { - return this.cmp(num) <= 0; - }; - - BN.prototype.eqn = function eqn (num) { - return this.cmpn(num) === 0; - }; - - BN.prototype.eq = function eq (num) { - return this.cmp(num) === 0; - }; - - // - // A reduce context, could be using montgomery or something better, depending - // on the `m` itself. - // - BN.red = function red (num) { - return new Red(num); - }; - - BN.prototype.toRed = function toRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - assert(this.negative === 0, 'red works only with positives'); - return ctx.convertTo(this)._forceRed(ctx); - }; - - BN.prototype.fromRed = function fromRed () { - assert(this.red, 'fromRed works only with numbers in reduction context'); - return this.red.convertFrom(this); - }; - - BN.prototype._forceRed = function _forceRed (ctx) { - this.red = ctx; - return this; - }; - - BN.prototype.forceRed = function forceRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - return this._forceRed(ctx); - }; - - BN.prototype.redAdd = function redAdd (num) { - assert(this.red, 'redAdd works only with red numbers'); - return this.red.add(this, num); - }; - - BN.prototype.redIAdd = function redIAdd (num) { - assert(this.red, 'redIAdd works only with red numbers'); - return this.red.iadd(this, num); - }; - - BN.prototype.redSub = function redSub (num) { - assert(this.red, 'redSub works only with red numbers'); - return this.red.sub(this, num); - }; - - BN.prototype.redISub = function redISub (num) { - assert(this.red, 'redISub works only with red numbers'); - return this.red.isub(this, num); - }; - - BN.prototype.redShl = function redShl (num) { - assert(this.red, 'redShl works only with red numbers'); - return this.red.shl(this, num); - }; - - BN.prototype.redMul = function redMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.mul(this, num); - }; - - BN.prototype.redIMul = function redIMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.imul(this, num); - }; - - BN.prototype.redSqr = function redSqr () { - assert(this.red, 'redSqr works only with red numbers'); - this.red._verify1(this); - return this.red.sqr(this); - }; - - BN.prototype.redISqr = function redISqr () { - assert(this.red, 'redISqr works only with red numbers'); - this.red._verify1(this); - return this.red.isqr(this); - }; - - // Square root over p - BN.prototype.redSqrt = function redSqrt () { - assert(this.red, 'redSqrt works only with red numbers'); - this.red._verify1(this); - return this.red.sqrt(this); - }; - - BN.prototype.redInvm = function redInvm () { - assert(this.red, 'redInvm works only with red numbers'); - this.red._verify1(this); - return this.red.invm(this); - }; - - // Return negative clone of `this` % `red modulo` - BN.prototype.redNeg = function redNeg () { - assert(this.red, 'redNeg works only with red numbers'); - this.red._verify1(this); - return this.red.neg(this); - }; - - BN.prototype.redPow = function redPow (num) { - assert(this.red && !num.red, 'redPow(normalNum)'); - this.red._verify1(this); - return this.red.pow(this, num); - }; - - // Prime numbers with efficient reduction - var primes = { - k256: null, - p224: null, - p192: null, - p25519: null - }; - - // Pseudo-Mersenne prime - function MPrime (name, p) { - // P = 2 ^ N - K - this.name = name; - this.p = new BN(p, 16); - this.n = this.p.bitLength(); - this.k = new BN(1).iushln(this.n).isub(this.p); - - this.tmp = this._tmp(); - } - - MPrime.prototype._tmp = function _tmp () { - var tmp = new BN(null); - tmp.words = new Array(Math.ceil(this.n / 13)); - return tmp; - }; - - MPrime.prototype.ireduce = function ireduce (num) { - // Assumes that `num` is less than `P^2` - // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) - var r = num; - var rlen; - - do { - this.split(r, this.tmp); - r = this.imulK(r); - r = r.iadd(this.tmp); - rlen = r.bitLength(); - } while (rlen > this.n); - - var cmp = rlen < this.n ? -1 : r.ucmp(this.p); - if (cmp === 0) { - r.words[0] = 0; - r.length = 1; - } else if (cmp > 0) { - r.isub(this.p); - } else { - if (r.strip !== undefined) { - // r is a BN v4 instance - r.strip(); - } else { - // r is a BN v5 instance - r._strip(); - } - } - - return r; - }; - - MPrime.prototype.split = function split (input, out) { - input.iushrn(this.n, 0, out); - }; - - MPrime.prototype.imulK = function imulK (num) { - return num.imul(this.k); - }; - - function K256 () { - MPrime.call( - this, - 'k256', - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); - } - inherits(K256, MPrime); - - K256.prototype.split = function split (input, output) { - // 256 = 9 * 26 + 22 - var mask = 0x3fffff; - - var outLen = Math.min(input.length, 9); - for (var i = 0; i < outLen; i++) { - output.words[i] = input.words[i]; - } - output.length = outLen; - - if (input.length <= 9) { - input.words[0] = 0; - input.length = 1; - return; - } - - // Shift by 9 limbs - var prev = input.words[9]; - output.words[output.length++] = prev & mask; - - for (i = 10; i < input.length; i++) { - var next = input.words[i] | 0; - input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); - prev = next; - } - prev >>>= 22; - input.words[i - 10] = prev; - if (prev === 0 && input.length > 10) { - input.length -= 10; - } else { - input.length -= 9; - } - }; - - K256.prototype.imulK = function imulK (num) { - // K = 0x1000003d1 = [ 0x40, 0x3d1 ] - num.words[num.length] = 0; - num.words[num.length + 1] = 0; - num.length += 2; - - // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 - var lo = 0; - for (var i = 0; i < num.length; i++) { - var w = num.words[i] | 0; - lo += w * 0x3d1; - num.words[i] = lo & 0x3ffffff; - lo = w * 0x40 + ((lo / 0x4000000) | 0); - } - - // Fast length reduction - if (num.words[num.length - 1] === 0) { - num.length--; - if (num.words[num.length - 1] === 0) { - num.length--; - } - } - return num; - }; - - function P224 () { - MPrime.call( - this, - 'p224', - 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); - } - inherits(P224, MPrime); - - function P192 () { - MPrime.call( - this, - 'p192', - 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); - } - inherits(P192, MPrime); - - function P25519 () { - // 2 ^ 255 - 19 - MPrime.call( - this, - '25519', - '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); - } - inherits(P25519, MPrime); - - P25519.prototype.imulK = function imulK (num) { - // K = 0x13 - var carry = 0; - for (var i = 0; i < num.length; i++) { - var hi = (num.words[i] | 0) * 0x13 + carry; - var lo = hi & 0x3ffffff; - hi >>>= 26; - - num.words[i] = lo; - carry = hi; - } - if (carry !== 0) { - num.words[num.length++] = carry; - } - return num; - }; - - // Exported mostly for testing purposes, use plain name instead - BN._prime = function prime (name) { - // Cached version of prime - if (primes[name]) return primes[name]; - - var prime; - if (name === 'k256') { - prime = new K256(); - } else if (name === 'p224') { - prime = new P224(); - } else if (name === 'p192') { - prime = new P192(); - } else if (name === 'p25519') { - prime = new P25519(); - } else { - throw new Error('Unknown prime ' + name); - } - primes[name] = prime; - - return prime; - }; - - // - // Base reduction engine - // - function Red (m) { - if (typeof m === 'string') { - var prime = BN._prime(m); - this.m = prime.p; - this.prime = prime; - } else { - assert(m.gtn(1), 'modulus must be greater than 1'); - this.m = m; - this.prime = null; - } - } - - Red.prototype._verify1 = function _verify1 (a) { - assert(a.negative === 0, 'red works only with positives'); - assert(a.red, 'red works only with red numbers'); - }; - - Red.prototype._verify2 = function _verify2 (a, b) { - assert((a.negative | b.negative) === 0, 'red works only with positives'); - assert(a.red && a.red === b.red, - 'red works only with red numbers'); - }; - - Red.prototype.imod = function imod (a) { - if (this.prime) return this.prime.ireduce(a)._forceRed(this); - - move(a, a.umod(this.m)._forceRed(this)); - return a; - }; - - Red.prototype.neg = function neg (a) { - if (a.isZero()) { - return a.clone(); - } - - return this.m.sub(a)._forceRed(this); - }; - - Red.prototype.add = function add (a, b) { - this._verify2(a, b); - - var res = a.add(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.iadd = function iadd (a, b) { - this._verify2(a, b); - - var res = a.iadd(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res; - }; - - Red.prototype.sub = function sub (a, b) { - this._verify2(a, b); - - var res = a.sub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.isub = function isub (a, b) { - this._verify2(a, b); - - var res = a.isub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res; - }; - - Red.prototype.shl = function shl (a, num) { - this._verify1(a); - return this.imod(a.ushln(num)); - }; - - Red.prototype.imul = function imul (a, b) { - this._verify2(a, b); - return this.imod(a.imul(b)); - }; - - Red.prototype.mul = function mul (a, b) { - this._verify2(a, b); - return this.imod(a.mul(b)); - }; - - Red.prototype.isqr = function isqr (a) { - return this.imul(a, a.clone()); - }; - - Red.prototype.sqr = function sqr (a) { - return this.mul(a, a); - }; - - Red.prototype.sqrt = function sqrt (a) { - if (a.isZero()) return a.clone(); - - var mod3 = this.m.andln(3); - assert(mod3 % 2 === 1); - - // Fast case - if (mod3 === 3) { - var pow = this.m.add(new BN(1)).iushrn(2); - return this.pow(a, pow); - } - - // Tonelli-Shanks algorithm (Totally unoptimized and slow) - // - // Find Q and S, that Q * 2 ^ S = (P - 1) - var q = this.m.subn(1); - var s = 0; - while (!q.isZero() && q.andln(1) === 0) { - s++; - q.iushrn(1); - } - assert(!q.isZero()); - - var one = new BN(1).toRed(this); - var nOne = one.redNeg(); - - // Find quadratic non-residue - // NOTE: Max is such because of generalized Riemann hypothesis. - var lpow = this.m.subn(1).iushrn(1); - var z = this.m.bitLength(); - z = new BN(2 * z * z).toRed(this); - - while (this.pow(z, lpow).cmp(nOne) !== 0) { - z.redIAdd(nOne); - } - - var c = this.pow(z, q); - var r = this.pow(a, q.addn(1).iushrn(1)); - var t = this.pow(a, q); - var m = s; - while (t.cmp(one) !== 0) { - var tmp = t; - for (var i = 0; tmp.cmp(one) !== 0; i++) { - tmp = tmp.redSqr(); - } - assert(i < m); - var b = this.pow(c, new BN(1).iushln(m - i - 1)); - - r = r.redMul(b); - c = b.redSqr(); - t = t.redMul(c); - m = i; - } - - return r; - }; - - Red.prototype.invm = function invm (a) { - var inv = a._invmp(this.m); - if (inv.negative !== 0) { - inv.negative = 0; - return this.imod(inv).redNeg(); - } else { - return this.imod(inv); - } - }; - - Red.prototype.pow = function pow (a, num) { - if (num.isZero()) return new BN(1).toRed(this); - if (num.cmpn(1) === 0) return a.clone(); - - var windowSize = 4; - var wnd = new Array(1 << windowSize); - wnd[0] = new BN(1).toRed(this); - wnd[1] = a; - for (var i = 2; i < wnd.length; i++) { - wnd[i] = this.mul(wnd[i - 1], a); - } - - var res = wnd[0]; - var current = 0; - var currentLen = 0; - var start = num.bitLength() % 26; - if (start === 0) { - start = 26; - } - - for (i = num.length - 1; i >= 0; i--) { - var word = num.words[i]; - for (var j = start - 1; j >= 0; j--) { - var bit = (word >> j) & 1; - if (res !== wnd[0]) { - res = this.sqr(res); - } - - if (bit === 0 && current === 0) { - currentLen = 0; - continue; - } - - current <<= 1; - current |= bit; - currentLen++; - if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; - - res = this.mul(res, wnd[current]); - currentLen = 0; - current = 0; - } - start = 26; - } - - return res; - }; - - Red.prototype.convertTo = function convertTo (num) { - var r = num.umod(this.m); - - return r === num ? r.clone() : r; - }; - - Red.prototype.convertFrom = function convertFrom (num) { - var res = num.clone(); - res.red = null; - return res; - }; - - // - // Montgomery method engine - // - - BN.mont = function mont (num) { - return new Mont(num); - }; - - function Mont (m) { - Red.call(this, m); - - this.shift = this.m.bitLength(); - if (this.shift % 26 !== 0) { - this.shift += 26 - (this.shift % 26); - } - - this.r = new BN(1).iushln(this.shift); - this.r2 = this.imod(this.r.sqr()); - this.rinv = this.r._invmp(this.m); - - this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); - this.minv = this.minv.umod(this.r); - this.minv = this.r.sub(this.minv); - } - inherits(Mont, Red); - - Mont.prototype.convertTo = function convertTo (num) { - return this.imod(num.ushln(this.shift)); - }; - - Mont.prototype.convertFrom = function convertFrom (num) { - var r = this.imod(num.mul(this.rinv)); - r.red = null; - return r; - }; - - Mont.prototype.imul = function imul (a, b) { - if (a.isZero() || b.isZero()) { - a.words[0] = 0; - a.length = 1; - return a; - } - - var t = a.imul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.mul = function mul (a, b) { - if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); - - var t = a.mul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.invm = function invm (a) { - // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R - var res = this.imod(a._invmp(this.m).mul(this.r2)); - return res._forceRed(this); - }; -})(typeof module === 'undefined' || module, this); - -},{"buffer":20}],43:[function(require,module,exports){ -module.exports = require('./browser/algorithms.json') - -},{"./browser/algorithms.json":44}],44:[function(require,module,exports){ -module.exports={ - "sha224WithRSAEncryption": { - "sign": "rsa", - "hash": "sha224", - "id": "302d300d06096086480165030402040500041c" - }, - "RSA-SHA224": { - "sign": "ecdsa/rsa", - "hash": "sha224", - "id": "302d300d06096086480165030402040500041c" - }, - "sha256WithRSAEncryption": { - "sign": "rsa", - "hash": "sha256", - "id": "3031300d060960864801650304020105000420" - }, - "RSA-SHA256": { - "sign": "ecdsa/rsa", - "hash": "sha256", - "id": "3031300d060960864801650304020105000420" - }, - "sha384WithRSAEncryption": { - "sign": "rsa", - "hash": "sha384", - "id": "3041300d060960864801650304020205000430" - }, - "RSA-SHA384": { - "sign": "ecdsa/rsa", - "hash": "sha384", - "id": "3041300d060960864801650304020205000430" - }, - "sha512WithRSAEncryption": { - "sign": "rsa", - "hash": "sha512", - "id": "3051300d060960864801650304020305000440" - }, - "RSA-SHA512": { - "sign": "ecdsa/rsa", - "hash": "sha512", - "id": "3051300d060960864801650304020305000440" - }, - "RSA-SHA1": { - "sign": "rsa", - "hash": "sha1", - "id": "3021300906052b0e03021a05000414" - }, - "ecdsa-with-SHA1": { - "sign": "ecdsa", - "hash": "sha1", - "id": "" - }, - "sha256": { - "sign": "ecdsa", - "hash": "sha256", - "id": "" - }, - "sha224": { - "sign": "ecdsa", - "hash": "sha224", - "id": "" - }, - "sha384": { - "sign": "ecdsa", - "hash": "sha384", - "id": "" - }, - "sha512": { - "sign": "ecdsa", - "hash": "sha512", - "id": "" - }, - "DSA-SHA": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA-SHA1": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA-WITH-SHA224": { - "sign": "dsa", - "hash": "sha224", - "id": "" - }, - "DSA-SHA224": { - "sign": "dsa", - "hash": "sha224", - "id": "" - }, - "DSA-WITH-SHA256": { - "sign": "dsa", - "hash": "sha256", - "id": "" - }, - "DSA-SHA256": { - "sign": "dsa", - "hash": "sha256", - "id": "" - }, - "DSA-WITH-SHA384": { - "sign": "dsa", - "hash": "sha384", - "id": "" - }, - "DSA-SHA384": { - "sign": "dsa", - "hash": "sha384", - "id": "" - }, - "DSA-WITH-SHA512": { - "sign": "dsa", - "hash": "sha512", - "id": "" - }, - "DSA-SHA512": { - "sign": "dsa", - "hash": "sha512", - "id": "" - }, - "DSA-RIPEMD160": { - "sign": "dsa", - "hash": "rmd160", - "id": "" - }, - "ripemd160WithRSA": { - "sign": "rsa", - "hash": "rmd160", - "id": "3021300906052b2403020105000414" - }, - "RSA-RIPEMD160": { - "sign": "rsa", - "hash": "rmd160", - "id": "3021300906052b2403020105000414" - }, - "md5WithRSAEncryption": { - "sign": "rsa", - "hash": "md5", - "id": "3020300c06082a864886f70d020505000410" - }, - "RSA-MD5": { - "sign": "rsa", - "hash": "md5", - "id": "3020300c06082a864886f70d020505000410" - } -} - -},{}],45:[function(require,module,exports){ -module.exports={ - "1.3.132.0.10": "secp256k1", - "1.3.132.0.33": "p224", - "1.2.840.10045.3.1.1": "p192", - "1.2.840.10045.3.1.7": "p256", - "1.3.132.0.34": "p384", - "1.3.132.0.35": "p521" -} - -},{}],46:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var createHash = require('create-hash') -var stream = require('readable-stream') -var inherits = require('inherits') -var sign = require('./sign') -var verify = require('./verify') - -var algorithms = require('./algorithms.json') -Object.keys(algorithms).forEach(function (key) { - algorithms[key].id = Buffer.from(algorithms[key].id, 'hex') - algorithms[key.toLowerCase()] = algorithms[key] -}) - -function Sign (algorithm) { - stream.Writable.call(this) - - var data = algorithms[algorithm] - if (!data) throw new Error('Unknown message digest') - - this._hashType = data.hash - this._hash = createHash(data.hash) - this._tag = data.id - this._signType = data.sign -} -inherits(Sign, stream.Writable) - -Sign.prototype._write = function _write (data, _, done) { - this._hash.update(data) - done() -} - -Sign.prototype.update = function update (data, enc) { - if (typeof data === 'string') data = Buffer.from(data, enc) - - this._hash.update(data) - return this -} - -Sign.prototype.sign = function signMethod (key, enc) { - this.end() - var hash = this._hash.digest() - var sig = sign(hash, key, this._hashType, this._signType, this._tag) - - return enc ? sig.toString(enc) : sig -} - -function Verify (algorithm) { - stream.Writable.call(this) - - var data = algorithms[algorithm] - if (!data) throw new Error('Unknown message digest') - - this._hash = createHash(data.hash) - this._tag = data.id - this._signType = data.sign -} -inherits(Verify, stream.Writable) - -Verify.prototype._write = function _write (data, _, done) { - this._hash.update(data) - done() -} - -Verify.prototype.update = function update (data, enc) { - if (typeof data === 'string') data = Buffer.from(data, enc) - - this._hash.update(data) - return this -} - -Verify.prototype.verify = function verifyMethod (key, sig, enc) { - if (typeof sig === 'string') sig = Buffer.from(sig, enc) - - this.end() - var hash = this._hash.digest() - return verify(sig, hash, key, this._signType, this._tag) -} - -function createSign (algorithm) { - return new Sign(algorithm) -} - -function createVerify (algorithm) { - return new Verify(algorithm) -} - -module.exports = { - Sign: createSign, - Verify: createVerify, - createSign: createSign, - createVerify: createVerify -} - -},{"./algorithms.json":44,"./sign":47,"./verify":48,"create-hash":74,"inherits":146,"readable-stream":64,"safe-buffer":250}],47:[function(require,module,exports){ -// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js -var Buffer = require('safe-buffer').Buffer -var createHmac = require('create-hmac') -var crt = require('browserify-rsa') -var EC = require('elliptic').ec -var BN = require('bn.js') -var parseKeys = require('parse-asn1') -var curves = require('./curves.json') - -function sign (hash, key, hashType, signType, tag) { - var priv = parseKeys(key) - if (priv.curve) { - // rsa keys can be interpreted as ecdsa ones in openssl - if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') throw new Error('wrong private key type') - return ecSign(hash, priv) - } else if (priv.type === 'dsa') { - if (signType !== 'dsa') throw new Error('wrong private key type') - return dsaSign(hash, priv, hashType) - } else { - if (signType !== 'rsa' && signType !== 'ecdsa/rsa') throw new Error('wrong private key type') - } - hash = Buffer.concat([tag, hash]) - var len = priv.modulus.byteLength() - var pad = [0, 1] - while (hash.length + pad.length + 1 < len) pad.push(0xff) - pad.push(0x00) - var i = -1 - while (++i < hash.length) pad.push(hash[i]) - - var out = crt(pad, priv) - return out -} - -function ecSign (hash, priv) { - var curveId = curves[priv.curve.join('.')] - if (!curveId) throw new Error('unknown curve ' + priv.curve.join('.')) - - var curve = new EC(curveId) - var key = curve.keyFromPrivate(priv.privateKey) - var out = key.sign(hash) - - return Buffer.from(out.toDER()) -} - -function dsaSign (hash, priv, algo) { - var x = priv.params.priv_key - var p = priv.params.p - var q = priv.params.q - var g = priv.params.g - var r = new BN(0) - var k - var H = bits2int(hash, q).mod(q) - var s = false - var kv = getKey(x, q, hash, algo) - while (s === false) { - k = makeKey(q, kv, algo) - r = makeR(g, k, p, q) - s = k.invm(q).imul(H.add(x.mul(r))).mod(q) - if (s.cmpn(0) === 0) { - s = false - r = new BN(0) - } - } - return toDER(r, s) -} - -function toDER (r, s) { - r = r.toArray() - s = s.toArray() - - // Pad values - if (r[0] & 0x80) r = [0].concat(r) - if (s[0] & 0x80) s = [0].concat(s) - - var total = r.length + s.length + 4 - var res = [0x30, total, 0x02, r.length] - res = res.concat(r, [0x02, s.length], s) - return Buffer.from(res) -} - -function getKey (x, q, hash, algo) { - x = Buffer.from(x.toArray()) - if (x.length < q.byteLength()) { - var zeros = Buffer.alloc(q.byteLength() - x.length) - x = Buffer.concat([zeros, x]) - } - var hlen = hash.length - var hbits = bits2octets(hash, q) - var v = Buffer.alloc(hlen) - v.fill(1) - var k = Buffer.alloc(hlen) - k = createHmac(algo, k).update(v).update(Buffer.from([0])).update(x).update(hbits).digest() - v = createHmac(algo, k).update(v).digest() - k = createHmac(algo, k).update(v).update(Buffer.from([1])).update(x).update(hbits).digest() - v = createHmac(algo, k).update(v).digest() - return { k: k, v: v } -} - -function bits2int (obits, q) { - var bits = new BN(obits) - var shift = (obits.length << 3) - q.bitLength() - if (shift > 0) bits.ishrn(shift) - return bits -} - -function bits2octets (bits, q) { - bits = bits2int(bits, q) - bits = bits.mod(q) - var out = Buffer.from(bits.toArray()) - if (out.length < q.byteLength()) { - var zeros = Buffer.alloc(q.byteLength() - out.length) - out = Buffer.concat([zeros, out]) - } - return out -} - -function makeKey (q, kv, algo) { - var t - var k - - do { - t = Buffer.alloc(0) - - while (t.length * 8 < q.bitLength()) { - kv.v = createHmac(algo, kv.k).update(kv.v).digest() - t = Buffer.concat([t, kv.v]) - } - - k = bits2int(t, q) - kv.k = createHmac(algo, kv.k).update(kv.v).update(Buffer.from([0])).digest() - kv.v = createHmac(algo, kv.k).update(kv.v).digest() - } while (k.cmp(q) !== -1) - - return k -} - -function makeR (g, k, p, q) { - return g.toRed(BN.mont(p)).redPow(k).fromRed().mod(q) -} - -module.exports = sign -module.exports.getKey = getKey -module.exports.makeKey = makeKey - -},{"./curves.json":45,"bn.js":49,"browserify-rsa":41,"create-hmac":76,"elliptic":89,"parse-asn1":230,"safe-buffer":250}],48:[function(require,module,exports){ -// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js -var Buffer = require('safe-buffer').Buffer -var BN = require('bn.js') -var EC = require('elliptic').ec -var parseKeys = require('parse-asn1') -var curves = require('./curves.json') - -function verify (sig, hash, key, signType, tag) { - var pub = parseKeys(key) - if (pub.type === 'ec') { - // rsa keys can be interpreted as ecdsa ones in openssl - if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') throw new Error('wrong public key type') - return ecVerify(sig, hash, pub) - } else if (pub.type === 'dsa') { - if (signType !== 'dsa') throw new Error('wrong public key type') - return dsaVerify(sig, hash, pub) - } else { - if (signType !== 'rsa' && signType !== 'ecdsa/rsa') throw new Error('wrong public key type') - } - hash = Buffer.concat([tag, hash]) - var len = pub.modulus.byteLength() - var pad = [1] - var padNum = 0 - while (hash.length + pad.length + 2 < len) { - pad.push(0xff) - padNum++ - } - pad.push(0x00) - var i = -1 - while (++i < hash.length) { - pad.push(hash[i]) - } - pad = Buffer.from(pad) - var red = BN.mont(pub.modulus) - sig = new BN(sig).toRed(red) - - sig = sig.redPow(new BN(pub.publicExponent)) - sig = Buffer.from(sig.fromRed().toArray()) - var out = padNum < 8 ? 1 : 0 - len = Math.min(sig.length, pad.length) - if (sig.length !== pad.length) out = 1 - - i = -1 - while (++i < len) out |= sig[i] ^ pad[i] - return out === 0 -} - -function ecVerify (sig, hash, pub) { - var curveId = curves[pub.data.algorithm.curve.join('.')] - if (!curveId) throw new Error('unknown curve ' + pub.data.algorithm.curve.join('.')) - - var curve = new EC(curveId) - var pubkey = pub.data.subjectPrivateKey.data - - return curve.verify(hash, sig, pubkey) -} - -function dsaVerify (sig, hash, pub) { - var p = pub.data.p - var q = pub.data.q - var g = pub.data.g - var y = pub.data.pub_key - var unpacked = parseKeys.signature.decode(sig, 'der') - var s = unpacked.s - var r = unpacked.r - checkValue(s, q) - checkValue(r, q) - var montp = BN.mont(p) - var w = s.invm(q) - var v = g.toRed(montp) - .redPow(new BN(hash).mul(w).mod(q)) - .fromRed() - .mul(y.toRed(montp).redPow(r.mul(w).mod(q)).fromRed()) - .mod(p) - .mod(q) - return v.cmp(r) === 0 -} - -function checkValue (b, q) { - if (b.cmpn(0) <= 0) throw new Error('invalid sig') - if (b.cmp(q) >= q) throw new Error('invalid sig') -} - -module.exports = verify - -},{"./curves.json":45,"bn.js":49,"elliptic":89,"parse-asn1":230,"safe-buffer":250}],49:[function(require,module,exports){ -arguments[4][42][0].apply(exports,arguments) -},{"buffer":20,"dup":42}],50:[function(require,module,exports){ -'use strict'; - -function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } - -var codes = {}; - -function createErrorType(code, message, Base) { - if (!Base) { - Base = Error; - } - - function getMessage(arg1, arg2, arg3) { - if (typeof message === 'string') { - return message; - } else { - return message(arg1, arg2, arg3); - } - } - - var NodeError = - /*#__PURE__*/ - function (_Base) { - _inheritsLoose(NodeError, _Base); - - function NodeError(arg1, arg2, arg3) { - return _Base.call(this, getMessage(arg1, arg2, arg3)) || this; - } - - return NodeError; - }(Base); - - NodeError.prototype.name = Base.name; - NodeError.prototype.code = code; - codes[code] = NodeError; -} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js - - -function oneOf(expected, thing) { - if (Array.isArray(expected)) { - var len = expected.length; - expected = expected.map(function (i) { - return String(i); - }); - - if (len > 2) { - return "one of ".concat(thing, " ").concat(expected.slice(0, len - 1).join(', '), ", or ") + expected[len - 1]; - } else if (len === 2) { - return "one of ".concat(thing, " ").concat(expected[0], " or ").concat(expected[1]); - } else { - return "of ".concat(thing, " ").concat(expected[0]); - } - } else { - return "of ".concat(thing, " ").concat(String(expected)); - } -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith - - -function startsWith(str, search, pos) { - return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith - - -function endsWith(str, search, this_len) { - if (this_len === undefined || this_len > str.length) { - this_len = str.length; - } - - return str.substring(this_len - search.length, this_len) === search; -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes - - -function includes(str, search, start) { - if (typeof start !== 'number') { - start = 0; - } - - if (start + search.length > str.length) { - return false; - } else { - return str.indexOf(search, start) !== -1; - } -} - -createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) { - return 'The value "' + value + '" is invalid for option "' + name + '"'; -}, TypeError); -createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) { - // determiner: 'must be' or 'must not be' - var determiner; - - if (typeof expected === 'string' && startsWith(expected, 'not ')) { - determiner = 'must not be'; - expected = expected.replace(/^not /, ''); - } else { - determiner = 'must be'; - } - - var msg; - - if (endsWith(name, ' argument')) { - // For cases like 'first argument' - msg = "The ".concat(name, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); - } else { - var type = includes(name, '.') ? 'property' : 'argument'; - msg = "The \"".concat(name, "\" ").concat(type, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); - } - - msg += ". Received type ".concat(typeof actual); - return msg; -}, TypeError); -createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF'); -createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) { - return 'The ' + name + ' method is not implemented'; -}); -createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close'); -createErrorType('ERR_STREAM_DESTROYED', function (name) { - return 'Cannot call ' + name + ' after a stream was destroyed'; -}); -createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times'); -createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable'); -createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end'); -createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); -createErrorType('ERR_UNKNOWN_ENCODING', function (arg) { - return 'Unknown encoding: ' + arg; -}, TypeError); -createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); -module.exports.codes = codes; - -},{}],51:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. -'use strict'; -/**/ - -var objectKeys = Object.keys || function (obj) { - var keys = []; - - for (var key in obj) { - keys.push(key); - } - - return keys; -}; -/**/ - - -module.exports = Duplex; - -var Readable = require('./_stream_readable'); - -var Writable = require('./_stream_writable'); - -require('inherits')(Duplex, Readable); - -{ - // Allow the keys array to be GC'ed. - var keys = objectKeys(Writable.prototype); - - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } -} - -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - Readable.call(this, options); - Writable.call(this, options); - this.allowHalfOpen = true; - - if (options) { - if (options.readable === false) this.readable = false; - if (options.writable === false) this.writable = false; - - if (options.allowHalfOpen === false) { - this.allowHalfOpen = false; - this.once('end', onend); - } - } -} - -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); -Object.defineProperty(Duplex.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); -Object.defineProperty(Duplex.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); // the no-half-open enforcer - -function onend() { - // If the writable side ended, then we're ok. - if (this._writableState.ended) return; // no more data can be written. - // But allow more writes to happen in this tick. - - process.nextTick(onEndNT, this); -} - -function onEndNT(self) { - self.end(); -} - -Object.defineProperty(Duplex.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined || this._writableState === undefined) { - return false; - } - - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); -}).call(this)}).call(this,require('_process')) - -},{"./_stream_readable":53,"./_stream_writable":55,"_process":237,"inherits":146}],52:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a passthrough stream. -// basically just the most minimal sort of Transform stream. -// Every written chunk gets output as-is. -'use strict'; - -module.exports = PassThrough; - -var Transform = require('./_stream_transform'); - -require('inherits')(PassThrough, Transform); - -function PassThrough(options) { - if (!(this instanceof PassThrough)) return new PassThrough(options); - Transform.call(this, options); -} - -PassThrough.prototype._transform = function (chunk, encoding, cb) { - cb(null, chunk); -}; -},{"./_stream_transform":54,"inherits":146}],53:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -'use strict'; - -module.exports = Readable; -/**/ - -var Duplex; -/**/ - -Readable.ReadableState = ReadableState; -/**/ - -var EE = require('events').EventEmitter; - -var EElistenerCount = function EElistenerCount(emitter, type) { - return emitter.listeners(type).length; -}; -/**/ - -/**/ - - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} -/**/ - - -var debugUtil = require('util'); - -var debug; - -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function debug() {}; -} -/**/ - - -var BufferList = require('./internal/streams/buffer_list'); - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance. - - -var StringDecoder; -var createReadableStreamAsyncIterator; -var from; - -require('inherits')(Readable, Stream); - -var errorOrDestroy = destroyImpl.errorOrDestroy; -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} - -function ReadableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - - this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - - this.sync = true; // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - this.paused = true; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish') - - this.autoDestroy = !!options.autoDestroy; // has it been destroyed - - this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s - - this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled - - this.readingMore = false; - this.decoder = null; - this.encoding = null; - - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} - -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside - // the ReadableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - this._readableState = new ReadableState(options, this, isDuplex); // legacy - - this.readable = true; - - if (options) { - if (typeof options.read === 'function') this._read = options.read; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - - Stream.call(this); -} - -Object.defineProperty(Readable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined) { - return false; - } - - return this._readableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - } -}); -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; - -Readable.prototype._destroy = function (err, cb) { - cb(err); -}; // Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. - - -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } - - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; // Unshift should *always* be something directly out of read() - - -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; - -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - debug('readableAddChunk', chunk); - var state = stream._readableState; - - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - - if (er) { - errorOrDestroy(stream, er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (addToFront) { - if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed) { - return false; - } else { - state.reading = false; - - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); - } - } // We can push more data if we are below the highWaterMark. - // Also, if we have no data yet, we can stand some more bytes. - // This is to work around cases where hwm=0, such as the repl. - - - return !state.ended && (state.length < state.highWaterMark || state.length === 0); -} - -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - state.awaitDrain = 0; - stream.emit('data', chunk); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - if (state.needReadable) emitReadable(stream); - } - - maybeReadMore(stream, state); -} - -function chunkInvalid(state, chunk) { - var er; - - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); - } - - return er; -} - -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; // backwards compatibility. - - -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - var decoder = new StringDecoder(enc); - this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8 - - this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers: - - var p = this._readableState.buffer.head; - var content = ''; - - while (p !== null) { - content += decoder.write(p.data); - p = p.next; - } - - this._readableState.buffer.clear(); - - if (content !== '') this._readableState.buffer.push(content); - this._readableState.length = content.length; - return this; -}; // Don't raise the hwm > 1GB - - -var MAX_HWM = 0x40000000; - -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - - return n; -} // This function is designed to be inlinable, so please take care when making -// changes to the function body. - - -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } // If we're asking for more than the current hwm, then raise the hwm. - - - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; // Don't have enough - - if (!state.ended) { - state.needReadable = true; - return 0; - } - - return state.length; -} // you can override either this method, or the async _read(n) below. - - -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - - if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - - n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. - - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - // if we need a readable event, then we need to do some reading. - - - var doRead = state.needReadable; - debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some - - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - - - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; // if the length is currently zero, then we *need* a readable event. - - if (state.length === 0) state.needReadable = true; // call internal read method - - this._read(state.highWaterMark); - - state.sync = false; // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - - if (!state.reading) n = howMuchToRead(nOrig, state); - } - - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; - - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n; - state.awaitDrain = 0; - } - - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. - - if (nOrig !== n && state.ended) endReadable(this); - } - - if (ret !== null) this.emit('data', ret); - return ret; -}; - -function onEofChunk(stream, state) { - debug('onEofChunk'); - if (state.ended) return; - - if (state.decoder) { - var chunk = state.decoder.end(); - - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - - state.ended = true; - - if (state.sync) { - // if we are sync, wait until next tick to emit the data. - // Otherwise we risk emitting data in the flow() - // the readable code triggers during a read() call - emitReadable(stream); - } else { - // emit 'readable' now to make sure it gets picked up. - state.needReadable = false; - - if (!state.emittedReadable) { - state.emittedReadable = true; - emitReadable_(stream); - } - } -} // Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. - - -function emitReadable(stream) { - var state = stream._readableState; - debug('emitReadable', state.needReadable, state.emittedReadable); - state.needReadable = false; - - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - process.nextTick(emitReadable_, stream); - } -} - -function emitReadable_(stream) { - var state = stream._readableState; - debug('emitReadable_', state.destroyed, state.length, state.ended); - - if (!state.destroyed && (state.length || state.ended)) { - stream.emit('readable'); - state.emittedReadable = false; - } // The stream needs another readable event if - // 1. It is not flowing, as the flow mechanism will take - // care of it. - // 2. It is not ended. - // 3. It is below the highWaterMark, so we can schedule - // another readable later. - - - state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; - flow(stream); -} // at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. - - -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - process.nextTick(maybeReadMore_, stream, state); - } -} - -function maybeReadMore_(stream, state) { - // Attempt to read more data if we should. - // - // The conditions for reading more data are (one of): - // - Not enough data buffered (state.length < state.highWaterMark). The loop - // is responsible for filling the buffer with enough data if such data - // is available. If highWaterMark is 0 and we are not in the flowing mode - // we should _not_ attempt to buffer any extra data. We'll get more data - // when the stream consumer calls read() instead. - // - No data in the buffer, and the stream is in flowing mode. In this mode - // the loop below is responsible for ensuring read() is called. Failing to - // call read here would abort the flow and there's no other mechanism for - // continuing the flow if the stream consumer has just subscribed to the - // 'data' event. - // - // In addition to the above conditions to keep reading data, the following - // conditions prevent the data from being read: - // - The stream has ended (state.ended). - // - There is already a pending 'read' operation (state.reading). This is a - // case where the the stream has called the implementation defined _read() - // method, but they are processing the call asynchronously and have _not_ - // called push() with new data. In this case we skip performing more - // read()s. The execution ends in this method again after the _read() ends - // up calling push() with more data. - while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { - var len = state.length; - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) // didn't get any data, stop spinning. - break; - } - - state.readingMore = false; -} // abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. - - -Readable.prototype._read = function (n) { - errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); -}; - -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - - case 1: - state.pipes = [state.pipes, dest]; - break; - - default: - state.pipes.push(dest); - break; - } - - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); - dest.on('unpipe', onunpipe); - - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - debug('onend'); - dest.end(); - } // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - - - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - var cleanedUp = false; - - function cleanup() { - debug('cleanup'); // cleanup event handlers once the pipe is broken - - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - cleanedUp = true; // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - - src.on('data', ondata); - - function ondata(chunk) { - debug('ondata'); - var ret = dest.write(chunk); - debug('dest.write', ret); - - if (ret === false) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', state.awaitDrain); - state.awaitDrain++; - } - - src.pause(); - } - } // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - - - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); - } // Make sure our error handler is attached before userland ones. - - - prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. - - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - - dest.once('close', onclose); - - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - - dest.once('finish', onfinish); - - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } // tell the dest that it's being piped to - - - dest.emit('pipe', src); // start the flow if it hasn't been started already. - - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } - - return dest; -}; - -function pipeOnDrain(src) { - return function pipeOnDrainFunctionResult() { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); - } - }; -} - -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { - hasUnpiped: false - }; // if we're not piping anywhere, then do nothing. - - if (state.pipesCount === 0) return this; // just one destination. most common case. - - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - if (!dest) dest = state.pipes; // got a match. - - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } // slow case. multiple pipe destinations. - - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, { - hasUnpiped: false - }); - } - - return this; - } // try to find the right one. - - - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - dest.emit('unpipe', this, unpipeInfo); - return this; -}; // set up data events if they are asked for -// Ensure readable listeners eventually get something - - -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - var state = this._readableState; - - if (ev === 'data') { - // update readableListening so that resume() may be a no-op - // a few lines down. This is needed to support once('readable'). - state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused - - if (state.flowing !== false) this.resume(); - } else if (ev === 'readable') { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - debug('on readable', state.length, state.reading); - - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - process.nextTick(nReadingNextTick, this); - } - } - } - - return res; -}; - -Readable.prototype.addListener = Readable.prototype.on; - -Readable.prototype.removeListener = function (ev, fn) { - var res = Stream.prototype.removeListener.call(this, ev, fn); - - if (ev === 'readable') { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; -}; - -Readable.prototype.removeAllListeners = function (ev) { - var res = Stream.prototype.removeAllListeners.apply(this, arguments); - - if (ev === 'readable' || ev === undefined) { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; -}; - -function updateReadableListening(self) { - var state = self._readableState; - state.readableListening = self.listenerCount('readable') > 0; - - if (state.resumeScheduled && !state.paused) { - // flowing needs to be set to true now, otherwise - // the upcoming resume will not flow. - state.flowing = true; // crude way to check if we should resume - } else if (self.listenerCount('data') > 0) { - self.resume(); - } -} - -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} // pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. - - -Readable.prototype.resume = function () { - var state = this._readableState; - - if (!state.flowing) { - debug('resume'); // we flow only if there is no one listening - // for readable, but we still have to call - // resume() - - state.flowing = !state.readableListening; - resume(this, state); - } - - state.paused = false; - return this; -}; - -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - process.nextTick(resume_, stream, state); - } -} - -function resume_(stream, state) { - debug('resume', state.reading); - - if (!state.reading) { - stream.read(0); - } - - state.resumeScheduled = false; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} - -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - - if (this._readableState.flowing !== false) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - - this._readableState.paused = true; - return this; -}; - -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - - while (state.flowing && stream.read() !== null) { - ; - } -} // wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. - - -Readable.prototype.wrap = function (stream) { - var _this = this; - - var state = this._readableState; - var paused = false; - stream.on('end', function () { - debug('wrapped end'); - - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); - } - - _this.push(null); - }); - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode - - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - - var ret = _this.push(chunk); - - if (!ret) { - paused = true; - stream.pause(); - } - }); // proxy all the other methods. - // important when wrapping filters and duplexes. - - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function methodWrap(method) { - return function methodWrapReturnFunction() { - return stream[method].apply(stream, arguments); - }; - }(i); - } - } // proxy certain important events. - - - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } // when we try to consume some more bytes, simply unpause the - // underlying stream. - - - this._read = function (n) { - debug('wrapped _read', n); - - if (paused) { - paused = false; - stream.resume(); - } - }; - - return this; -}; - -if (typeof Symbol === 'function') { - Readable.prototype[Symbol.asyncIterator] = function () { - if (createReadableStreamAsyncIterator === undefined) { - createReadableStreamAsyncIterator = require('./internal/streams/async_iterator'); - } - - return createReadableStreamAsyncIterator(this); - }; -} - -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.highWaterMark; - } -}); -Object.defineProperty(Readable.prototype, 'readableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState && this._readableState.buffer; - } -}); -Object.defineProperty(Readable.prototype, 'readableFlowing', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.flowing; - }, - set: function set(state) { - if (this._readableState) { - this._readableState.flowing = state; - } - } -}); // exposed for testing purposes only. - -Readable._fromList = fromList; -Object.defineProperty(Readable.prototype, 'readableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.length; - } -}); // Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. - -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = state.buffer.consume(n, state.decoder); - } - return ret; -} - -function endReadable(stream) { - var state = stream._readableState; - debug('endReadable', state.endEmitted); - - if (!state.endEmitted) { - state.ended = true; - process.nextTick(endReadableNT, state, stream); - } -} - -function endReadableNT(state, stream) { - debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift. - - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the writable side is ready for autoDestroy as well - var wState = stream._writableState; - - if (!wState || wState.autoDestroy && wState.finished) { - stream.destroy(); - } - } - } -} - -if (typeof Symbol === 'function') { - Readable.from = function (iterable, opts) { - if (from === undefined) { - from = require('./internal/streams/from'); - } - - return from(Readable, iterable, opts); - }; -} - -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - - return -1; -} -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../errors":50,"./_stream_duplex":51,"./internal/streams/async_iterator":56,"./internal/streams/buffer_list":57,"./internal/streams/destroy":58,"./internal/streams/from":60,"./internal/streams/state":62,"./internal/streams/stream":63,"_process":237,"buffer":68,"events":105,"inherits":146,"string_decoder/":279,"util":20}],54:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a transform stream is a readable/writable stream where you do -// something with the data. Sometimes it's called a "filter", -// but that's not a great name for it, since that implies a thing where -// some bits pass through, and others are simply ignored. (That would -// be a valid example of a transform, of course.) -// -// While the output is causally related to the input, it's not a -// necessarily symmetric or synchronous transformation. For example, -// a zlib stream might take multiple plain-text writes(), and then -// emit a single compressed chunk some time in the future. -// -// Here's how this works: -// -// The Transform stream has all the aspects of the readable and writable -// stream classes. When you write(chunk), that calls _write(chunk,cb) -// internally, and returns false if there's a lot of pending writes -// buffered up. When you call read(), that calls _read(n) until -// there's enough pending readable data buffered up. -// -// In a transform stream, the written data is placed in a buffer. When -// _read(n) is called, it transforms the queued up data, calling the -// buffered _write cb's as it consumes chunks. If consuming a single -// written chunk would result in multiple output chunks, then the first -// outputted bit calls the readcb, and subsequent chunks just go into -// the read buffer, and will cause it to emit 'readable' if necessary. -// -// This way, back-pressure is actually determined by the reading side, -// since _read has to be called to start processing a new chunk. However, -// a pathological inflate type of transform can cause excessive buffering -// here. For example, imagine a stream where every byte of input is -// interpreted as an integer from 0-255, and then results in that many -// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in -// 1kb of data being output. In this case, you could write a very small -// amount of input, and end up with a very large amount of output. In -// such a pathological inflating mechanism, there'd be no way to tell -// the system to stop doing the transform. A single 4MB write could -// cause the system to run out of memory. -// -// However, even in such a pathological case, only a single written chunk -// would be consumed, and then the rest would wait (un-transformed) until -// the results of the previous transformed chunk were consumed. -'use strict'; - -module.exports = Transform; - -var _require$codes = require('../errors').codes, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING, - ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0; - -var Duplex = require('./_stream_duplex'); - -require('inherits')(Transform, Duplex); - -function afterTransform(er, data) { - var ts = this._transformState; - ts.transforming = false; - var cb = ts.writecb; - - if (cb === null) { - return this.emit('error', new ERR_MULTIPLE_CALLBACK()); - } - - ts.writechunk = null; - ts.writecb = null; - if (data != null) // single equals check for both `null` and `undefined` - this.push(data); - cb(er); - var rs = this._readableState; - rs.reading = false; - - if (rs.needReadable || rs.length < rs.highWaterMark) { - this._read(rs.highWaterMark); - } -} - -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); - Duplex.call(this, options); - this._transformState = { - afterTransform: afterTransform.bind(this), - needTransform: false, - transforming: false, - writecb: null, - writechunk: null, - writeencoding: null - }; // start out asking for a readable event once data is transformed. - - this._readableState.needReadable = true; // we have implemented the _read method, and done the other things - // that Readable wants before the first _read call, so unset the - // sync guard flag. - - this._readableState.sync = false; - - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; - if (typeof options.flush === 'function') this._flush = options.flush; - } // When the writable side finishes, then flush out anything remaining. - - - this.on('prefinish', prefinish); -} - -function prefinish() { - var _this = this; - - if (typeof this._flush === 'function' && !this._readableState.destroyed) { - this._flush(function (er, data) { - done(_this, er, data); - }); - } else { - done(this, null, null); - } -} - -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; // This is the part where you do stuff! -// override this function in implementation classes. -// 'chunk' is an input chunk. -// -// Call `push(newChunk)` to pass along transformed output -// to the readable side. You may call 'push' zero or more times. -// -// Call `cb(err)` when you are done with this chunk. If you pass -// an error, then that'll put the hurt on the whole operation. If you -// never call cb(), then you'll never get another chunk. - - -Transform.prototype._transform = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()')); -}; - -Transform.prototype._write = function (chunk, encoding, cb) { - var ts = this._transformState; - ts.writecb = cb; - ts.writechunk = chunk; - ts.writeencoding = encoding; - - if (!ts.transforming) { - var rs = this._readableState; - if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); - } -}; // Doesn't matter what the args are here. -// _transform does all the work. -// That we got here means that the readable side wants more data. - - -Transform.prototype._read = function (n) { - var ts = this._transformState; - - if (ts.writechunk !== null && !ts.transforming) { - ts.transforming = true; - - this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); - } else { - // mark that we need a transform, so that any data that comes in - // will get processed, now that we've asked for it. - ts.needTransform = true; - } -}; - -Transform.prototype._destroy = function (err, cb) { - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - }); -}; - -function done(stream, er, data) { - if (er) return stream.emit('error', er); - if (data != null) // single equals check for both `null` and `undefined` - stream.push(data); // TODO(BridgeAR): Write a test for these two error cases - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - - if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0(); - if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING(); - return stream.push(null); -} -},{"../errors":50,"./_stream_duplex":51,"inherits":146}],55:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. -'use strict'; - -module.exports = Writable; -/* */ - -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} // It seems a linked list but it is not -// there will be only 2 of these for each stream - - -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* */ - -/**/ - - -var Duplex; -/**/ - -Writable.WritableState = WritableState; -/**/ - -var internalUtil = { - deprecate: require('util-deprecate') -}; -/**/ - -/**/ - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, - ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, - ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, - ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; - -var errorOrDestroy = destroyImpl.errorOrDestroy; - -require('inherits')(Writable, Stream); - -function nop() {} - -function WritableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream, - // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream - // contains buffers or objects. - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - - this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called - - this.finalCalled = false; // drain event flag. - - this.needDrain = false; // at the start of calling end() - - this.ending = false; // when end() has been called, and returned - - this.ended = false; // when 'finish' is emitted - - this.finished = false; // has it been destroyed - - this.destroyed = false; // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - - this.length = 0; // a flag to see when we're in the middle of a write. - - this.writing = false; // when true all writes will be buffered until .uncork() call - - this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - - this.sync = true; // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - - this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) - - this.onwrite = function (er) { - onwrite(stream, er); - }; // the callback that the user supplies to write(chunk,encoding,cb) - - - this.writecb = null; // the amount that is being written when _write is called. - - this.writelen = 0; - this.bufferedRequest = null; - this.lastBufferedRequest = null; // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - - this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - - this.prefinished = false; // True if the error was already emitted and should not be thrown again - - this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end') - - this.autoDestroy = !!options.autoDestroy; // count buffered requests - - this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - - this.corkedRequestsFree = new CorkedRequest(this); -} - -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - - while (current) { - out.push(current); - current = current.next; - } - - return out; -}; - -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function writableStateBufferGetter() { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); // Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. - - -var realHasInstance; - -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function value(object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function realHasInstance(object) { - return object instanceof this; - }; -} - -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - // Checking for a Stream.Duplex instance is faster here instead of inside - // the WritableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); - this._writableState = new WritableState(options, this, isDuplex); // legacy. - - this.writable = true; - - if (options) { - if (typeof options.write === 'function') this._write = options.write; - if (typeof options.writev === 'function') this._writev = options.writev; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - if (typeof options.final === 'function') this._final = options.final; - } - - Stream.call(this); -} // Otherwise people can pipe Writable streams, which is just wrong. - - -Writable.prototype.pipe = function () { - errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); -}; - -function writeAfterEnd(stream, cb) { - var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb - - errorOrDestroy(stream, er); - process.nextTick(cb, er); -} // Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. - - -function validChunk(stream, state, chunk, cb) { - var er; - - if (chunk === null) { - er = new ERR_STREAM_NULL_VALUES(); - } else if (typeof chunk !== 'string' && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); - } - - if (er) { - errorOrDestroy(stream, er); - process.nextTick(cb, er); - return false; - } - - return true; -} - -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - - var isBuf = !state.objectMode && _isUint8Array(chunk); - - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - if (typeof cb !== 'function') cb = nop; - if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - return ret; -}; - -Writable.prototype.cork = function () { - this._writableState.corked++; -}; - -Writable.prototype.uncork = function () { - var state = this._writableState; - - if (state.corked) { - state.corked--; - if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; - -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; - -Object.defineProperty(Writable.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); - -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - - return chunk; -} - -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); // if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. - -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - - var len = state.objectMode ? 1 : chunk.length; - state.length += len; - var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. - - if (!ret) state.needDrain = true; - - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - - return ret; -} - -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} - -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - process.nextTick(cb, er); // this can emit finish, and it will always happen - // after error - - process.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); // this can emit finish, but finish must - // always follow error - - finishMaybe(stream, state); - } -} - -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} - -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); - onwriteStateUpdate(state); - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state) || stream.destroyed; - - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - - if (sync) { - process.nextTick(afterWrite, stream, state, finished, cb); - } else { - afterWrite(stream, state, finished, cb); - } - } -} - -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} // Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. - - -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} // if there's something in the buffer waiting, then process it - - -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - var count = 0; - var allBuffers = true; - - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - - buffer.allBuffers = allBuffers; - doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - - state.pendingcb++; - state.lastBufferedRequest = null; - - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - - if (state.writing) { - break; - } - } - - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks - - if (state.corked) { - state.corked = 1; - this.uncork(); - } // ignore unnecessary end() calls. - - - if (!state.ending) endWritable(this, state, cb); - return this; -}; - -Object.defineProperty(Writable.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); - -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} - -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - - if (err) { - errorOrDestroy(stream, err); - } - - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} - -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function' && !state.destroyed) { - state.pendingcb++; - state.finalCalled = true; - process.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - - if (need) { - prefinish(stream, state); - - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the readable side is ready for autoDestroy as well - var rState = stream._readableState; - - if (!rState || rState.autoDestroy && rState.endEmitted) { - stream.destroy(); - } - } - } - } - - return need; -} - -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - - if (cb) { - if (state.finished) process.nextTick(cb);else stream.once('finish', cb); - } - - state.ended = true; - stream.writable = false; -} - -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } // reuse the free corkReq. - - - state.corkedRequestsFree.next = corkReq; -} - -Object.defineProperty(Writable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._writableState === undefined) { - return false; - } - - return this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._writableState.destroyed = value; - } -}); -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; - -Writable.prototype._destroy = function (err, cb) { - cb(err); -}; -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../errors":50,"./_stream_duplex":51,"./internal/streams/destroy":58,"./internal/streams/state":62,"./internal/streams/stream":63,"_process":237,"buffer":68,"inherits":146,"util-deprecate":283}],56:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; - -var _Object$setPrototypeO; - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -var finished = require('./end-of-stream'); - -var kLastResolve = Symbol('lastResolve'); -var kLastReject = Symbol('lastReject'); -var kError = Symbol('error'); -var kEnded = Symbol('ended'); -var kLastPromise = Symbol('lastPromise'); -var kHandlePromise = Symbol('handlePromise'); -var kStream = Symbol('stream'); - -function createIterResult(value, done) { - return { - value: value, - done: done - }; -} - -function readAndResolve(iter) { - var resolve = iter[kLastResolve]; - - if (resolve !== null) { - var data = iter[kStream].read(); // we defer if data is null - // we can be expecting either 'end' or - // 'error' - - if (data !== null) { - iter[kLastPromise] = null; - iter[kLastResolve] = null; - iter[kLastReject] = null; - resolve(createIterResult(data, false)); - } - } -} - -function onReadable(iter) { - // we wait for the next tick, because it might - // emit an error with process.nextTick - process.nextTick(readAndResolve, iter); -} - -function wrapForNext(lastPromise, iter) { - return function (resolve, reject) { - lastPromise.then(function () { - if (iter[kEnded]) { - resolve(createIterResult(undefined, true)); - return; - } - - iter[kHandlePromise](resolve, reject); - }, reject); - }; -} - -var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); -var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { - get stream() { - return this[kStream]; - }, - - next: function next() { - var _this = this; - - // if we have detected an error in the meanwhile - // reject straight away - var error = this[kError]; - - if (error !== null) { - return Promise.reject(error); - } - - if (this[kEnded]) { - return Promise.resolve(createIterResult(undefined, true)); - } - - if (this[kStream].destroyed) { - // We need to defer via nextTick because if .destroy(err) is - // called, the error will be emitted via nextTick, and - // we cannot guarantee that there is no error lingering around - // waiting to be emitted. - return new Promise(function (resolve, reject) { - process.nextTick(function () { - if (_this[kError]) { - reject(_this[kError]); - } else { - resolve(createIterResult(undefined, true)); - } - }); - }); - } // if we have multiple next() calls - // we will wait for the previous Promise to finish - // this logic is optimized to support for await loops, - // where next() is only called once at a time - - - var lastPromise = this[kLastPromise]; - var promise; - - if (lastPromise) { - promise = new Promise(wrapForNext(lastPromise, this)); - } else { - // fast path needed to support multiple this.push() - // without triggering the next() queue - var data = this[kStream].read(); - - if (data !== null) { - return Promise.resolve(createIterResult(data, false)); - } - - promise = new Promise(this[kHandlePromise]); - } - - this[kLastPromise] = promise; - return promise; - } -}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { - return this; -}), _defineProperty(_Object$setPrototypeO, "return", function _return() { - var _this2 = this; - - // destroy(err, cb) is a private API - // we can guarantee we have that here, because we control the - // Readable class this is attached to - return new Promise(function (resolve, reject) { - _this2[kStream].destroy(null, function (err) { - if (err) { - reject(err); - return; - } - - resolve(createIterResult(undefined, true)); - }); - }); -}), _Object$setPrototypeO), AsyncIteratorPrototype); - -var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { - var _Object$create; - - var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { - value: stream, - writable: true - }), _defineProperty(_Object$create, kLastResolve, { - value: null, - writable: true - }), _defineProperty(_Object$create, kLastReject, { - value: null, - writable: true - }), _defineProperty(_Object$create, kError, { - value: null, - writable: true - }), _defineProperty(_Object$create, kEnded, { - value: stream._readableState.endEmitted, - writable: true - }), _defineProperty(_Object$create, kHandlePromise, { - value: function value(resolve, reject) { - var data = iterator[kStream].read(); - - if (data) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(data, false)); - } else { - iterator[kLastResolve] = resolve; - iterator[kLastReject] = reject; - } - }, - writable: true - }), _Object$create)); - iterator[kLastPromise] = null; - finished(stream, function (err) { - if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { - var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise - // returned by next() and store the error - - if (reject !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - reject(err); - } - - iterator[kError] = err; - return; - } - - var resolve = iterator[kLastResolve]; - - if (resolve !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(undefined, true)); - } - - iterator[kEnded] = true; - }); - stream.on('readable', onReadable.bind(null, iterator)); - return iterator; -}; - -module.exports = createReadableStreamAsyncIterator; -}).call(this)}).call(this,require('_process')) - -},{"./end-of-stream":59,"_process":237}],57:[function(require,module,exports){ -'use strict'; - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } - -var _require = require('buffer'), - Buffer = _require.Buffer; - -var _require2 = require('util'), - inspect = _require2.inspect; - -var custom = inspect && inspect.custom || 'inspect'; - -function copyBuffer(src, target, offset) { - Buffer.prototype.copy.call(src, target, offset); -} - -module.exports = -/*#__PURE__*/ -function () { - function BufferList() { - _classCallCheck(this, BufferList); - - this.head = null; - this.tail = null; - this.length = 0; - } - - _createClass(BufferList, [{ - key: "push", - value: function push(v) { - var entry = { - data: v, - next: null - }; - if (this.length > 0) this.tail.next = entry;else this.head = entry; - this.tail = entry; - ++this.length; - } - }, { - key: "unshift", - value: function unshift(v) { - var entry = { - data: v, - next: this.head - }; - if (this.length === 0) this.tail = entry; - this.head = entry; - ++this.length; - } - }, { - key: "shift", - value: function shift() { - if (this.length === 0) return; - var ret = this.head.data; - if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; - --this.length; - return ret; - } - }, { - key: "clear", - value: function clear() { - this.head = this.tail = null; - this.length = 0; - } - }, { - key: "join", - value: function join(s) { - if (this.length === 0) return ''; - var p = this.head; - var ret = '' + p.data; - - while (p = p.next) { - ret += s + p.data; - } - - return ret; - } - }, { - key: "concat", - value: function concat(n) { - if (this.length === 0) return Buffer.alloc(0); - var ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; - - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } - - return ret; - } // Consumes a specified amount of bytes or characters from the buffered data. - - }, { - key: "consume", - value: function consume(n, hasStrings) { - var ret; - - if (n < this.head.data.length) { - // `slice` is the same for buffers and strings. - ret = this.head.data.slice(0, n); - this.head.data = this.head.data.slice(n); - } else if (n === this.head.data.length) { - // First chunk is a perfect match. - ret = this.shift(); - } else { - // Result spans more than one buffer. - ret = hasStrings ? this._getString(n) : this._getBuffer(n); - } - - return ret; - } - }, { - key: "first", - value: function first() { - return this.head.data; - } // Consumes a specified amount of characters from the buffered data. - - }, { - key: "_getString", - value: function _getString(n) { - var p = this.head; - var c = 1; - var ret = p.data; - n -= ret.length; - - while (p = p.next) { - var str = p.data; - var nb = n > str.length ? str.length : n; - if (nb === str.length) ret += str;else ret += str.slice(0, n); - n -= nb; - - if (n === 0) { - if (nb === str.length) { - ++c; - if (p.next) this.head = p.next;else this.head = this.tail = null; - } else { - this.head = p; - p.data = str.slice(nb); - } - - break; - } - - ++c; - } - - this.length -= c; - return ret; - } // Consumes a specified amount of bytes from the buffered data. - - }, { - key: "_getBuffer", - value: function _getBuffer(n) { - var ret = Buffer.allocUnsafe(n); - var p = this.head; - var c = 1; - p.data.copy(ret); - n -= p.data.length; - - while (p = p.next) { - var buf = p.data; - var nb = n > buf.length ? buf.length : n; - buf.copy(ret, ret.length - n, 0, nb); - n -= nb; - - if (n === 0) { - if (nb === buf.length) { - ++c; - if (p.next) this.head = p.next;else this.head = this.tail = null; - } else { - this.head = p; - p.data = buf.slice(nb); - } - - break; - } - - ++c; - } - - this.length -= c; - return ret; - } // Make sure the linked list only shows the minimal necessary information. - - }, { - key: custom, - value: function value(_, options) { - return inspect(this, _objectSpread({}, options, { - // Only inspect one level. - depth: 0, - // It should not recurse. - customInspect: false - })); - } - }]); - - return BufferList; -}(); -},{"buffer":68,"util":20}],58:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; // undocumented cb() API, needed for core, not for public API - -function destroy(err, cb) { - var _this = this; - - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; - - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err) { - if (!this._writableState) { - process.nextTick(emitErrorNT, this, err); - } else if (!this._writableState.errorEmitted) { - this._writableState.errorEmitted = true; - process.nextTick(emitErrorNT, this, err); - } - } - - return this; - } // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - - if (this._readableState) { - this._readableState.destroyed = true; - } // if this is a duplex stream mark the writable part as destroyed as well - - - if (this._writableState) { - this._writableState.destroyed = true; - } - - this._destroy(err || null, function (err) { - if (!cb && err) { - if (!_this._writableState) { - process.nextTick(emitErrorAndCloseNT, _this, err); - } else if (!_this._writableState.errorEmitted) { - _this._writableState.errorEmitted = true; - process.nextTick(emitErrorAndCloseNT, _this, err); - } else { - process.nextTick(emitCloseNT, _this); - } - } else if (cb) { - process.nextTick(emitCloseNT, _this); - cb(err); - } else { - process.nextTick(emitCloseNT, _this); - } - }); - - return this; -} - -function emitErrorAndCloseNT(self, err) { - emitErrorNT(self, err); - emitCloseNT(self); -} - -function emitCloseNT(self) { - if (self._writableState && !self._writableState.emitClose) return; - if (self._readableState && !self._readableState.emitClose) return; - self.emit('close'); -} - -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finalCalled = false; - this._writableState.prefinished = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} - -function emitErrorNT(self, err) { - self.emit('error', err); -} - -function errorOrDestroy(stream, err) { - // We have tests that rely on errors being emitted - // in the same tick, so changing this is semver major. - // For now when you opt-in to autoDestroy we allow - // the error to be emitted nextTick. In a future - // semver major update we should change the default to this. - var rState = stream._readableState; - var wState = stream._writableState; - if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); -} - -module.exports = { - destroy: destroy, - undestroy: undestroy, - errorOrDestroy: errorOrDestroy -}; -}).call(this)}).call(this,require('_process')) - -},{"_process":237}],59:[function(require,module,exports){ -// Ported from https://github.com/mafintosh/end-of-stream with -// permission from the author, Mathias Buus (@mafintosh). -'use strict'; - -var ERR_STREAM_PREMATURE_CLOSE = require('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE; - -function once(callback) { - var called = false; - return function () { - if (called) return; - called = true; - - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - callback.apply(this, args); - }; -} - -function noop() {} - -function isRequest(stream) { - return stream.setHeader && typeof stream.abort === 'function'; -} - -function eos(stream, opts, callback) { - if (typeof opts === 'function') return eos(stream, null, opts); - if (!opts) opts = {}; - callback = once(callback || noop); - var readable = opts.readable || opts.readable !== false && stream.readable; - var writable = opts.writable || opts.writable !== false && stream.writable; - - var onlegacyfinish = function onlegacyfinish() { - if (!stream.writable) onfinish(); - }; - - var writableEnded = stream._writableState && stream._writableState.finished; - - var onfinish = function onfinish() { - writable = false; - writableEnded = true; - if (!readable) callback.call(stream); - }; - - var readableEnded = stream._readableState && stream._readableState.endEmitted; - - var onend = function onend() { - readable = false; - readableEnded = true; - if (!writable) callback.call(stream); - }; - - var onerror = function onerror(err) { - callback.call(stream, err); - }; - - var onclose = function onclose() { - var err; - - if (readable && !readableEnded) { - if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); - return callback.call(stream, err); - } - - if (writable && !writableEnded) { - if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); - return callback.call(stream, err); - } - }; - - var onrequest = function onrequest() { - stream.req.on('finish', onfinish); - }; - - if (isRequest(stream)) { - stream.on('complete', onfinish); - stream.on('abort', onclose); - if (stream.req) onrequest();else stream.on('request', onrequest); - } else if (writable && !stream._writableState) { - // legacy streams - stream.on('end', onlegacyfinish); - stream.on('close', onlegacyfinish); - } - - stream.on('end', onend); - stream.on('finish', onfinish); - if (opts.error !== false) stream.on('error', onerror); - stream.on('close', onclose); - return function () { - stream.removeListener('complete', onfinish); - stream.removeListener('abort', onclose); - stream.removeListener('request', onrequest); - if (stream.req) stream.req.removeListener('finish', onfinish); - stream.removeListener('end', onlegacyfinish); - stream.removeListener('close', onlegacyfinish); - stream.removeListener('finish', onfinish); - stream.removeListener('end', onend); - stream.removeListener('error', onerror); - stream.removeListener('close', onclose); - }; -} - -module.exports = eos; -},{"../../../errors":50}],60:[function(require,module,exports){ -module.exports = function () { - throw new Error('Readable.from is not available in the browser') -}; - -},{}],61:[function(require,module,exports){ -// Ported from https://github.com/mafintosh/pump with -// permission from the author, Mathias Buus (@mafintosh). -'use strict'; - -var eos; - -function once(callback) { - var called = false; - return function () { - if (called) return; - called = true; - callback.apply(void 0, arguments); - }; -} - -var _require$codes = require('../../../errors').codes, - ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED; - -function noop(err) { - // Rethrow the error if it exists to avoid swallowing it - if (err) throw err; -} - -function isRequest(stream) { - return stream.setHeader && typeof stream.abort === 'function'; -} - -function destroyer(stream, reading, writing, callback) { - callback = once(callback); - var closed = false; - stream.on('close', function () { - closed = true; - }); - if (eos === undefined) eos = require('./end-of-stream'); - eos(stream, { - readable: reading, - writable: writing - }, function (err) { - if (err) return callback(err); - closed = true; - callback(); - }); - var destroyed = false; - return function (err) { - if (closed) return; - if (destroyed) return; - destroyed = true; // request.destroy just do .end - .abort is what we want - - if (isRequest(stream)) return stream.abort(); - if (typeof stream.destroy === 'function') return stream.destroy(); - callback(err || new ERR_STREAM_DESTROYED('pipe')); - }; -} - -function call(fn) { - fn(); -} - -function pipe(from, to) { - return from.pipe(to); -} - -function popCallback(streams) { - if (!streams.length) return noop; - if (typeof streams[streams.length - 1] !== 'function') return noop; - return streams.pop(); -} - -function pipeline() { - for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) { - streams[_key] = arguments[_key]; - } - - var callback = popCallback(streams); - if (Array.isArray(streams[0])) streams = streams[0]; - - if (streams.length < 2) { - throw new ERR_MISSING_ARGS('streams'); - } - - var error; - var destroys = streams.map(function (stream, i) { - var reading = i < streams.length - 1; - var writing = i > 0; - return destroyer(stream, reading, writing, function (err) { - if (!error) error = err; - if (err) destroys.forEach(call); - if (reading) return; - destroys.forEach(call); - callback(error); - }); - }); - return streams.reduce(pipe); -} - -module.exports = pipeline; -},{"../../../errors":50,"./end-of-stream":59}],62:[function(require,module,exports){ -'use strict'; - -var ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE; - -function highWaterMarkFrom(options, isDuplex, duplexKey) { - return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; -} - -function getHighWaterMark(state, options, duplexKey, isDuplex) { - var hwm = highWaterMarkFrom(options, isDuplex, duplexKey); - - if (hwm != null) { - if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) { - var name = isDuplex ? duplexKey : 'highWaterMark'; - throw new ERR_INVALID_OPT_VALUE(name, hwm); - } - - return Math.floor(hwm); - } // Default value - - - return state.objectMode ? 16 : 16 * 1024; -} - -module.exports = { - getHighWaterMark: getHighWaterMark -}; -},{"../../../errors":50}],63:[function(require,module,exports){ -module.exports = require('events').EventEmitter; - -},{"events":105}],64:[function(require,module,exports){ -exports = module.exports = require('./lib/_stream_readable.js'); -exports.Stream = exports; -exports.Readable = exports; -exports.Writable = require('./lib/_stream_writable.js'); -exports.Duplex = require('./lib/_stream_duplex.js'); -exports.Transform = require('./lib/_stream_transform.js'); -exports.PassThrough = require('./lib/_stream_passthrough.js'); -exports.finished = require('./lib/internal/streams/end-of-stream.js'); -exports.pipeline = require('./lib/internal/streams/pipeline.js'); - -},{"./lib/_stream_duplex.js":51,"./lib/_stream_passthrough.js":52,"./lib/_stream_readable.js":53,"./lib/_stream_transform.js":54,"./lib/_stream_writable.js":55,"./lib/internal/streams/end-of-stream.js":59,"./lib/internal/streams/pipeline.js":61}],65:[function(require,module,exports){ -const basex = require('base-x') -const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' - -module.exports = basex(ALPHABET) - -},{"base-x":66}],66:[function(require,module,exports){ -'use strict' -// base-x encoding / decoding -// Copyright (c) 2018 base-x contributors -// Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp) -// Distributed under the MIT software license, see the accompanying -// file LICENSE or http://www.opensource.org/licenses/mit-license.php. -function base (ALPHABET) { - if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') } - var BASE_MAP = new Uint8Array(256) - for (var j = 0; j < BASE_MAP.length; j++) { - BASE_MAP[j] = 255 - } - for (var i = 0; i < ALPHABET.length; i++) { - var x = ALPHABET.charAt(i) - var xc = x.charCodeAt(0) - if (BASE_MAP[xc] !== 255) { throw new TypeError(x + ' is ambiguous') } - BASE_MAP[xc] = i - } - var BASE = ALPHABET.length - var LEADER = ALPHABET.charAt(0) - var FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up - var iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up - function encode (source) { - if (source instanceof Uint8Array) { - } else if (ArrayBuffer.isView(source)) { - source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength) - } else if (Array.isArray(source)) { - source = Uint8Array.from(source) - } - if (!(source instanceof Uint8Array)) { throw new TypeError('Expected Uint8Array') } - if (source.length === 0) { return '' } - // Skip & count leading zeroes. - var zeroes = 0 - var length = 0 - var pbegin = 0 - var pend = source.length - while (pbegin !== pend && source[pbegin] === 0) { - pbegin++ - zeroes++ - } - // Allocate enough space in big-endian base58 representation. - var size = ((pend - pbegin) * iFACTOR + 1) >>> 0 - var b58 = new Uint8Array(size) - // Process the bytes. - while (pbegin !== pend) { - var carry = source[pbegin] - // Apply "b58 = b58 * 256 + ch". - var i = 0 - for (var it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) { - carry += (256 * b58[it1]) >>> 0 - b58[it1] = (carry % BASE) >>> 0 - carry = (carry / BASE) >>> 0 - } - if (carry !== 0) { throw new Error('Non-zero carry') } - length = i - pbegin++ - } - // Skip leading zeroes in base58 result. - var it2 = size - length - while (it2 !== size && b58[it2] === 0) { - it2++ - } - // Translate the result into a string. - var str = LEADER.repeat(zeroes) - for (; it2 < size; ++it2) { str += ALPHABET.charAt(b58[it2]) } - return str - } - function decodeUnsafe (source) { - if (typeof source !== 'string') { throw new TypeError('Expected String') } - if (source.length === 0) { return new Uint8Array() } - var psz = 0 - // Skip and count leading '1's. - var zeroes = 0 - var length = 0 - while (source[psz] === LEADER) { - zeroes++ - psz++ - } - // Allocate enough space in big-endian base256 representation. - var size = (((source.length - psz) * FACTOR) + 1) >>> 0 // log(58) / log(256), rounded up. - var b256 = new Uint8Array(size) - // Process the characters. - while (source[psz]) { - // Decode character - var carry = BASE_MAP[source.charCodeAt(psz)] - // Invalid character - if (carry === 255) { return } - var i = 0 - for (var it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) { - carry += (BASE * b256[it3]) >>> 0 - b256[it3] = (carry % 256) >>> 0 - carry = (carry / 256) >>> 0 - } - if (carry !== 0) { throw new Error('Non-zero carry') } - length = i - psz++ - } - // Skip leading zeroes in b256. - var it4 = size - length - while (it4 !== size && b256[it4] === 0) { - it4++ - } - var vch = new Uint8Array(zeroes + (size - it4)) - var j = zeroes - while (it4 !== size) { - vch[j++] = b256[it4++] - } - return vch - } - function decode (string) { - var buffer = decodeUnsafe(string) - if (buffer) { return buffer } - throw new Error('Non-base' + BASE + ' character') - } - return { - encode: encode, - decodeUnsafe: decodeUnsafe, - decode: decode - } -} -module.exports = base - -},{}],67:[function(require,module,exports){ -(function (Buffer){(function (){ -module.exports = function xor (a, b) { - var length = Math.min(a.length, b.length) - var buffer = new Buffer(length) - - for (var i = 0; i < length; ++i) { - buffer[i] = a[i] ^ b[i] - } - - return buffer -} - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"buffer":68}],68:[function(require,module,exports){ -(function (Buffer){(function (){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = require('base64-js') -var ieee754 = require('ieee754') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 - -var K_MAX_LENGTH = 0x7fffffff -exports.kMaxLength = K_MAX_LENGTH - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Print warning and recommend using `buffer` v4.x which has an Object - * implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * We report that the browser does not support typed arrays if the are not subclassable - * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` - * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support - * for __proto__ and has a buggy typed array implementation. - */ -Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() - -if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && - typeof console.error === 'function') { - console.error( - 'This browser lacks typed array (Uint8Array) support which is required by ' + - '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' - ) -} - -function typedArraySupport () { - // Can typed array instances can be augmented? - try { - var arr = new Uint8Array(1) - arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } - return arr.foo() === 42 - } catch (e) { - return false - } -} - -Object.defineProperty(Buffer.prototype, 'parent', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.buffer - } -}) - -Object.defineProperty(Buffer.prototype, 'offset', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.byteOffset - } -}) - -function createBuffer (length) { - if (length > K_MAX_LENGTH) { - throw new RangeError('The value "' + length + '" is invalid for option "size"') - } - // Return an augmented `Uint8Array` instance - var buf = new Uint8Array(length) - buf.__proto__ = Buffer.prototype - return buf -} - -/** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. - */ - -function Buffer (arg, encodingOrOffset, length) { - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new TypeError( - 'The "string" argument must be of type string. Received type number' - ) - } - return allocUnsafe(arg) - } - return from(arg, encodingOrOffset, length) -} - -// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 -if (typeof Symbol !== 'undefined' && Symbol.species != null && - Buffer[Symbol.species] === Buffer) { - Object.defineProperty(Buffer, Symbol.species, { - value: null, - configurable: true, - enumerable: false, - writable: false - }) -} - -Buffer.poolSize = 8192 // not used by this implementation - -function from (value, encodingOrOffset, length) { - if (typeof value === 'string') { - return fromString(value, encodingOrOffset) - } - - if (ArrayBuffer.isView(value)) { - return fromArrayLike(value) - } - - if (value == null) { - throw TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) - } - - if (isInstance(value, ArrayBuffer) || - (value && isInstance(value.buffer, ArrayBuffer))) { - return fromArrayBuffer(value, encodingOrOffset, length) - } - - if (typeof value === 'number') { - throw new TypeError( - 'The "value" argument must not be of type number. Received type number' - ) - } - - var valueOf = value.valueOf && value.valueOf() - if (valueOf != null && valueOf !== value) { - return Buffer.from(valueOf, encodingOrOffset, length) - } - - var b = fromObject(value) - if (b) return b - - if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && - typeof value[Symbol.toPrimitive] === 'function') { - return Buffer.from( - value[Symbol.toPrimitive]('string'), encodingOrOffset, length - ) - } - - throw new TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) -} - -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(value, encodingOrOffset, length) -} - -// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: -// https://github.com/feross/buffer/pull/148 -Buffer.prototype.__proto__ = Uint8Array.prototype -Buffer.__proto__ = Uint8Array - -function assertSize (size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be of type number') - } else if (size < 0) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } -} - -function alloc (size, fill, encoding) { - assertSize(size) - if (size <= 0) { - return createBuffer(size) - } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpretted as a start offset. - return typeof encoding === 'string' - ? createBuffer(size).fill(fill, encoding) - : createBuffer(size).fill(fill) - } - return createBuffer(size) -} - -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(size, fill, encoding) -} - -function allocUnsafe (size) { - assertSize(size) - return createBuffer(size < 0 ? 0 : checked(size) | 0) -} - -/** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(size) -} -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(size) -} - -function fromString (string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8' - } - - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - - var length = byteLength(string, encoding) | 0 - var buf = createBuffer(length) - - var actual = buf.write(string, encoding) - - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - buf = buf.slice(0, actual) - } - - return buf -} - -function fromArrayLike (array) { - var length = array.length < 0 ? 0 : checked(array.length) | 0 - var buf = createBuffer(length) - for (var i = 0; i < length; i += 1) { - buf[i] = array[i] & 255 - } - return buf -} - -function fromArrayBuffer (array, byteOffset, length) { - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('"offset" is outside of buffer bounds') - } - - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('"length" is outside of buffer bounds') - } - - var buf - if (byteOffset === undefined && length === undefined) { - buf = new Uint8Array(array) - } else if (length === undefined) { - buf = new Uint8Array(array, byteOffset) - } else { - buf = new Uint8Array(array, byteOffset, length) - } - - // Return an augmented `Uint8Array` instance - buf.__proto__ = Buffer.prototype - return buf -} - -function fromObject (obj) { - if (Buffer.isBuffer(obj)) { - var len = checked(obj.length) | 0 - var buf = createBuffer(len) - - if (buf.length === 0) { - return buf - } - - obj.copy(buf, 0, 0, len) - return buf - } - - if (obj.length !== undefined) { - if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { - return createBuffer(0) - } - return fromArrayLike(obj) - } - - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { - return fromArrayLike(obj.data) - } -} - -function checked (length) { - // Note: cannot use `length < K_MAX_LENGTH` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= K_MAX_LENGTH) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') - } - return length | 0 -} - -function SlowBuffer (length) { - if (+length != length) { // eslint-disable-line eqeqeq - length = 0 - } - return Buffer.alloc(+length) -} - -Buffer.isBuffer = function isBuffer (b) { - return b != null && b._isBuffer === true && - b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false -} - -Buffer.compare = function compare (a, b) { - if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) - if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError( - 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' - ) - } - - if (a === b) return 0 - - var x = a.length - var y = b.length - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i] - y = b[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } -} - -Buffer.concat = function concat (list, length) { - if (!Array.isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - - if (list.length === 0) { - return Buffer.alloc(0) - } - - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; ++i) { - length += list[i].length - } - } - - var buffer = Buffer.allocUnsafe(length) - var pos = 0 - for (i = 0; i < list.length; ++i) { - var buf = list[i] - if (isInstance(buf, Uint8Array)) { - buf = Buffer.from(buf) - } - if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - buf.copy(buffer, pos) - pos += buf.length - } - return buffer -} - -function byteLength (string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length - } - if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { - return string.byteLength - } - if (typeof string !== 'string') { - throw new TypeError( - 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + - 'Received type ' + typeof string - ) - } - - var len = string.length - var mustMatch = (arguments.length > 2 && arguments[2] === true) - if (!mustMatch && len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) { - return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 - } - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} -Buffer.byteLength = byteLength - -function slowToString (encoding, start, end) { - var loweredCase = false - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0 - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return '' - } - - if (end === undefined || end > this.length) { - end = this.length - } - - if (end <= 0) { - return '' - } - - // Force coersion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0 - start >>>= 0 - - if (end <= start) { - return '' - } - - if (!encoding) encoding = 'utf8' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'latin1': - case 'binary': - return latin1Slice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true - } - } -} - -// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) -// to detect a Buffer instance. It's not possible to use `instanceof Buffer` -// reliably in a browserify context because there could be multiple different -// copies of the 'buffer' package in use. This method works even for Buffer -// instances that were created from another copy of the `buffer` package. -// See: https://github.com/feross/buffer/issues/154 -Buffer.prototype._isBuffer = true - -function swap (b, n, m) { - var i = b[n] - b[n] = b[m] - b[m] = i -} - -Buffer.prototype.swap16 = function swap16 () { - var len = this.length - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits') - } - for (var i = 0; i < len; i += 2) { - swap(this, i, i + 1) - } - return this -} - -Buffer.prototype.swap32 = function swap32 () { - var len = this.length - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits') - } - for (var i = 0; i < len; i += 4) { - swap(this, i, i + 3) - swap(this, i + 1, i + 2) - } - return this -} - -Buffer.prototype.swap64 = function swap64 () { - var len = this.length - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits') - } - for (var i = 0; i < len; i += 8) { - swap(this, i, i + 7) - swap(this, i + 1, i + 6) - swap(this, i + 2, i + 5) - swap(this, i + 3, i + 4) - } - return this -} - -Buffer.prototype.toString = function toString () { - var length = this.length - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) -} - -Buffer.prototype.toLocaleString = Buffer.prototype.toString - -Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 -} - -Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() - if (this.length > max) str += ' ... ' - return '' -} - -Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { - if (isInstance(target, Uint8Array)) { - target = Buffer.from(target, target.offset, target.byteLength) - } - if (!Buffer.isBuffer(target)) { - throw new TypeError( - 'The "target" argument must be one of type Buffer or Uint8Array. ' + - 'Received type ' + (typeof target) - ) - } - - if (start === undefined) { - start = 0 - } - if (end === undefined) { - end = target ? target.length : 0 - } - if (thisStart === undefined) { - thisStart = 0 - } - if (thisEnd === undefined) { - thisEnd = this.length - } - - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index') - } - - if (thisStart >= thisEnd && start >= end) { - return 0 - } - if (thisStart >= thisEnd) { - return -1 - } - if (start >= end) { - return 1 - } - - start >>>= 0 - end >>>= 0 - thisStart >>>= 0 - thisEnd >>>= 0 - - if (this === target) return 0 - - var x = thisEnd - thisStart - var y = end - start - var len = Math.min(x, y) - - var thisCopy = this.slice(thisStart, thisEnd) - var targetCopy = target.slice(start, end) - - for (var i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i] - y = targetCopy[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1 - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset - byteOffset = 0 - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000 - } - byteOffset = +byteOffset // Coerce to Number. - if (numberIsNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : (buffer.length - 1) - } - - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset - if (byteOffset >= buffer.length) { - if (dir) return -1 - else byteOffset = buffer.length - 1 - } else if (byteOffset < 0) { - if (dir) byteOffset = 0 - else return -1 - } - - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding) - } - - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1 - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir) - } else if (typeof val === 'number') { - val = val & 0xFF // Search for a byte value [0-255] - if (typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) - } - } - return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) - } - - throw new TypeError('val must be string, number or Buffer') -} - -function arrayIndexOf (arr, val, byteOffset, encoding, dir) { - var indexSize = 1 - var arrLength = arr.length - var valLength = val.length - - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase() - if (encoding === 'ucs2' || encoding === 'ucs-2' || - encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1 - } - indexSize = 2 - arrLength /= 2 - valLength /= 2 - byteOffset /= 2 - } - } - - function read (buf, i) { - if (indexSize === 1) { - return buf[i] - } else { - return buf.readUInt16BE(i * indexSize) - } - } - - var i - if (dir) { - var foundIndex = -1 - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize - } else { - if (foundIndex !== -1) i -= i - foundIndex - foundIndex = -1 - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength - for (i = byteOffset; i >= 0; i--) { - var found = true - for (var j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false - break - } - } - if (found) return i - } - } - - return -1 -} - -Buffer.prototype.includes = function includes (val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1 -} - -Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true) -} - -Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false) -} - -function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining - } - } - - var strLen = string.length - - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; ++i) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (numberIsNaN(parsed)) return i - buf[offset + i] = parsed - } - return i -} - -function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) -} - -function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) -} - -function latin1Write (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) -} - -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) -} - -function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) -} - -Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0 - if (isFinite(length)) { - length = length >>> 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined - } - } else { - throw new Error( - 'Buffer.write(string, encoding, offset[, length]) is no longer supported' - ) - } - - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds') - } - - if (!encoding) encoding = 'utf8' - - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) - - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) - - case 'ascii': - return asciiWrite(this, string, offset, length) - - case 'latin1': - case 'binary': - return latin1Write(this, string, offset, length) - - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} - -Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } -} - -function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) - } -} - -function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 - - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint - - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } - } - } - - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF - } - - res.push(codePoint) - i += bytesPerSequence - } - - return decodeCodePointsArray(res) -} - -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -var MAX_ARGUMENTS_LENGTH = 0x1000 - -function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) - } - return res -} - -function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F) - } - return ret -} - -function latin1Slice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]) - } - return ret -} - -function hexSlice (buf, start, end) { - var len = buf.length - - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len - - var out = '' - for (var i = start; i < end; ++i) { - out += toHex(buf[i]) - } - return out -} - -function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) - } - return res -} - -Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end - - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } - - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len - } - - if (end < start) end = start - - var newBuf = this.subarray(start, end) - // Return an augmented `Uint8Array` instance - newBuf.__proto__ = Buffer.prototype - return newBuf -} - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - */ -function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') -} - -Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - - return val -} - -Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } - - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul - } - - return val -} - -Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] -} - -Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) -} - -Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] -} - -Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) -} - -Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) -} - -Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) -} - -Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) -} - -Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) -} - -Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) -} - -Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) -} - -Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) -} - -Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) -} - -function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') - if (offset + ext > buf.length) throw new RangeError('Index out of range') -} - -Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = 0 - var mul = 1 - var sub = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = byteLength - 1 - var mul = 1 - var sub = 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - return offset + 4 -} - -Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -function checkIEEE754 (buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range') - if (offset < 0) throw new RangeError('Index out of range') -} - -function writeFloat (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) - } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 -} - -Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) -} - -function writeDouble (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) - } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 -} - -Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) -} - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start - - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') - } - if (start < 0 || start >= this.length) throw new RangeError('Index out of range') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } - - var len = end - start - - if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { - // Use built-in when available, missing from IE11 - this.copyWithin(targetStart, start, end) - } else if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (var i = len - 1; i >= 0; --i) { - target[i + targetStart] = this[i + start] - } - } else { - Uint8Array.prototype.set.call( - target, - this.subarray(start, end), - targetStart - ) - } - - return len -} - -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill (val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start - start = 0 - end = this.length - } else if (typeof end === 'string') { - encoding = end - end = this.length - } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string') - } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - if (val.length === 1) { - var code = val.charCodeAt(0) - if ((encoding === 'utf8' && code < 128) || - encoding === 'latin1') { - // Fast path: If `val` fits into a single byte, use that numeric value. - val = code - } - } - } else if (typeof val === 'number') { - val = val & 255 - } - - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index') - } - - if (end <= start) { - return this - } - - start = start >>> 0 - end = end === undefined ? this.length : end >>> 0 - - if (!val) val = 0 - - var i - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val - } - } else { - var bytes = Buffer.isBuffer(val) - ? val - : Buffer.from(val, encoding) - var len = bytes.length - if (len === 0) { - throw new TypeError('The value "' + val + - '" is invalid for argument "value"') - } - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len] - } - } - - return this -} - -// HELPER FUNCTIONS -// ================ - -var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g - -function base64clean (str) { - // Node takes equal signs as end of the Base64 encoding - str = str.split('=')[0] - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = str.trim().replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str -} - -function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) -} - -function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] - - for (var i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') - } - } - - return bytes -} - -function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) - } - return byteArray -} - -function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) - } - - return byteArray -} - -function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) -} - -function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; ++i) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] - } - return i -} - -// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass -// the `instanceof` check but they should be treated as of that type. -// See: https://github.com/feross/buffer/issues/166 -function isInstance (obj, type) { - return obj instanceof type || - (obj != null && obj.constructor != null && obj.constructor.name != null && - obj.constructor.name === type.name) -} -function numberIsNaN (obj) { - // For IE11 support - return obj !== obj // eslint-disable-line no-self-compare -} - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"base64-js":17,"buffer":68,"ieee754":145}],69:[function(require,module,exports){ -'use strict'; - -var GetIntrinsic = require('get-intrinsic'); - -var callBind = require('./'); - -var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf')); - -module.exports = function callBoundIntrinsic(name, allowMissing) { - var intrinsic = GetIntrinsic(name, !!allowMissing); - if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) { - return callBind(intrinsic); - } - return intrinsic; -}; - -},{"./":70,"get-intrinsic":110}],70:[function(require,module,exports){ -'use strict'; - -var bind = require('function-bind'); -var GetIntrinsic = require('get-intrinsic'); - -var $apply = GetIntrinsic('%Function.prototype.apply%'); -var $call = GetIntrinsic('%Function.prototype.call%'); -var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply); - -var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true); -var $defineProperty = GetIntrinsic('%Object.defineProperty%', true); -var $max = GetIntrinsic('%Math.max%'); - -if ($defineProperty) { - try { - $defineProperty({}, 'a', { value: 1 }); - } catch (e) { - // IE 8 has a broken defineProperty - $defineProperty = null; - } -} - -module.exports = function callBind(originalFunction) { - var func = $reflectApply(bind, $call, arguments); - if ($gOPD && $defineProperty) { - var desc = $gOPD(func, 'length'); - if (desc.configurable) { - // original length, plus the receiver, minus any additional arguments (after the receiver) - $defineProperty( - func, - 'length', - { value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) } - ); - } - } - return func; -}; - -var applyBind = function applyBind() { - return $reflectApply(bind, $apply, arguments); -}; - -if ($defineProperty) { - $defineProperty(module.exports, 'apply', { value: applyBind }); -} else { - module.exports.apply = applyBind; -} - -},{"function-bind":109,"get-intrinsic":110}],71:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var Transform = require('stream').Transform -var StringDecoder = require('string_decoder').StringDecoder -var inherits = require('inherits') - -function CipherBase (hashMode) { - Transform.call(this) - this.hashMode = typeof hashMode === 'string' - if (this.hashMode) { - this[hashMode] = this._finalOrDigest - } else { - this.final = this._finalOrDigest - } - if (this._final) { - this.__final = this._final - this._final = null - } - this._decoder = null - this._encoding = null -} -inherits(CipherBase, Transform) - -CipherBase.prototype.update = function (data, inputEnc, outputEnc) { - if (typeof data === 'string') { - data = Buffer.from(data, inputEnc) - } - - var outData = this._update(data) - if (this.hashMode) return this - - if (outputEnc) { - outData = this._toString(outData, outputEnc) - } - - return outData -} - -CipherBase.prototype.setAutoPadding = function () {} -CipherBase.prototype.getAuthTag = function () { - throw new Error('trying to get auth tag in unsupported state') -} - -CipherBase.prototype.setAuthTag = function () { - throw new Error('trying to set auth tag in unsupported state') -} - -CipherBase.prototype.setAAD = function () { - throw new Error('trying to set aad in unsupported state') -} - -CipherBase.prototype._transform = function (data, _, next) { - var err - try { - if (this.hashMode) { - this._update(data) - } else { - this.push(this._update(data)) - } - } catch (e) { - err = e - } finally { - next(err) - } -} -CipherBase.prototype._flush = function (done) { - var err - try { - this.push(this.__final()) - } catch (e) { - err = e - } - - done(err) -} -CipherBase.prototype._finalOrDigest = function (outputEnc) { - var outData = this.__final() || Buffer.alloc(0) - if (outputEnc) { - outData = this._toString(outData, outputEnc, true) - } - return outData -} - -CipherBase.prototype._toString = function (value, enc, fin) { - if (!this._decoder) { - this._decoder = new StringDecoder(enc) - this._encoding = enc - } - - if (this._encoding !== enc) throw new Error('can\'t switch encodings') - - var out = this._decoder.write(value) - if (fin) { - out += this._decoder.end() - } - - return out -} - -module.exports = CipherBase - -},{"inherits":146,"safe-buffer":250,"stream":264,"string_decoder":279}],72:[function(require,module,exports){ -/*! - * content-type - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict' - -/** - * RegExp to match *( ";" parameter ) in RFC 7231 sec 3.1.1.1 - * - * parameter = token "=" ( token / quoted-string ) - * token = 1*tchar - * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" - * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" - * / DIGIT / ALPHA - * ; any VCHAR, except delimiters - * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE - * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text - * obs-text = %x80-FF - * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - */ -var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g // eslint-disable-line no-control-regex -var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/ // eslint-disable-line no-control-regex -var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/ - -/** - * RegExp to match quoted-pair in RFC 7230 sec 3.2.6 - * - * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - * obs-text = %x80-FF - */ -var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g // eslint-disable-line no-control-regex - -/** - * RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6 - */ -var QUOTE_REGEXP = /([\\"])/g - -/** - * RegExp to match type in RFC 7231 sec 3.1.1.1 - * - * media-type = type "/" subtype - * type = token - * subtype = token - */ -var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/ - -/** - * Module exports. - * @public - */ - -exports.format = format -exports.parse = parse - -/** - * Format object to media type. - * - * @param {object} obj - * @return {string} - * @public - */ - -function format (obj) { - if (!obj || typeof obj !== 'object') { - throw new TypeError('argument obj is required') - } - - var parameters = obj.parameters - var type = obj.type - - if (!type || !TYPE_REGEXP.test(type)) { - throw new TypeError('invalid type') - } - - var string = type - - // append parameters - if (parameters && typeof parameters === 'object') { - var param - var params = Object.keys(parameters).sort() - - for (var i = 0; i < params.length; i++) { - param = params[i] - - if (!TOKEN_REGEXP.test(param)) { - throw new TypeError('invalid parameter name') - } - - string += '; ' + param + '=' + qstring(parameters[param]) - } - } - - return string -} - -/** - * Parse media type to object. - * - * @param {string|object} string - * @return {Object} - * @public - */ - -function parse (string) { - if (!string) { - throw new TypeError('argument string is required') - } - - // support req/res-like objects as argument - var header = typeof string === 'object' - ? getcontenttype(string) - : string - - if (typeof header !== 'string') { - throw new TypeError('argument string is required to be a string') - } - - var index = header.indexOf(';') - var type = index !== -1 - ? header.slice(0, index).trim() - : header.trim() - - if (!TYPE_REGEXP.test(type)) { - throw new TypeError('invalid media type') - } - - var obj = new ContentType(type.toLowerCase()) - - // parse parameters - if (index !== -1) { - var key - var match - var value - - PARAM_REGEXP.lastIndex = index - - while ((match = PARAM_REGEXP.exec(header))) { - if (match.index !== index) { - throw new TypeError('invalid parameter format') - } - - index += match[0].length - key = match[1].toLowerCase() - value = match[2] - - if (value.charCodeAt(0) === 0x22 /* " */) { - // remove quotes - value = value.slice(1, -1) - - // remove escapes - if (value.indexOf('\\') !== -1) { - value = value.replace(QESC_REGEXP, '$1') - } - } - - obj.parameters[key] = value - } - - if (index !== header.length) { - throw new TypeError('invalid parameter format') - } - } - - return obj -} - -/** - * Get content-type from req/res objects. - * - * @param {object} - * @return {Object} - * @private - */ - -function getcontenttype (obj) { - var header - - if (typeof obj.getHeader === 'function') { - // res-like - header = obj.getHeader('content-type') - } else if (typeof obj.headers === 'object') { - // req-like - header = obj.headers && obj.headers['content-type'] - } - - if (typeof header !== 'string') { - throw new TypeError('content-type header is missing from object') - } - - return header -} - -/** - * Quote a string if necessary. - * - * @param {string} val - * @return {string} - * @private - */ - -function qstring (val) { - var str = String(val) - - // no need to quote tokens - if (TOKEN_REGEXP.test(str)) { - return str - } - - if (str.length > 0 && !TEXT_REGEXP.test(str)) { - throw new TypeError('invalid parameter value') - } - - return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"' -} - -/** - * Class to represent a content type. - * @private - */ -function ContentType (type) { - this.parameters = Object.create(null) - this.type = type -} - -},{}],73:[function(require,module,exports){ -(function (Buffer){(function (){ -var elliptic = require('elliptic') -var BN = require('bn.js') - -module.exports = function createECDH (curve) { - return new ECDH(curve) -} - -var aliases = { - secp256k1: { - name: 'secp256k1', - byteLength: 32 - }, - secp224r1: { - name: 'p224', - byteLength: 28 - }, - prime256v1: { - name: 'p256', - byteLength: 32 - }, - prime192v1: { - name: 'p192', - byteLength: 24 - }, - ed25519: { - name: 'ed25519', - byteLength: 32 - }, - secp384r1: { - name: 'p384', - byteLength: 48 - }, - secp521r1: { - name: 'p521', - byteLength: 66 - } -} - -aliases.p224 = aliases.secp224r1 -aliases.p256 = aliases.secp256r1 = aliases.prime256v1 -aliases.p192 = aliases.secp192r1 = aliases.prime192v1 -aliases.p384 = aliases.secp384r1 -aliases.p521 = aliases.secp521r1 - -function ECDH (curve) { - this.curveType = aliases[curve] - if (!this.curveType) { - this.curveType = { - name: curve - } - } - this.curve = new elliptic.ec(this.curveType.name) // eslint-disable-line new-cap - this.keys = void 0 -} - -ECDH.prototype.generateKeys = function (enc, format) { - this.keys = this.curve.genKeyPair() - return this.getPublicKey(enc, format) -} - -ECDH.prototype.computeSecret = function (other, inenc, enc) { - inenc = inenc || 'utf8' - if (!Buffer.isBuffer(other)) { - other = new Buffer(other, inenc) - } - var otherPub = this.curve.keyFromPublic(other).getPublic() - var out = otherPub.mul(this.keys.getPrivate()).getX() - return formatReturnValue(out, enc, this.curveType.byteLength) -} - -ECDH.prototype.getPublicKey = function (enc, format) { - var key = this.keys.getPublic(format === 'compressed', true) - if (format === 'hybrid') { - if (key[key.length - 1] % 2) { - key[0] = 7 - } else { - key[0] = 6 - } - } - return formatReturnValue(key, enc) -} - -ECDH.prototype.getPrivateKey = function (enc) { - return formatReturnValue(this.keys.getPrivate(), enc) -} - -ECDH.prototype.setPublicKey = function (pub, enc) { - enc = enc || 'utf8' - if (!Buffer.isBuffer(pub)) { - pub = new Buffer(pub, enc) - } - this.keys._importPublic(pub) - return this -} - -ECDH.prototype.setPrivateKey = function (priv, enc) { - enc = enc || 'utf8' - if (!Buffer.isBuffer(priv)) { - priv = new Buffer(priv, enc) - } - - var _priv = new BN(priv) - _priv = _priv.toString(16) - this.keys = this.curve.genKeyPair() - this.keys._importPrivate(_priv) - return this -} - -function formatReturnValue (bn, enc, len) { - if (!Array.isArray(bn)) { - bn = bn.toArray() - } - var buf = new Buffer(bn) - if (len && buf.length < len) { - var zeros = new Buffer(len - buf.length) - zeros.fill(0) - buf = Buffer.concat([zeros, buf]) - } - if (!enc) { - return buf - } else { - return buf.toString(enc) - } -} - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"bn.js":18,"buffer":68,"elliptic":89}],74:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var MD5 = require('md5.js') -var RIPEMD160 = require('ripemd160') -var sha = require('sha.js') -var Base = require('cipher-base') - -function Hash (hash) { - Base.call(this, 'digest') - - this._hash = hash -} - -inherits(Hash, Base) - -Hash.prototype._update = function (data) { - this._hash.update(data) -} - -Hash.prototype._final = function () { - return this._hash.digest() -} - -module.exports = function createHash (alg) { - alg = alg.toLowerCase() - if (alg === 'md5') return new MD5() - if (alg === 'rmd160' || alg === 'ripemd160') return new RIPEMD160() - - return new Hash(sha(alg)) -} - -},{"cipher-base":71,"inherits":146,"md5.js":221,"ripemd160":249,"sha.js":257}],75:[function(require,module,exports){ -var MD5 = require('md5.js') - -module.exports = function (buffer) { - return new MD5().update(buffer).digest() -} - -},{"md5.js":221}],76:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var Legacy = require('./legacy') -var Base = require('cipher-base') -var Buffer = require('safe-buffer').Buffer -var md5 = require('create-hash/md5') -var RIPEMD160 = require('ripemd160') - -var sha = require('sha.js') - -var ZEROS = Buffer.alloc(128) - -function Hmac (alg, key) { - Base.call(this, 'digest') - if (typeof key === 'string') { - key = Buffer.from(key) - } - - var blocksize = (alg === 'sha512' || alg === 'sha384') ? 128 : 64 - - this._alg = alg - this._key = key - if (key.length > blocksize) { - var hash = alg === 'rmd160' ? new RIPEMD160() : sha(alg) - key = hash.update(key).digest() - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) - } - - var ipad = this._ipad = Buffer.allocUnsafe(blocksize) - var opad = this._opad = Buffer.allocUnsafe(blocksize) - - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C - } - this._hash = alg === 'rmd160' ? new RIPEMD160() : sha(alg) - this._hash.update(ipad) -} - -inherits(Hmac, Base) - -Hmac.prototype._update = function (data) { - this._hash.update(data) -} - -Hmac.prototype._final = function () { - var h = this._hash.digest() - var hash = this._alg === 'rmd160' ? new RIPEMD160() : sha(this._alg) - return hash.update(this._opad).update(h).digest() -} - -module.exports = function createHmac (alg, key) { - alg = alg.toLowerCase() - if (alg === 'rmd160' || alg === 'ripemd160') { - return new Hmac('rmd160', key) - } - if (alg === 'md5') { - return new Legacy(md5, key) - } - return new Hmac(alg, key) -} - -},{"./legacy":77,"cipher-base":71,"create-hash/md5":75,"inherits":146,"ripemd160":249,"safe-buffer":250,"sha.js":257}],77:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var Buffer = require('safe-buffer').Buffer - -var Base = require('cipher-base') - -var ZEROS = Buffer.alloc(128) -var blocksize = 64 - -function Hmac (alg, key) { - Base.call(this, 'digest') - if (typeof key === 'string') { - key = Buffer.from(key) - } - - this._alg = alg - this._key = key - - if (key.length > blocksize) { - key = alg(key) - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) - } - - var ipad = this._ipad = Buffer.allocUnsafe(blocksize) - var opad = this._opad = Buffer.allocUnsafe(blocksize) - - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C - } - - this._hash = [ipad] -} - -inherits(Hmac, Base) - -Hmac.prototype._update = function (data) { - this._hash.push(data) -} - -Hmac.prototype._final = function () { - var h = this._alg(Buffer.concat(this._hash)) - return this._alg(Buffer.concat([this._opad, h])) -} -module.exports = Hmac - -},{"cipher-base":71,"inherits":146,"safe-buffer":250}],78:[function(require,module,exports){ -'use strict' - -exports.randomBytes = exports.rng = exports.pseudoRandomBytes = exports.prng = require('randombytes') -exports.createHash = exports.Hash = require('create-hash') -exports.createHmac = exports.Hmac = require('create-hmac') - -var algos = require('browserify-sign/algos') -var algoKeys = Object.keys(algos) -var hashes = ['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'md5', 'rmd160'].concat(algoKeys) -exports.getHashes = function () { - return hashes -} - -var p = require('pbkdf2') -exports.pbkdf2 = p.pbkdf2 -exports.pbkdf2Sync = p.pbkdf2Sync - -var aes = require('browserify-cipher') - -exports.Cipher = aes.Cipher -exports.createCipher = aes.createCipher -exports.Cipheriv = aes.Cipheriv -exports.createCipheriv = aes.createCipheriv -exports.Decipher = aes.Decipher -exports.createDecipher = aes.createDecipher -exports.Decipheriv = aes.Decipheriv -exports.createDecipheriv = aes.createDecipheriv -exports.getCiphers = aes.getCiphers -exports.listCiphers = aes.listCiphers - -var dh = require('diffie-hellman') - -exports.DiffieHellmanGroup = dh.DiffieHellmanGroup -exports.createDiffieHellmanGroup = dh.createDiffieHellmanGroup -exports.getDiffieHellman = dh.getDiffieHellman -exports.createDiffieHellman = dh.createDiffieHellman -exports.DiffieHellman = dh.DiffieHellman - -var sign = require('browserify-sign') - -exports.createSign = sign.createSign -exports.Sign = sign.Sign -exports.createVerify = sign.createVerify -exports.Verify = sign.Verify - -exports.createECDH = require('create-ecdh') - -var publicEncrypt = require('public-encrypt') - -exports.publicEncrypt = publicEncrypt.publicEncrypt -exports.privateEncrypt = publicEncrypt.privateEncrypt -exports.publicDecrypt = publicEncrypt.publicDecrypt -exports.privateDecrypt = publicEncrypt.privateDecrypt - -// the least I can do is make error messages for the rest of the node.js/crypto api. -// ;[ -// 'createCredentials' -// ].forEach(function (name) { -// exports[name] = function () { -// throw new Error([ -// 'sorry, ' + name + ' is not implemented yet', -// 'we accept pull requests', -// 'https://github.com/crypto-browserify/crypto-browserify' -// ].join('\n')) -// } -// }) - -var rf = require('randomfill') - -exports.randomFill = rf.randomFill -exports.randomFillSync = rf.randomFillSync - -exports.createCredentials = function () { - throw new Error([ - 'sorry, createCredentials is not implemented yet', - 'we accept pull requests', - 'https://github.com/crypto-browserify/crypto-browserify' - ].join('\n')) -} - -exports.constants = { - 'DH_CHECK_P_NOT_SAFE_PRIME': 2, - 'DH_CHECK_P_NOT_PRIME': 1, - 'DH_UNABLE_TO_CHECK_GENERATOR': 4, - 'DH_NOT_SUITABLE_GENERATOR': 8, - 'NPN_ENABLED': 1, - 'ALPN_ENABLED': 1, - 'RSA_PKCS1_PADDING': 1, - 'RSA_SSLV23_PADDING': 2, - 'RSA_NO_PADDING': 3, - 'RSA_PKCS1_OAEP_PADDING': 4, - 'RSA_X931_PADDING': 5, - 'RSA_PKCS1_PSS_PADDING': 6, - 'POINT_CONVERSION_COMPRESSED': 2, - 'POINT_CONVERSION_UNCOMPRESSED': 4, - 'POINT_CONVERSION_HYBRID': 6 -} - -},{"browserify-cipher":38,"browserify-sign":46,"browserify-sign/algos":43,"create-ecdh":73,"create-hash":74,"create-hmac":76,"diffie-hellman":85,"pbkdf2":231,"public-encrypt":238,"randombytes":244,"randomfill":245}],79:[function(require,module,exports){ -'use strict'; - -exports.utils = require('./des/utils'); -exports.Cipher = require('./des/cipher'); -exports.DES = require('./des/des'); -exports.CBC = require('./des/cbc'); -exports.EDE = require('./des/ede'); - -},{"./des/cbc":80,"./des/cipher":81,"./des/des":82,"./des/ede":83,"./des/utils":84}],80:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -var proto = {}; - -function CBCState(iv) { - assert.equal(iv.length, 8, 'Invalid IV length'); - - this.iv = new Array(8); - for (var i = 0; i < this.iv.length; i++) - this.iv[i] = iv[i]; -} - -function instantiate(Base) { - function CBC(options) { - Base.call(this, options); - this._cbcInit(); - } - inherits(CBC, Base); - - var keys = Object.keys(proto); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - CBC.prototype[key] = proto[key]; - } - - CBC.create = function create(options) { - return new CBC(options); - }; - - return CBC; -} - -exports.instantiate = instantiate; - -proto._cbcInit = function _cbcInit() { - var state = new CBCState(this.options.iv); - this._cbcState = state; -}; - -proto._update = function _update(inp, inOff, out, outOff) { - var state = this._cbcState; - var superProto = this.constructor.super_.prototype; - - var iv = state.iv; - if (this.type === 'encrypt') { - for (var i = 0; i < this.blockSize; i++) - iv[i] ^= inp[inOff + i]; - - superProto._update.call(this, iv, 0, out, outOff); - - for (var i = 0; i < this.blockSize; i++) - iv[i] = out[outOff + i]; - } else { - superProto._update.call(this, inp, inOff, out, outOff); - - for (var i = 0; i < this.blockSize; i++) - out[outOff + i] ^= iv[i]; - - for (var i = 0; i < this.blockSize; i++) - iv[i] = inp[inOff + i]; - } -}; - -},{"inherits":146,"minimalistic-assert":223}],81:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); - -function Cipher(options) { - this.options = options; - - this.type = this.options.type; - this.blockSize = 8; - this._init(); - - this.buffer = new Array(this.blockSize); - this.bufferOff = 0; -} -module.exports = Cipher; - -Cipher.prototype._init = function _init() { - // Might be overrided -}; - -Cipher.prototype.update = function update(data) { - if (data.length === 0) - return []; - - if (this.type === 'decrypt') - return this._updateDecrypt(data); - else - return this._updateEncrypt(data); -}; - -Cipher.prototype._buffer = function _buffer(data, off) { - // Append data to buffer - var min = Math.min(this.buffer.length - this.bufferOff, data.length - off); - for (var i = 0; i < min; i++) - this.buffer[this.bufferOff + i] = data[off + i]; - this.bufferOff += min; - - // Shift next - return min; -}; - -Cipher.prototype._flushBuffer = function _flushBuffer(out, off) { - this._update(this.buffer, 0, out, off); - this.bufferOff = 0; - return this.blockSize; -}; - -Cipher.prototype._updateEncrypt = function _updateEncrypt(data) { - var inputOff = 0; - var outputOff = 0; - - var count = ((this.bufferOff + data.length) / this.blockSize) | 0; - var out = new Array(count * this.blockSize); - - if (this.bufferOff !== 0) { - inputOff += this._buffer(data, inputOff); - - if (this.bufferOff === this.buffer.length) - outputOff += this._flushBuffer(out, outputOff); - } - - // Write blocks - var max = data.length - ((data.length - inputOff) % this.blockSize); - for (; inputOff < max; inputOff += this.blockSize) { - this._update(data, inputOff, out, outputOff); - outputOff += this.blockSize; - } - - // Queue rest - for (; inputOff < data.length; inputOff++, this.bufferOff++) - this.buffer[this.bufferOff] = data[inputOff]; - - return out; -}; - -Cipher.prototype._updateDecrypt = function _updateDecrypt(data) { - var inputOff = 0; - var outputOff = 0; - - var count = Math.ceil((this.bufferOff + data.length) / this.blockSize) - 1; - var out = new Array(count * this.blockSize); - - // TODO(indutny): optimize it, this is far from optimal - for (; count > 0; count--) { - inputOff += this._buffer(data, inputOff); - outputOff += this._flushBuffer(out, outputOff); - } - - // Buffer rest of the input - inputOff += this._buffer(data, inputOff); - - return out; -}; - -Cipher.prototype.final = function final(buffer) { - var first; - if (buffer) - first = this.update(buffer); - - var last; - if (this.type === 'encrypt') - last = this._finalEncrypt(); - else - last = this._finalDecrypt(); - - if (first) - return first.concat(last); - else - return last; -}; - -Cipher.prototype._pad = function _pad(buffer, off) { - if (off === 0) - return false; - - while (off < buffer.length) - buffer[off++] = 0; - - return true; -}; - -Cipher.prototype._finalEncrypt = function _finalEncrypt() { - if (!this._pad(this.buffer, this.bufferOff)) - return []; - - var out = new Array(this.blockSize); - this._update(this.buffer, 0, out, 0); - return out; -}; - -Cipher.prototype._unpad = function _unpad(buffer) { - return buffer; -}; - -Cipher.prototype._finalDecrypt = function _finalDecrypt() { - assert.equal(this.bufferOff, this.blockSize, 'Not enough data to decrypt'); - var out = new Array(this.blockSize); - this._flushBuffer(out, 0); - - return this._unpad(out); -}; - -},{"minimalistic-assert":223}],82:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -var utils = require('./utils'); -var Cipher = require('./cipher'); - -function DESState() { - this.tmp = new Array(2); - this.keys = null; -} - -function DES(options) { - Cipher.call(this, options); - - var state = new DESState(); - this._desState = state; - - this.deriveKeys(state, options.key); -} -inherits(DES, Cipher); -module.exports = DES; - -DES.create = function create(options) { - return new DES(options); -}; - -var shiftTable = [ - 1, 1, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 1 -]; - -DES.prototype.deriveKeys = function deriveKeys(state, key) { - state.keys = new Array(16 * 2); - - assert.equal(key.length, this.blockSize, 'Invalid key length'); - - var kL = utils.readUInt32BE(key, 0); - var kR = utils.readUInt32BE(key, 4); - - utils.pc1(kL, kR, state.tmp, 0); - kL = state.tmp[0]; - kR = state.tmp[1]; - for (var i = 0; i < state.keys.length; i += 2) { - var shift = shiftTable[i >>> 1]; - kL = utils.r28shl(kL, shift); - kR = utils.r28shl(kR, shift); - utils.pc2(kL, kR, state.keys, i); - } -}; - -DES.prototype._update = function _update(inp, inOff, out, outOff) { - var state = this._desState; - - var l = utils.readUInt32BE(inp, inOff); - var r = utils.readUInt32BE(inp, inOff + 4); - - // Initial Permutation - utils.ip(l, r, state.tmp, 0); - l = state.tmp[0]; - r = state.tmp[1]; - - if (this.type === 'encrypt') - this._encrypt(state, l, r, state.tmp, 0); - else - this._decrypt(state, l, r, state.tmp, 0); - - l = state.tmp[0]; - r = state.tmp[1]; - - utils.writeUInt32BE(out, l, outOff); - utils.writeUInt32BE(out, r, outOff + 4); -}; - -DES.prototype._pad = function _pad(buffer, off) { - var value = buffer.length - off; - for (var i = off; i < buffer.length; i++) - buffer[i] = value; - - return true; -}; - -DES.prototype._unpad = function _unpad(buffer) { - var pad = buffer[buffer.length - 1]; - for (var i = buffer.length - pad; i < buffer.length; i++) - assert.equal(buffer[i], pad); - - return buffer.slice(0, buffer.length - pad); -}; - -DES.prototype._encrypt = function _encrypt(state, lStart, rStart, out, off) { - var l = lStart; - var r = rStart; - - // Apply f() x16 times - for (var i = 0; i < state.keys.length; i += 2) { - var keyL = state.keys[i]; - var keyR = state.keys[i + 1]; - - // f(r, k) - utils.expand(r, state.tmp, 0); - - keyL ^= state.tmp[0]; - keyR ^= state.tmp[1]; - var s = utils.substitute(keyL, keyR); - var f = utils.permute(s); - - var t = r; - r = (l ^ f) >>> 0; - l = t; - } - - // Reverse Initial Permutation - utils.rip(r, l, out, off); -}; - -DES.prototype._decrypt = function _decrypt(state, lStart, rStart, out, off) { - var l = rStart; - var r = lStart; - - // Apply f() x16 times - for (var i = state.keys.length - 2; i >= 0; i -= 2) { - var keyL = state.keys[i]; - var keyR = state.keys[i + 1]; - - // f(r, k) - utils.expand(l, state.tmp, 0); - - keyL ^= state.tmp[0]; - keyR ^= state.tmp[1]; - var s = utils.substitute(keyL, keyR); - var f = utils.permute(s); - - var t = l; - l = (r ^ f) >>> 0; - r = t; - } - - // Reverse Initial Permutation - utils.rip(l, r, out, off); -}; - -},{"./cipher":81,"./utils":84,"inherits":146,"minimalistic-assert":223}],83:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -var Cipher = require('./cipher'); -var DES = require('./des'); - -function EDEState(type, key) { - assert.equal(key.length, 24, 'Invalid key length'); - - var k1 = key.slice(0, 8); - var k2 = key.slice(8, 16); - var k3 = key.slice(16, 24); - - if (type === 'encrypt') { - this.ciphers = [ - DES.create({ type: 'encrypt', key: k1 }), - DES.create({ type: 'decrypt', key: k2 }), - DES.create({ type: 'encrypt', key: k3 }) - ]; - } else { - this.ciphers = [ - DES.create({ type: 'decrypt', key: k3 }), - DES.create({ type: 'encrypt', key: k2 }), - DES.create({ type: 'decrypt', key: k1 }) - ]; - } -} - -function EDE(options) { - Cipher.call(this, options); - - var state = new EDEState(this.type, this.options.key); - this._edeState = state; -} -inherits(EDE, Cipher); - -module.exports = EDE; - -EDE.create = function create(options) { - return new EDE(options); -}; - -EDE.prototype._update = function _update(inp, inOff, out, outOff) { - var state = this._edeState; - - state.ciphers[0]._update(inp, inOff, out, outOff); - state.ciphers[1]._update(out, outOff, out, outOff); - state.ciphers[2]._update(out, outOff, out, outOff); -}; - -EDE.prototype._pad = DES.prototype._pad; -EDE.prototype._unpad = DES.prototype._unpad; - -},{"./cipher":81,"./des":82,"inherits":146,"minimalistic-assert":223}],84:[function(require,module,exports){ -'use strict'; - -exports.readUInt32BE = function readUInt32BE(bytes, off) { - var res = (bytes[0 + off] << 24) | - (bytes[1 + off] << 16) | - (bytes[2 + off] << 8) | - bytes[3 + off]; - return res >>> 0; -}; - -exports.writeUInt32BE = function writeUInt32BE(bytes, value, off) { - bytes[0 + off] = value >>> 24; - bytes[1 + off] = (value >>> 16) & 0xff; - bytes[2 + off] = (value >>> 8) & 0xff; - bytes[3 + off] = value & 0xff; -}; - -exports.ip = function ip(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - for (var i = 6; i >= 0; i -= 2) { - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >>> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inL >>> (j + i)) & 1; - } - } - - for (var i = 6; i >= 0; i -= 2) { - for (var j = 1; j <= 25; j += 8) { - outR <<= 1; - outR |= (inR >>> (j + i)) & 1; - } - for (var j = 1; j <= 25; j += 8) { - outR <<= 1; - outR |= (inL >>> (j + i)) & 1; - } - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.rip = function rip(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - for (var i = 0; i < 4; i++) { - for (var j = 24; j >= 0; j -= 8) { - outL <<= 1; - outL |= (inR >>> (j + i)) & 1; - outL <<= 1; - outL |= (inL >>> (j + i)) & 1; - } - } - for (var i = 4; i < 8; i++) { - for (var j = 24; j >= 0; j -= 8) { - outR <<= 1; - outR |= (inR >>> (j + i)) & 1; - outR <<= 1; - outR |= (inL >>> (j + i)) & 1; - } - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.pc1 = function pc1(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - // 7, 15, 23, 31, 39, 47, 55, 63 - // 6, 14, 22, 30, 39, 47, 55, 63 - // 5, 13, 21, 29, 39, 47, 55, 63 - // 4, 12, 20, 28 - for (var i = 7; i >= 5; i--) { - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inL >> (j + i)) & 1; - } - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >> (j + i)) & 1; - } - - // 1, 9, 17, 25, 33, 41, 49, 57 - // 2, 10, 18, 26, 34, 42, 50, 58 - // 3, 11, 19, 27, 35, 43, 51, 59 - // 36, 44, 52, 60 - for (var i = 1; i <= 3; i++) { - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inR >> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inL >> (j + i)) & 1; - } - } - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inL >> (j + i)) & 1; - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.r28shl = function r28shl(num, shift) { - return ((num << shift) & 0xfffffff) | (num >>> (28 - shift)); -}; - -var pc2table = [ - // inL => outL - 14, 11, 17, 4, 27, 23, 25, 0, - 13, 22, 7, 18, 5, 9, 16, 24, - 2, 20, 12, 21, 1, 8, 15, 26, - - // inR => outR - 15, 4, 25, 19, 9, 1, 26, 16, - 5, 11, 23, 8, 12, 7, 17, 0, - 22, 3, 10, 14, 6, 20, 27, 24 -]; - -exports.pc2 = function pc2(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - var len = pc2table.length >>> 1; - for (var i = 0; i < len; i++) { - outL <<= 1; - outL |= (inL >>> pc2table[i]) & 0x1; - } - for (var i = len; i < pc2table.length; i++) { - outR <<= 1; - outR |= (inR >>> pc2table[i]) & 0x1; - } - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.expand = function expand(r, out, off) { - var outL = 0; - var outR = 0; - - outL = ((r & 1) << 5) | (r >>> 27); - for (var i = 23; i >= 15; i -= 4) { - outL <<= 6; - outL |= (r >>> i) & 0x3f; - } - for (var i = 11; i >= 3; i -= 4) { - outR |= (r >>> i) & 0x3f; - outR <<= 6; - } - outR |= ((r & 0x1f) << 1) | (r >>> 31); - - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -var sTable = [ - 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, - 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, - 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, - 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13, - - 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, - 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, - 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, - 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9, - - 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, - 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, - 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, - 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12, - - 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, - 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, - 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, - 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14, - - 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, - 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, - 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, - 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3, - - 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, - 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, - 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, - 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13, - - 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, - 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, - 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, - 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12, - - 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, - 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, - 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, - 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 -]; - -exports.substitute = function substitute(inL, inR) { - var out = 0; - for (var i = 0; i < 4; i++) { - var b = (inL >>> (18 - i * 6)) & 0x3f; - var sb = sTable[i * 0x40 + b]; - - out <<= 4; - out |= sb; - } - for (var i = 0; i < 4; i++) { - var b = (inR >>> (18 - i * 6)) & 0x3f; - var sb = sTable[4 * 0x40 + i * 0x40 + b]; - - out <<= 4; - out |= sb; - } - return out >>> 0; -}; - -var permuteTable = [ - 16, 25, 12, 11, 3, 20, 4, 15, 31, 17, 9, 6, 27, 14, 1, 22, - 30, 24, 8, 18, 0, 5, 29, 23, 13, 19, 2, 26, 10, 21, 28, 7 -]; - -exports.permute = function permute(num) { - var out = 0; - for (var i = 0; i < permuteTable.length; i++) { - out <<= 1; - out |= (num >>> permuteTable[i]) & 0x1; - } - return out >>> 0; -}; - -exports.padSplit = function padSplit(num, size, group) { - var str = num.toString(2); - while (str.length < size) - str = '0' + str; - - var out = []; - for (var i = 0; i < size; i += group) - out.push(str.slice(i, i + group)); - return out.join(' '); -}; - -},{}],85:[function(require,module,exports){ -(function (Buffer){(function (){ -var generatePrime = require('./lib/generatePrime') -var primes = require('./lib/primes.json') - -var DH = require('./lib/dh') - -function getDiffieHellman (mod) { - var prime = new Buffer(primes[mod].prime, 'hex') - var gen = new Buffer(primes[mod].gen, 'hex') - - return new DH(prime, gen) -} - -var ENCODINGS = { - 'binary': true, 'hex': true, 'base64': true -} - -function createDiffieHellman (prime, enc, generator, genc) { - if (Buffer.isBuffer(enc) || ENCODINGS[enc] === undefined) { - return createDiffieHellman(prime, 'binary', enc, generator) - } - - enc = enc || 'binary' - genc = genc || 'binary' - generator = generator || new Buffer([2]) - - if (!Buffer.isBuffer(generator)) { - generator = new Buffer(generator, genc) - } - - if (typeof prime === 'number') { - return new DH(generatePrime(prime, generator), generator, true) - } - - if (!Buffer.isBuffer(prime)) { - prime = new Buffer(prime, enc) - } - - return new DH(prime, generator, true) -} - -exports.DiffieHellmanGroup = exports.createDiffieHellmanGroup = exports.getDiffieHellman = getDiffieHellman -exports.createDiffieHellman = exports.DiffieHellman = createDiffieHellman - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"./lib/dh":86,"./lib/generatePrime":87,"./lib/primes.json":88,"buffer":68}],86:[function(require,module,exports){ -(function (Buffer){(function (){ -var BN = require('bn.js'); -var MillerRabin = require('miller-rabin'); -var millerRabin = new MillerRabin(); -var TWENTYFOUR = new BN(24); -var ELEVEN = new BN(11); -var TEN = new BN(10); -var THREE = new BN(3); -var SEVEN = new BN(7); -var primes = require('./generatePrime'); -var randomBytes = require('randombytes'); -module.exports = DH; - -function setPublicKey(pub, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(pub)) { - pub = new Buffer(pub, enc); - } - this._pub = new BN(pub); - return this; -} - -function setPrivateKey(priv, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(priv)) { - priv = new Buffer(priv, enc); - } - this._priv = new BN(priv); - return this; -} - -var primeCache = {}; -function checkPrime(prime, generator) { - var gen = generator.toString('hex'); - var hex = [gen, prime.toString(16)].join('_'); - if (hex in primeCache) { - return primeCache[hex]; - } - var error = 0; - - if (prime.isEven() || - !primes.simpleSieve || - !primes.fermatTest(prime) || - !millerRabin.test(prime)) { - //not a prime so +1 - error += 1; - - if (gen === '02' || gen === '05') { - // we'd be able to check the generator - // it would fail so +8 - error += 8; - } else { - //we wouldn't be able to test the generator - // so +4 - error += 4; - } - primeCache[hex] = error; - return error; - } - if (!millerRabin.test(prime.shrn(1))) { - //not a safe prime - error += 2; - } - var rem; - switch (gen) { - case '02': - if (prime.mod(TWENTYFOUR).cmp(ELEVEN)) { - // unsuidable generator - error += 8; - } - break; - case '05': - rem = prime.mod(TEN); - if (rem.cmp(THREE) && rem.cmp(SEVEN)) { - // prime mod 10 needs to equal 3 or 7 - error += 8; - } - break; - default: - error += 4; - } - primeCache[hex] = error; - return error; -} - -function DH(prime, generator, malleable) { - this.setGenerator(generator); - this.__prime = new BN(prime); - this._prime = BN.mont(this.__prime); - this._primeLen = prime.length; - this._pub = undefined; - this._priv = undefined; - this._primeCode = undefined; - if (malleable) { - this.setPublicKey = setPublicKey; - this.setPrivateKey = setPrivateKey; - } else { - this._primeCode = 8; - } -} -Object.defineProperty(DH.prototype, 'verifyError', { - enumerable: true, - get: function () { - if (typeof this._primeCode !== 'number') { - this._primeCode = checkPrime(this.__prime, this.__gen); - } - return this._primeCode; - } -}); -DH.prototype.generateKeys = function () { - if (!this._priv) { - this._priv = new BN(randomBytes(this._primeLen)); - } - this._pub = this._gen.toRed(this._prime).redPow(this._priv).fromRed(); - return this.getPublicKey(); -}; - -DH.prototype.computeSecret = function (other) { - other = new BN(other); - other = other.toRed(this._prime); - var secret = other.redPow(this._priv).fromRed(); - var out = new Buffer(secret.toArray()); - var prime = this.getPrime(); - if (out.length < prime.length) { - var front = new Buffer(prime.length - out.length); - front.fill(0); - out = Buffer.concat([front, out]); - } - return out; -}; - -DH.prototype.getPublicKey = function getPublicKey(enc) { - return formatReturnValue(this._pub, enc); -}; - -DH.prototype.getPrivateKey = function getPrivateKey(enc) { - return formatReturnValue(this._priv, enc); -}; - -DH.prototype.getPrime = function (enc) { - return formatReturnValue(this.__prime, enc); -}; - -DH.prototype.getGenerator = function (enc) { - return formatReturnValue(this._gen, enc); -}; - -DH.prototype.setGenerator = function (gen, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(gen)) { - gen = new Buffer(gen, enc); - } - this.__gen = gen; - this._gen = new BN(gen); - return this; -}; - -function formatReturnValue(bn, enc) { - var buf = new Buffer(bn.toArray()); - if (!enc) { - return buf; - } else { - return buf.toString(enc); - } -} - -}).call(this)}).call(this,require("buffer").Buffer) - -},{"./generatePrime":87,"bn.js":18,"buffer":68,"miller-rabin":222,"randombytes":244}],87:[function(require,module,exports){ -var randomBytes = require('randombytes'); -module.exports = findPrime; -findPrime.simpleSieve = simpleSieve; -findPrime.fermatTest = fermatTest; -var BN = require('bn.js'); -var TWENTYFOUR = new BN(24); -var MillerRabin = require('miller-rabin'); -var millerRabin = new MillerRabin(); -var ONE = new BN(1); -var TWO = new BN(2); -var FIVE = new BN(5); -var SIXTEEN = new BN(16); -var EIGHT = new BN(8); -var TEN = new BN(10); -var THREE = new BN(3); -var SEVEN = new BN(7); -var ELEVEN = new BN(11); -var FOUR = new BN(4); -var TWELVE = new BN(12); -var primes = null; - -function _getPrimes() { - if (primes !== null) - return primes; - - var limit = 0x100000; - var res = []; - res[0] = 2; - for (var i = 1, k = 3; k < limit; k += 2) { - var sqrt = Math.ceil(Math.sqrt(k)); - for (var j = 0; j < i && res[j] <= sqrt; j++) - if (k % res[j] === 0) - break; - - if (i !== j && res[j] <= sqrt) - continue; - - res[i++] = k; - } - primes = res; - return res; -} - -function simpleSieve(p) { - var primes = _getPrimes(); - - for (var i = 0; i < primes.length; i++) - if (p.modn(primes[i]) === 0) { - if (p.cmpn(primes[i]) === 0) { - return true; - } else { - return false; - } - } - - return true; -} - -function fermatTest(p) { - var red = BN.mont(p); - return TWO.toRed(red).redPow(p.subn(1)).fromRed().cmpn(1) === 0; -} - -function findPrime(bits, gen) { - if (bits < 16) { - // this is what openssl does - if (gen === 2 || gen === 5) { - return new BN([0x8c, 0x7b]); - } else { - return new BN([0x8c, 0x27]); - } - } - gen = new BN(gen); - - var num, n2; - - while (true) { - num = new BN(randomBytes(Math.ceil(bits / 8))); - while (num.bitLength() > bits) { - num.ishrn(1); - } - if (num.isEven()) { - num.iadd(ONE); - } - if (!num.testn(1)) { - num.iadd(TWO); - } - if (!gen.cmp(TWO)) { - while (num.mod(TWENTYFOUR).cmp(ELEVEN)) { - num.iadd(FOUR); - } - } else if (!gen.cmp(FIVE)) { - while (num.mod(TEN).cmp(THREE)) { - num.iadd(FOUR); - } - } - n2 = num.shrn(1); - if (simpleSieve(n2) && simpleSieve(num) && - fermatTest(n2) && fermatTest(num) && - millerRabin.test(n2) && millerRabin.test(num)) { - return num; - } - } - -} - -},{"bn.js":18,"miller-rabin":222,"randombytes":244}],88:[function(require,module,exports){ -module.exports={ - "modp1": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff" - }, - "modp2": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff" - }, - "modp5": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff" - }, - "modp14": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff" - }, - "modp15": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff" - }, - "modp16": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff" - }, - "modp17": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff" - }, - "modp18": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff" - } -} -},{}],89:[function(require,module,exports){ -'use strict'; - -var elliptic = exports; - -elliptic.version = require('../package.json').version; -elliptic.utils = require('./elliptic/utils'); -elliptic.rand = require('brorand'); -elliptic.curve = require('./elliptic/curve'); -elliptic.curves = require('./elliptic/curves'); - -// Protocols -elliptic.ec = require('./elliptic/ec'); -elliptic.eddsa = require('./elliptic/eddsa'); - -},{"../package.json":104,"./elliptic/curve":92,"./elliptic/curves":95,"./elliptic/ec":96,"./elliptic/eddsa":99,"./elliptic/utils":103,"brorand":19}],90:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var utils = require('../utils'); -var getNAF = utils.getNAF; -var getJSF = utils.getJSF; -var assert = utils.assert; - -function BaseCurve(type, conf) { - this.type = type; - this.p = new BN(conf.p, 16); - - // Use Montgomery, when there is no fast reduction for the prime - this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p); - - // Useful for many curves - this.zero = new BN(0).toRed(this.red); - this.one = new BN(1).toRed(this.red); - this.two = new BN(2).toRed(this.red); - - // Curve configuration, optional - this.n = conf.n && new BN(conf.n, 16); - this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed); - - // Temporary arrays - this._wnafT1 = new Array(4); - this._wnafT2 = new Array(4); - this._wnafT3 = new Array(4); - this._wnafT4 = new Array(4); - - this._bitLength = this.n ? this.n.bitLength() : 0; - - // Generalized Greg Maxwell's trick - var adjustCount = this.n && this.p.div(this.n); - if (!adjustCount || adjustCount.cmpn(100) > 0) { - this.redN = null; - } else { - this._maxwellTrick = true; - this.redN = this.n.toRed(this.red); - } -} -module.exports = BaseCurve; - -BaseCurve.prototype.point = function point() { - throw new Error('Not implemented'); -}; - -BaseCurve.prototype.validate = function validate() { - throw new Error('Not implemented'); -}; - -BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) { - assert(p.precomputed); - var doubles = p._getDoubles(); - - var naf = getNAF(k, 1, this._bitLength); - var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1); - I /= 3; - - // Translate into more windowed form - var repr = []; - var j; - var nafW; - for (j = 0; j < naf.length; j += doubles.step) { - nafW = 0; - for (var l = j + doubles.step - 1; l >= j; l--) - nafW = (nafW << 1) + naf[l]; - repr.push(nafW); - } - - var a = this.jpoint(null, null, null); - var b = this.jpoint(null, null, null); - for (var i = I; i > 0; i--) { - for (j = 0; j < repr.length; j++) { - nafW = repr[j]; - if (nafW === i) - b = b.mixedAdd(doubles.points[j]); - else if (nafW === -i) - b = b.mixedAdd(doubles.points[j].neg()); - } - a = a.add(b); - } - return a.toP(); -}; - -BaseCurve.prototype._wnafMul = function _wnafMul(p, k) { - var w = 4; - - // Precompute window - var nafPoints = p._getNAFPoints(w); - w = nafPoints.wnd; - var wnd = nafPoints.points; - - // Get NAF form - var naf = getNAF(k, w, this._bitLength); - - // Add `this`*(N+1) for every w-NAF index - var acc = this.jpoint(null, null, null); - for (var i = naf.length - 1; i >= 0; i--) { - // Count zeroes - for (var l = 0; i >= 0 && naf[i] === 0; i--) - l++; - if (i >= 0) - l++; - acc = acc.dblp(l); - - if (i < 0) - break; - var z = naf[i]; - assert(z !== 0); - if (p.type === 'affine') { - // J +- P - if (z > 0) - acc = acc.mixedAdd(wnd[(z - 1) >> 1]); - else - acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg()); - } else { - // J +- J - if (z > 0) - acc = acc.add(wnd[(z - 1) >> 1]); - else - acc = acc.add(wnd[(-z - 1) >> 1].neg()); - } - } - return p.type === 'affine' ? acc.toP() : acc; -}; - -BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW, - points, - coeffs, - len, - jacobianResult) { - var wndWidth = this._wnafT1; - var wnd = this._wnafT2; - var naf = this._wnafT3; - - // Fill all arrays - var max = 0; - var i; - var j; - var p; - for (i = 0; i < len; i++) { - p = points[i]; - var nafPoints = p._getNAFPoints(defW); - wndWidth[i] = nafPoints.wnd; - wnd[i] = nafPoints.points; - } - - // Comb small window NAFs - for (i = len - 1; i >= 1; i -= 2) { - var a = i - 1; - var b = i; - if (wndWidth[a] !== 1 || wndWidth[b] !== 1) { - naf[a] = getNAF(coeffs[a], wndWidth[a], this._bitLength); - naf[b] = getNAF(coeffs[b], wndWidth[b], this._bitLength); - max = Math.max(naf[a].length, max); - max = Math.max(naf[b].length, max); - continue; - } - - var comb = [ - points[a], /* 1 */ - null, /* 3 */ - null, /* 5 */ - points[b], /* 7 */ - ]; - - // Try to avoid Projective points, if possible - if (points[a].y.cmp(points[b].y) === 0) { - comb[1] = points[a].add(points[b]); - comb[2] = points[a].toJ().mixedAdd(points[b].neg()); - } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) { - comb[1] = points[a].toJ().mixedAdd(points[b]); - comb[2] = points[a].add(points[b].neg()); - } else { - comb[1] = points[a].toJ().mixedAdd(points[b]); - comb[2] = points[a].toJ().mixedAdd(points[b].neg()); - } - - var index = [ - -3, /* -1 -1 */ - -1, /* -1 0 */ - -5, /* -1 1 */ - -7, /* 0 -1 */ - 0, /* 0 0 */ - 7, /* 0 1 */ - 5, /* 1 -1 */ - 1, /* 1 0 */ - 3, /* 1 1 */ - ]; - - var jsf = getJSF(coeffs[a], coeffs[b]); - max = Math.max(jsf[0].length, max); - naf[a] = new Array(max); - naf[b] = new Array(max); - for (j = 0; j < max; j++) { - var ja = jsf[0][j] | 0; - var jb = jsf[1][j] | 0; - - naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; - naf[b][j] = 0; - wnd[a] = comb; - } - } - - var acc = this.jpoint(null, null, null); - var tmp = this._wnafT4; - for (i = max; i >= 0; i--) { - var k = 0; - - while (i >= 0) { - var zero = true; - for (j = 0; j < len; j++) { - tmp[j] = naf[j][i] | 0; - if (tmp[j] !== 0) - zero = false; - } - if (!zero) - break; - k++; - i--; - } - if (i >= 0) - k++; - acc = acc.dblp(k); - if (i < 0) - break; - - for (j = 0; j < len; j++) { - var z = tmp[j]; - p; - if (z === 0) - continue; - else if (z > 0) - p = wnd[j][(z - 1) >> 1]; - else if (z < 0) - p = wnd[j][(-z - 1) >> 1].neg(); - - if (p.type === 'affine') - acc = acc.mixedAdd(p); - else - acc = acc.add(p); - } - } - // Zeroify references - for (i = 0; i < len; i++) - wnd[i] = null; - - if (jacobianResult) - return acc; - else - return acc.toP(); -}; - -function BasePoint(curve, type) { - this.curve = curve; - this.type = type; - this.precomputed = null; -} -BaseCurve.BasePoint = BasePoint; - -BasePoint.prototype.eq = function eq(/*other*/) { - throw new Error('Not implemented'); -}; - -BasePoint.prototype.validate = function validate() { - return this.curve.validate(this); -}; - -BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) { - bytes = utils.toArray(bytes, enc); - - var len = this.p.byteLength(); - - // uncompressed, hybrid-odd, hybrid-even - if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) && - bytes.length - 1 === 2 * len) { - if (bytes[0] === 0x06) - assert(bytes[bytes.length - 1] % 2 === 0); - else if (bytes[0] === 0x07) - assert(bytes[bytes.length - 1] % 2 === 1); - - var res = this.point(bytes.slice(1, 1 + len), - bytes.slice(1 + len, 1 + 2 * len)); - - return res; - } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) && - bytes.length - 1 === len) { - return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03); - } - throw new Error('Unknown point format'); -}; - -BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) { - return this.encode(enc, true); -}; - -BasePoint.prototype._encode = function _encode(compact) { - var len = this.curve.p.byteLength(); - var x = this.getX().toArray('be', len); - - if (compact) - return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); - - return [ 0x04 ].concat(x, this.getY().toArray('be', len)); -}; - -BasePoint.prototype.encode = function encode(enc, compact) { - return utils.encode(this._encode(compact), enc); -}; - -BasePoint.prototype.precompute = function precompute(power) { - if (this.precomputed) - return this; - - var precomputed = { - doubles: null, - naf: null, - beta: null, - }; - precomputed.naf = this._getNAFPoints(8); - precomputed.doubles = this._getDoubles(4, power); - precomputed.beta = this._getBeta(); - this.precomputed = precomputed; - - return this; -}; - -BasePoint.prototype._hasDoubles = function _hasDoubles(k) { - if (!this.precomputed) - return false; - - var doubles = this.precomputed.doubles; - if (!doubles) - return false; - - return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step); -}; - -BasePoint.prototype._getDoubles = function _getDoubles(step, power) { - if (this.precomputed && this.precomputed.doubles) - return this.precomputed.doubles; - - var doubles = [ this ]; - var acc = this; - for (var i = 0; i < power; i += step) { - for (var j = 0; j < step; j++) - acc = acc.dbl(); - doubles.push(acc); - } - return { - step: step, - points: doubles, - }; -}; - -BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) { - if (this.precomputed && this.precomputed.naf) - return this.precomputed.naf; - - var res = [ this ]; - var max = (1 << wnd) - 1; - var dbl = max === 1 ? null : this.dbl(); - for (var i = 1; i < max; i++) - res[i] = res[i - 1].add(dbl); - return { - wnd: wnd, - points: res, - }; -}; - -BasePoint.prototype._getBeta = function _getBeta() { - return null; -}; - -BasePoint.prototype.dblp = function dblp(k) { - var r = this; - for (var i = 0; i < k; i++) - r = r.dbl(); - return r; -}; - -},{"../utils":103,"bn.js":18}],91:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var BN = require('bn.js'); -var inherits = require('inherits'); -var Base = require('./base'); - -var assert = utils.assert; - -function EdwardsCurve(conf) { - // NOTE: Important as we are creating point in Base.call() - this.twisted = (conf.a | 0) !== 1; - this.mOneA = this.twisted && (conf.a | 0) === -1; - this.extended = this.mOneA; - - Base.call(this, 'edwards', conf); - - this.a = new BN(conf.a, 16).umod(this.red.m); - this.a = this.a.toRed(this.red); - this.c = new BN(conf.c, 16).toRed(this.red); - this.c2 = this.c.redSqr(); - this.d = new BN(conf.d, 16).toRed(this.red); - this.dd = this.d.redAdd(this.d); - - assert(!this.twisted || this.c.fromRed().cmpn(1) === 0); - this.oneC = (conf.c | 0) === 1; -} -inherits(EdwardsCurve, Base); -module.exports = EdwardsCurve; - -EdwardsCurve.prototype._mulA = function _mulA(num) { - if (this.mOneA) - return num.redNeg(); - else - return this.a.redMul(num); -}; - -EdwardsCurve.prototype._mulC = function _mulC(num) { - if (this.oneC) - return num; - else - return this.c.redMul(num); -}; - -// Just for compatibility with Short curve -EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) { - return this.point(x, y, z, t); -}; - -EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) { - x = new BN(x, 16); - if (!x.red) - x = x.toRed(this.red); - - var x2 = x.redSqr(); - var rhs = this.c2.redSub(this.a.redMul(x2)); - var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); - - var y2 = rhs.redMul(lhs.redInvm()); - var y = y2.redSqrt(); - if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) - throw new Error('invalid point'); - - var isOdd = y.fromRed().isOdd(); - if (odd && !isOdd || !odd && isOdd) - y = y.redNeg(); - - return this.point(x, y); -}; - -EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) { - y = new BN(y, 16); - if (!y.red) - y = y.toRed(this.red); - - // x^2 = (y^2 - c^2) / (c^2 d y^2 - a) - var y2 = y.redSqr(); - var lhs = y2.redSub(this.c2); - var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a); - var x2 = lhs.redMul(rhs.redInvm()); - - if (x2.cmp(this.zero) === 0) { - if (odd) - throw new Error('invalid point'); - else - return this.point(this.zero, y); - } - - var x = x2.redSqrt(); - if (x.redSqr().redSub(x2).cmp(this.zero) !== 0) - throw new Error('invalid point'); - - if (x.fromRed().isOdd() !== odd) - x = x.redNeg(); - - return this.point(x, y); -}; - -EdwardsCurve.prototype.validate = function validate(point) { - if (point.isInfinity()) - return true; - - // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2) - point.normalize(); - - var x2 = point.x.redSqr(); - var y2 = point.y.redSqr(); - var lhs = x2.redMul(this.a).redAdd(y2); - var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); - - return lhs.cmp(rhs) === 0; -}; - -function Point(curve, x, y, z, t) { - Base.BasePoint.call(this, curve, 'projective'); - if (x === null && y === null && z === null) { - this.x = this.curve.zero; - this.y = this.curve.one; - this.z = this.curve.one; - this.t = this.curve.zero; - this.zOne = true; - } else { - this.x = new BN(x, 16); - this.y = new BN(y, 16); - this.z = z ? new BN(z, 16) : this.curve.one; - this.t = t && new BN(t, 16); - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.y.red) - this.y = this.y.toRed(this.curve.red); - if (!this.z.red) - this.z = this.z.toRed(this.curve.red); - if (this.t && !this.t.red) - this.t = this.t.toRed(this.curve.red); - this.zOne = this.z === this.curve.one; - - // Use extended coordinates - if (this.curve.extended && !this.t) { - this.t = this.x.redMul(this.y); - if (!this.zOne) - this.t = this.t.redMul(this.z.redInvm()); - } - } -} -inherits(Point, Base.BasePoint); - -EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) { - return Point.fromJSON(this, obj); -}; - -EdwardsCurve.prototype.point = function point(x, y, z, t) { - return new Point(this, x, y, z, t); -}; - -Point.fromJSON = function fromJSON(curve, obj) { - return new Point(curve, obj[0], obj[1], obj[2]); -}; - -Point.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -Point.prototype.isInfinity = function isInfinity() { - // XXX This code assumes that zero is always zero in red - return this.x.cmpn(0) === 0 && - (this.y.cmp(this.z) === 0 || - (this.zOne && this.y.cmp(this.curve.c) === 0)); -}; - -Point.prototype._extDbl = function _extDbl() { - // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - // #doubling-dbl-2008-hwcd - // 4M + 4S - - // A = X1^2 - var a = this.x.redSqr(); - // B = Y1^2 - var b = this.y.redSqr(); - // C = 2 * Z1^2 - var c = this.z.redSqr(); - c = c.redIAdd(c); - // D = a * A - var d = this.curve._mulA(a); - // E = (X1 + Y1)^2 - A - B - var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b); - // G = D + B - var g = d.redAdd(b); - // F = G - C - var f = g.redSub(c); - // H = D - B - var h = d.redSub(b); - // X3 = E * F - var nx = e.redMul(f); - // Y3 = G * H - var ny = g.redMul(h); - // T3 = E * H - var nt = e.redMul(h); - // Z3 = F * G - var nz = f.redMul(g); - return this.curve.point(nx, ny, nz, nt); -}; - -Point.prototype._projDbl = function _projDbl() { - // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html - // #doubling-dbl-2008-bbjlp - // #doubling-dbl-2007-bl - // and others - // Generally 3M + 4S or 2M + 4S - - // B = (X1 + Y1)^2 - var b = this.x.redAdd(this.y).redSqr(); - // C = X1^2 - var c = this.x.redSqr(); - // D = Y1^2 - var d = this.y.redSqr(); - - var nx; - var ny; - var nz; - var e; - var h; - var j; - if (this.curve.twisted) { - // E = a * C - e = this.curve._mulA(c); - // F = E + D - var f = e.redAdd(d); - if (this.zOne) { - // X3 = (B - C - D) * (F - 2) - nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); - // Y3 = F * (E - D) - ny = f.redMul(e.redSub(d)); - // Z3 = F^2 - 2 * F - nz = f.redSqr().redSub(f).redSub(f); - } else { - // H = Z1^2 - h = this.z.redSqr(); - // J = F - 2 * H - j = f.redSub(h).redISub(h); - // X3 = (B-C-D)*J - nx = b.redSub(c).redISub(d).redMul(j); - // Y3 = F * (E - D) - ny = f.redMul(e.redSub(d)); - // Z3 = F * J - nz = f.redMul(j); - } - } else { - // E = C + D - e = c.redAdd(d); - // H = (c * Z1)^2 - h = this.curve._mulC(this.z).redSqr(); - // J = E - 2 * H - j = e.redSub(h).redSub(h); - // X3 = c * (B - E) * J - nx = this.curve._mulC(b.redISub(e)).redMul(j); - // Y3 = c * E * (C - D) - ny = this.curve._mulC(e).redMul(c.redISub(d)); - // Z3 = E * J - nz = e.redMul(j); - } - return this.curve.point(nx, ny, nz); -}; - -Point.prototype.dbl = function dbl() { - if (this.isInfinity()) - return this; - - // Double in extended coordinates - if (this.curve.extended) - return this._extDbl(); - else - return this._projDbl(); -}; - -Point.prototype._extAdd = function _extAdd(p) { - // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - // #addition-add-2008-hwcd-3 - // 8M - - // A = (Y1 - X1) * (Y2 - X2) - var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); - // B = (Y1 + X1) * (Y2 + X2) - var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); - // C = T1 * k * T2 - var c = this.t.redMul(this.curve.dd).redMul(p.t); - // D = Z1 * 2 * Z2 - var d = this.z.redMul(p.z.redAdd(p.z)); - // E = B - A - var e = b.redSub(a); - // F = D - C - var f = d.redSub(c); - // G = D + C - var g = d.redAdd(c); - // H = B + A - var h = b.redAdd(a); - // X3 = E * F - var nx = e.redMul(f); - // Y3 = G * H - var ny = g.redMul(h); - // T3 = E * H - var nt = e.redMul(h); - // Z3 = F * G - var nz = f.redMul(g); - return this.curve.point(nx, ny, nz, nt); -}; - -Point.prototype._projAdd = function _projAdd(p) { - // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html - // #addition-add-2008-bbjlp - // #addition-add-2007-bl - // 10M + 1S - - // A = Z1 * Z2 - var a = this.z.redMul(p.z); - // B = A^2 - var b = a.redSqr(); - // C = X1 * X2 - var c = this.x.redMul(p.x); - // D = Y1 * Y2 - var d = this.y.redMul(p.y); - // E = d * C * D - var e = this.curve.d.redMul(c).redMul(d); - // F = B - E - var f = b.redSub(e); - // G = B + E - var g = b.redAdd(e); - // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) - var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d); - var nx = a.redMul(f).redMul(tmp); - var ny; - var nz; - if (this.curve.twisted) { - // Y3 = A * G * (D - a * C) - ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); - // Z3 = F * G - nz = f.redMul(g); - } else { - // Y3 = A * G * (D - C) - ny = a.redMul(g).redMul(d.redSub(c)); - // Z3 = c * F * G - nz = this.curve._mulC(f).redMul(g); - } - return this.curve.point(nx, ny, nz); -}; - -Point.prototype.add = function add(p) { - if (this.isInfinity()) - return p; - if (p.isInfinity()) - return this; - - if (this.curve.extended) - return this._extAdd(p); - else - return this._projAdd(p); -}; - -Point.prototype.mul = function mul(k) { - if (this._hasDoubles(k)) - return this.curve._fixedNafMul(this, k); - else - return this.curve._wnafMul(this, k); -}; - -Point.prototype.mulAdd = function mulAdd(k1, p, k2) { - return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false); -}; - -Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) { - return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true); -}; - -Point.prototype.normalize = function normalize() { - if (this.zOne) - return this; - - // Normalize coordinates - var zi = this.z.redInvm(); - this.x = this.x.redMul(zi); - this.y = this.y.redMul(zi); - if (this.t) - this.t = this.t.redMul(zi); - this.z = this.curve.one; - this.zOne = true; - return this; -}; - -Point.prototype.neg = function neg() { - return this.curve.point(this.x.redNeg(), - this.y, - this.z, - this.t && this.t.redNeg()); -}; - -Point.prototype.getX = function getX() { - this.normalize(); - return this.x.fromRed(); -}; - -Point.prototype.getY = function getY() { - this.normalize(); - return this.y.fromRed(); -}; - -Point.prototype.eq = function eq(other) { - return this === other || - this.getX().cmp(other.getX()) === 0 && - this.getY().cmp(other.getY()) === 0; -}; - -Point.prototype.eqXToP = function eqXToP(x) { - var rx = x.toRed(this.curve.red).redMul(this.z); - if (this.x.cmp(rx) === 0) - return true; - - var xc = x.clone(); - var t = this.curve.redN.redMul(this.z); - for (;;) { - xc.iadd(this.curve.n); - if (xc.cmp(this.curve.p) >= 0) - return false; - - rx.redIAdd(t); - if (this.x.cmp(rx) === 0) - return true; - } -}; - -// Compatibility with BaseCurve -Point.prototype.toP = Point.prototype.normalize; -Point.prototype.mixedAdd = Point.prototype.add; - -},{"../utils":103,"./base":90,"bn.js":18,"inherits":146}],92:[function(require,module,exports){ -'use strict'; - -var curve = exports; - -curve.base = require('./base'); -curve.short = require('./short'); -curve.mont = require('./mont'); -curve.edwards = require('./edwards'); - -},{"./base":90,"./edwards":91,"./mont":93,"./short":94}],93:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var inherits = require('inherits'); -var Base = require('./base'); - -var utils = require('../utils'); - -function MontCurve(conf) { - Base.call(this, 'mont', conf); - - this.a = new BN(conf.a, 16).toRed(this.red); - this.b = new BN(conf.b, 16).toRed(this.red); - this.i4 = new BN(4).toRed(this.red).redInvm(); - this.two = new BN(2).toRed(this.red); - this.a24 = this.i4.redMul(this.a.redAdd(this.two)); -} -inherits(MontCurve, Base); -module.exports = MontCurve; - -MontCurve.prototype.validate = function validate(point) { - var x = point.normalize().x; - var x2 = x.redSqr(); - var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x); - var y = rhs.redSqrt(); - - return y.redSqr().cmp(rhs) === 0; -}; - -function Point(curve, x, z) { - Base.BasePoint.call(this, curve, 'projective'); - if (x === null && z === null) { - this.x = this.curve.one; - this.z = this.curve.zero; - } else { - this.x = new BN(x, 16); - this.z = new BN(z, 16); - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.z.red) - this.z = this.z.toRed(this.curve.red); - } -} -inherits(Point, Base.BasePoint); - -MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) { - return this.point(utils.toArray(bytes, enc), 1); -}; - -MontCurve.prototype.point = function point(x, z) { - return new Point(this, x, z); -}; - -MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) { - return Point.fromJSON(this, obj); -}; - -Point.prototype.precompute = function precompute() { - // No-op -}; - -Point.prototype._encode = function _encode() { - return this.getX().toArray('be', this.curve.p.byteLength()); -}; - -Point.fromJSON = function fromJSON(curve, obj) { - return new Point(curve, obj[0], obj[1] || curve.one); -}; - -Point.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -Point.prototype.isInfinity = function isInfinity() { - // XXX This code assumes that zero is always zero in red - return this.z.cmpn(0) === 0; -}; - -Point.prototype.dbl = function dbl() { - // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3 - // 2M + 2S + 4A - - // A = X1 + Z1 - var a = this.x.redAdd(this.z); - // AA = A^2 - var aa = a.redSqr(); - // B = X1 - Z1 - var b = this.x.redSub(this.z); - // BB = B^2 - var bb = b.redSqr(); - // C = AA - BB - var c = aa.redSub(bb); - // X3 = AA * BB - var nx = aa.redMul(bb); - // Z3 = C * (BB + A24 * C) - var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c))); - return this.curve.point(nx, nz); -}; - -Point.prototype.add = function add() { - throw new Error('Not supported on Montgomery curve'); -}; - -Point.prototype.diffAdd = function diffAdd(p, diff) { - // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3 - // 4M + 2S + 6A - - // A = X2 + Z2 - var a = this.x.redAdd(this.z); - // B = X2 - Z2 - var b = this.x.redSub(this.z); - // C = X3 + Z3 - var c = p.x.redAdd(p.z); - // D = X3 - Z3 - var d = p.x.redSub(p.z); - // DA = D * A - var da = d.redMul(a); - // CB = C * B - var cb = c.redMul(b); - // X5 = Z1 * (DA + CB)^2 - var nx = diff.z.redMul(da.redAdd(cb).redSqr()); - // Z5 = X1 * (DA - CB)^2 - var nz = diff.x.redMul(da.redISub(cb).redSqr()); - return this.curve.point(nx, nz); -}; - -Point.prototype.mul = function mul(k) { - var t = k.clone(); - var a = this; // (N / 2) * Q + Q - var b = this.curve.point(null, null); // (N / 2) * Q - var c = this; // Q - - for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1)) - bits.push(t.andln(1)); - - for (var i = bits.length - 1; i >= 0; i--) { - if (bits[i] === 0) { - // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q - a = a.diffAdd(b, c); - // N * Q = 2 * ((N / 2) * Q + Q)) - b = b.dbl(); - } else { - // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q) - b = a.diffAdd(b, c); - // N * Q + Q = 2 * ((N / 2) * Q + Q) - a = a.dbl(); - } - } - return b; -}; - -Point.prototype.mulAdd = function mulAdd() { - throw new Error('Not supported on Montgomery curve'); -}; - -Point.prototype.jumlAdd = function jumlAdd() { - throw new Error('Not supported on Montgomery curve'); -}; - -Point.prototype.eq = function eq(other) { - return this.getX().cmp(other.getX()) === 0; -}; - -Point.prototype.normalize = function normalize() { - this.x = this.x.redMul(this.z.redInvm()); - this.z = this.curve.one; - return this; -}; - -Point.prototype.getX = function getX() { - // Normalize coordinates - this.normalize(); - - return this.x.fromRed(); -}; - -},{"../utils":103,"./base":90,"bn.js":18,"inherits":146}],94:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var BN = require('bn.js'); -var inherits = require('inherits'); -var Base = require('./base'); - -var assert = utils.assert; - -function ShortCurve(conf) { - Base.call(this, 'short', conf); - - this.a = new BN(conf.a, 16).toRed(this.red); - this.b = new BN(conf.b, 16).toRed(this.red); - this.tinv = this.two.redInvm(); - - this.zeroA = this.a.fromRed().cmpn(0) === 0; - this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0; - - // If the curve is endomorphic, precalculate beta and lambda - this.endo = this._getEndomorphism(conf); - this._endoWnafT1 = new Array(4); - this._endoWnafT2 = new Array(4); -} -inherits(ShortCurve, Base); -module.exports = ShortCurve; - -ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) { - // No efficient endomorphism - if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1) - return; - - // Compute beta and lambda, that lambda * P = (beta * Px; Py) - var beta; - var lambda; - if (conf.beta) { - beta = new BN(conf.beta, 16).toRed(this.red); - } else { - var betas = this._getEndoRoots(this.p); - // Choose the smallest beta - beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1]; - beta = beta.toRed(this.red); - } - if (conf.lambda) { - lambda = new BN(conf.lambda, 16); - } else { - // Choose the lambda that is matching selected beta - var lambdas = this._getEndoRoots(this.n); - if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) { - lambda = lambdas[0]; - } else { - lambda = lambdas[1]; - assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0); - } - } - - // Get basis vectors, used for balanced length-two representation - var basis; - if (conf.basis) { - basis = conf.basis.map(function(vec) { - return { - a: new BN(vec.a, 16), - b: new BN(vec.b, 16), - }; - }); - } else { - basis = this._getEndoBasis(lambda); - } - - return { - beta: beta, - lambda: lambda, - basis: basis, - }; -}; - -ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) { - // Find roots of for x^2 + x + 1 in F - // Root = (-1 +- Sqrt(-3)) / 2 - // - var red = num === this.p ? this.red : BN.mont(num); - var tinv = new BN(2).toRed(red).redInvm(); - var ntinv = tinv.redNeg(); - - var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv); - - var l1 = ntinv.redAdd(s).fromRed(); - var l2 = ntinv.redSub(s).fromRed(); - return [ l1, l2 ]; -}; - -ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) { - // aprxSqrt >= sqrt(this.n) - var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2)); - - // 3.74 - // Run EGCD, until r(L + 1) < aprxSqrt - var u = lambda; - var v = this.n.clone(); - var x1 = new BN(1); - var y1 = new BN(0); - var x2 = new BN(0); - var y2 = new BN(1); - - // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n) - var a0; - var b0; - // First vector - var a1; - var b1; - // Second vector - var a2; - var b2; - - var prevR; - var i = 0; - var r; - var x; - while (u.cmpn(0) !== 0) { - var q = v.div(u); - r = v.sub(q.mul(u)); - x = x2.sub(q.mul(x1)); - var y = y2.sub(q.mul(y1)); - - if (!a1 && r.cmp(aprxSqrt) < 0) { - a0 = prevR.neg(); - b0 = x1; - a1 = r.neg(); - b1 = x; - } else if (a1 && ++i === 2) { - break; - } - prevR = r; - - v = u; - u = r; - x2 = x1; - x1 = x; - y2 = y1; - y1 = y; - } - a2 = r.neg(); - b2 = x; - - var len1 = a1.sqr().add(b1.sqr()); - var len2 = a2.sqr().add(b2.sqr()); - if (len2.cmp(len1) >= 0) { - a2 = a0; - b2 = b0; - } - - // Normalize signs - if (a1.negative) { - a1 = a1.neg(); - b1 = b1.neg(); - } - if (a2.negative) { - a2 = a2.neg(); - b2 = b2.neg(); - } - - return [ - { a: a1, b: b1 }, - { a: a2, b: b2 }, - ]; -}; - -ShortCurve.prototype._endoSplit = function _endoSplit(k) { - var basis = this.endo.basis; - var v1 = basis[0]; - var v2 = basis[1]; - - var c1 = v2.b.mul(k).divRound(this.n); - var c2 = v1.b.neg().mul(k).divRound(this.n); - - var p1 = c1.mul(v1.a); - var p2 = c2.mul(v2.a); - var q1 = c1.mul(v1.b); - var q2 = c2.mul(v2.b); - - // Calculate answer - var k1 = k.sub(p1).sub(p2); - var k2 = q1.add(q2).neg(); - return { k1: k1, k2: k2 }; -}; - -ShortCurve.prototype.pointFromX = function pointFromX(x, odd) { - x = new BN(x, 16); - if (!x.red) - x = x.toRed(this.red); - - var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b); - var y = y2.redSqrt(); - if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) - throw new Error('invalid point'); - - // XXX Is there any way to tell if the number is odd without converting it - // to non-red form? - var isOdd = y.fromRed().isOdd(); - if (odd && !isOdd || !odd && isOdd) - y = y.redNeg(); - - return this.point(x, y); -}; - -ShortCurve.prototype.validate = function validate(point) { - if (point.inf) - return true; - - var x = point.x; - var y = point.y; - - var ax = this.a.redMul(x); - var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b); - return y.redSqr().redISub(rhs).cmpn(0) === 0; -}; - -ShortCurve.prototype._endoWnafMulAdd = - function _endoWnafMulAdd(points, coeffs, jacobianResult) { - var npoints = this._endoWnafT1; - var ncoeffs = this._endoWnafT2; - for (var i = 0; i < points.length; i++) { - var split = this._endoSplit(coeffs[i]); - var p = points[i]; - var beta = p._getBeta(); - - if (split.k1.negative) { - split.k1.ineg(); - p = p.neg(true); - } - if (split.k2.negative) { - split.k2.ineg(); - beta = beta.neg(true); - } - - npoints[i * 2] = p; - npoints[i * 2 + 1] = beta; - ncoeffs[i * 2] = split.k1; - ncoeffs[i * 2 + 1] = split.k2; - } - var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult); - - // Clean-up references to points and coefficients - for (var j = 0; j < i * 2; j++) { - npoints[j] = null; - ncoeffs[j] = null; - } - return res; - }; - -function Point(curve, x, y, isRed) { - Base.BasePoint.call(this, curve, 'affine'); - if (x === null && y === null) { - this.x = null; - this.y = null; - this.inf = true; - } else { - this.x = new BN(x, 16); - this.y = new BN(y, 16); - // Force redgomery representation when loading from JSON - if (isRed) { - this.x.forceRed(this.curve.red); - this.y.forceRed(this.curve.red); - } - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.y.red) - this.y = this.y.toRed(this.curve.red); - this.inf = false; - } -} -inherits(Point, Base.BasePoint); - -ShortCurve.prototype.point = function point(x, y, isRed) { - return new Point(this, x, y, isRed); -}; - -ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) { - return Point.fromJSON(this, obj, red); -}; - -Point.prototype._getBeta = function _getBeta() { - if (!this.curve.endo) - return; - - var pre = this.precomputed; - if (pre && pre.beta) - return pre.beta; - - var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y); - if (pre) { - var curve = this.curve; - var endoMul = function(p) { - return curve.point(p.x.redMul(curve.endo.beta), p.y); - }; - pre.beta = beta; - beta.precomputed = { - beta: null, - naf: pre.naf && { - wnd: pre.naf.wnd, - points: pre.naf.points.map(endoMul), - }, - doubles: pre.doubles && { - step: pre.doubles.step, - points: pre.doubles.points.map(endoMul), - }, - }; - } - return beta; -}; - -Point.prototype.toJSON = function toJSON() { - if (!this.precomputed) - return [ this.x, this.y ]; - - return [ this.x, this.y, this.precomputed && { - doubles: this.precomputed.doubles && { - step: this.precomputed.doubles.step, - points: this.precomputed.doubles.points.slice(1), - }, - naf: this.precomputed.naf && { - wnd: this.precomputed.naf.wnd, - points: this.precomputed.naf.points.slice(1), - }, - } ]; -}; - -Point.fromJSON = function fromJSON(curve, obj, red) { - if (typeof obj === 'string') - obj = JSON.parse(obj); - var res = curve.point(obj[0], obj[1], red); - if (!obj[2]) - return res; - - function obj2point(obj) { - return curve.point(obj[0], obj[1], red); - } - - var pre = obj[2]; - res.precomputed = { - beta: null, - doubles: pre.doubles && { - step: pre.doubles.step, - points: [ res ].concat(pre.doubles.points.map(obj2point)), - }, - naf: pre.naf && { - wnd: pre.naf.wnd, - points: [ res ].concat(pre.naf.points.map(obj2point)), - }, - }; - return res; -}; - -Point.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -Point.prototype.isInfinity = function isInfinity() { - return this.inf; -}; - -Point.prototype.add = function add(p) { - // O + P = P - if (this.inf) - return p; - - // P + O = P - if (p.inf) - return this; - - // P + P = 2P - if (this.eq(p)) - return this.dbl(); - - // P + (-P) = O - if (this.neg().eq(p)) - return this.curve.point(null, null); - - // P + Q = O - if (this.x.cmp(p.x) === 0) - return this.curve.point(null, null); - - var c = this.y.redSub(p.y); - if (c.cmpn(0) !== 0) - c = c.redMul(this.x.redSub(p.x).redInvm()); - var nx = c.redSqr().redISub(this.x).redISub(p.x); - var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); - return this.curve.point(nx, ny); -}; - -Point.prototype.dbl = function dbl() { - if (this.inf) - return this; - - // 2P = O - var ys1 = this.y.redAdd(this.y); - if (ys1.cmpn(0) === 0) - return this.curve.point(null, null); - - var a = this.curve.a; - - var x2 = this.x.redSqr(); - var dyinv = ys1.redInvm(); - var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv); - - var nx = c.redSqr().redISub(this.x.redAdd(this.x)); - var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); - return this.curve.point(nx, ny); -}; - -Point.prototype.getX = function getX() { - return this.x.fromRed(); -}; - -Point.prototype.getY = function getY() { - return this.y.fromRed(); -}; - -Point.prototype.mul = function mul(k) { - k = new BN(k, 16); - if (this.isInfinity()) - return this; - else if (this._hasDoubles(k)) - return this.curve._fixedNafMul(this, k); - else if (this.curve.endo) - return this.curve._endoWnafMulAdd([ this ], [ k ]); - else - return this.curve._wnafMul(this, k); -}; - -Point.prototype.mulAdd = function mulAdd(k1, p2, k2) { - var points = [ this, p2 ]; - var coeffs = [ k1, k2 ]; - if (this.curve.endo) - return this.curve._endoWnafMulAdd(points, coeffs); - else - return this.curve._wnafMulAdd(1, points, coeffs, 2); -}; - -Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) { - var points = [ this, p2 ]; - var coeffs = [ k1, k2 ]; - if (this.curve.endo) - return this.curve._endoWnafMulAdd(points, coeffs, true); - else - return this.curve._wnafMulAdd(1, points, coeffs, 2, true); -}; - -Point.prototype.eq = function eq(p) { - return this === p || - this.inf === p.inf && - (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0); -}; - -Point.prototype.neg = function neg(_precompute) { - if (this.inf) - return this; - - var res = this.curve.point(this.x, this.y.redNeg()); - if (_precompute && this.precomputed) { - var pre = this.precomputed; - var negate = function(p) { - return p.neg(); - }; - res.precomputed = { - naf: pre.naf && { - wnd: pre.naf.wnd, - points: pre.naf.points.map(negate), - }, - doubles: pre.doubles && { - step: pre.doubles.step, - points: pre.doubles.points.map(negate), - }, - }; - } - return res; -}; - -Point.prototype.toJ = function toJ() { - if (this.inf) - return this.curve.jpoint(null, null, null); - - var res = this.curve.jpoint(this.x, this.y, this.curve.one); - return res; -}; - -function JPoint(curve, x, y, z) { - Base.BasePoint.call(this, curve, 'jacobian'); - if (x === null && y === null && z === null) { - this.x = this.curve.one; - this.y = this.curve.one; - this.z = new BN(0); - } else { - this.x = new BN(x, 16); - this.y = new BN(y, 16); - this.z = new BN(z, 16); - } - if (!this.x.red) - this.x = this.x.toRed(this.curve.red); - if (!this.y.red) - this.y = this.y.toRed(this.curve.red); - if (!this.z.red) - this.z = this.z.toRed(this.curve.red); - - this.zOne = this.z === this.curve.one; -} -inherits(JPoint, Base.BasePoint); - -ShortCurve.prototype.jpoint = function jpoint(x, y, z) { - return new JPoint(this, x, y, z); -}; - -JPoint.prototype.toP = function toP() { - if (this.isInfinity()) - return this.curve.point(null, null); - - var zinv = this.z.redInvm(); - var zinv2 = zinv.redSqr(); - var ax = this.x.redMul(zinv2); - var ay = this.y.redMul(zinv2).redMul(zinv); - - return this.curve.point(ax, ay); -}; - -JPoint.prototype.neg = function neg() { - return this.curve.jpoint(this.x, this.y.redNeg(), this.z); -}; - -JPoint.prototype.add = function add(p) { - // O + P = P - if (this.isInfinity()) - return p; - - // P + O = P - if (p.isInfinity()) - return this; - - // 12M + 4S + 7A - var pz2 = p.z.redSqr(); - var z2 = this.z.redSqr(); - var u1 = this.x.redMul(pz2); - var u2 = p.x.redMul(z2); - var s1 = this.y.redMul(pz2.redMul(p.z)); - var s2 = p.y.redMul(z2.redMul(this.z)); - - var h = u1.redSub(u2); - var r = s1.redSub(s2); - if (h.cmpn(0) === 0) { - if (r.cmpn(0) !== 0) - return this.curve.jpoint(null, null, null); - else - return this.dbl(); - } - - var h2 = h.redSqr(); - var h3 = h2.redMul(h); - var v = u1.redMul(h2); - - var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); - var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); - var nz = this.z.redMul(p.z).redMul(h); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.mixedAdd = function mixedAdd(p) { - // O + P = P - if (this.isInfinity()) - return p.toJ(); - - // P + O = P - if (p.isInfinity()) - return this; - - // 8M + 3S + 7A - var z2 = this.z.redSqr(); - var u1 = this.x; - var u2 = p.x.redMul(z2); - var s1 = this.y; - var s2 = p.y.redMul(z2).redMul(this.z); - - var h = u1.redSub(u2); - var r = s1.redSub(s2); - if (h.cmpn(0) === 0) { - if (r.cmpn(0) !== 0) - return this.curve.jpoint(null, null, null); - else - return this.dbl(); - } - - var h2 = h.redSqr(); - var h3 = h2.redMul(h); - var v = u1.redMul(h2); - - var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); - var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); - var nz = this.z.redMul(h); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.dblp = function dblp(pow) { - if (pow === 0) - return this; - if (this.isInfinity()) - return this; - if (!pow) - return this.dbl(); - - var i; - if (this.curve.zeroA || this.curve.threeA) { - var r = this; - for (i = 0; i < pow; i++) - r = r.dbl(); - return r; - } - - // 1M + 2S + 1A + N * (4S + 5M + 8A) - // N = 1 => 6M + 6S + 9A - var a = this.curve.a; - var tinv = this.curve.tinv; - - var jx = this.x; - var jy = this.y; - var jz = this.z; - var jz4 = jz.redSqr().redSqr(); - - // Reuse results - var jyd = jy.redAdd(jy); - for (i = 0; i < pow; i++) { - var jx2 = jx.redSqr(); - var jyd2 = jyd.redSqr(); - var jyd4 = jyd2.redSqr(); - var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); - - var t1 = jx.redMul(jyd2); - var nx = c.redSqr().redISub(t1.redAdd(t1)); - var t2 = t1.redISub(nx); - var dny = c.redMul(t2); - dny = dny.redIAdd(dny).redISub(jyd4); - var nz = jyd.redMul(jz); - if (i + 1 < pow) - jz4 = jz4.redMul(jyd4); - - jx = nx; - jz = nz; - jyd = dny; - } - - return this.curve.jpoint(jx, jyd.redMul(tinv), jz); -}; - -JPoint.prototype.dbl = function dbl() { - if (this.isInfinity()) - return this; - - if (this.curve.zeroA) - return this._zeroDbl(); - else if (this.curve.threeA) - return this._threeDbl(); - else - return this._dbl(); -}; - -JPoint.prototype._zeroDbl = function _zeroDbl() { - var nx; - var ny; - var nz; - // Z = 1 - if (this.zOne) { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html - // #doubling-mdbl-2007-bl - // 1M + 5S + 14A - - // XX = X1^2 - var xx = this.x.redSqr(); - // YY = Y1^2 - var yy = this.y.redSqr(); - // YYYY = YY^2 - var yyyy = yy.redSqr(); - // S = 2 * ((X1 + YY)^2 - XX - YYYY) - var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); - s = s.redIAdd(s); - // M = 3 * XX + a; a = 0 - var m = xx.redAdd(xx).redIAdd(xx); - // T = M ^ 2 - 2*S - var t = m.redSqr().redISub(s).redISub(s); - - // 8 * YYYY - var yyyy8 = yyyy.redIAdd(yyyy); - yyyy8 = yyyy8.redIAdd(yyyy8); - yyyy8 = yyyy8.redIAdd(yyyy8); - - // X3 = T - nx = t; - // Y3 = M * (S - T) - 8 * YYYY - ny = m.redMul(s.redISub(t)).redISub(yyyy8); - // Z3 = 2*Y1 - nz = this.y.redAdd(this.y); - } else { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html - // #doubling-dbl-2009-l - // 2M + 5S + 13A - - // A = X1^2 - var a = this.x.redSqr(); - // B = Y1^2 - var b = this.y.redSqr(); - // C = B^2 - var c = b.redSqr(); - // D = 2 * ((X1 + B)^2 - A - C) - var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c); - d = d.redIAdd(d); - // E = 3 * A - var e = a.redAdd(a).redIAdd(a); - // F = E^2 - var f = e.redSqr(); - - // 8 * C - var c8 = c.redIAdd(c); - c8 = c8.redIAdd(c8); - c8 = c8.redIAdd(c8); - - // X3 = F - 2 * D - nx = f.redISub(d).redISub(d); - // Y3 = E * (D - X3) - 8 * C - ny = e.redMul(d.redISub(nx)).redISub(c8); - // Z3 = 2 * Y1 * Z1 - nz = this.y.redMul(this.z); - nz = nz.redIAdd(nz); - } - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype._threeDbl = function _threeDbl() { - var nx; - var ny; - var nz; - // Z = 1 - if (this.zOne) { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html - // #doubling-mdbl-2007-bl - // 1M + 5S + 15A - - // XX = X1^2 - var xx = this.x.redSqr(); - // YY = Y1^2 - var yy = this.y.redSqr(); - // YYYY = YY^2 - var yyyy = yy.redSqr(); - // S = 2 * ((X1 + YY)^2 - XX - YYYY) - var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); - s = s.redIAdd(s); - // M = 3 * XX + a - var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a); - // T = M^2 - 2 * S - var t = m.redSqr().redISub(s).redISub(s); - // X3 = T - nx = t; - // Y3 = M * (S - T) - 8 * YYYY - var yyyy8 = yyyy.redIAdd(yyyy); - yyyy8 = yyyy8.redIAdd(yyyy8); - yyyy8 = yyyy8.redIAdd(yyyy8); - ny = m.redMul(s.redISub(t)).redISub(yyyy8); - // Z3 = 2 * Y1 - nz = this.y.redAdd(this.y); - } else { - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b - // 3M + 5S - - // delta = Z1^2 - var delta = this.z.redSqr(); - // gamma = Y1^2 - var gamma = this.y.redSqr(); - // beta = X1 * gamma - var beta = this.x.redMul(gamma); - // alpha = 3 * (X1 - delta) * (X1 + delta) - var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta)); - alpha = alpha.redAdd(alpha).redIAdd(alpha); - // X3 = alpha^2 - 8 * beta - var beta4 = beta.redIAdd(beta); - beta4 = beta4.redIAdd(beta4); - var beta8 = beta4.redAdd(beta4); - nx = alpha.redSqr().redISub(beta8); - // Z3 = (Y1 + Z1)^2 - gamma - delta - nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta); - // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2 - var ggamma8 = gamma.redSqr(); - ggamma8 = ggamma8.redIAdd(ggamma8); - ggamma8 = ggamma8.redIAdd(ggamma8); - ggamma8 = ggamma8.redIAdd(ggamma8); - ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8); - } - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype._dbl = function _dbl() { - var a = this.curve.a; - - // 4M + 6S + 10A - var jx = this.x; - var jy = this.y; - var jz = this.z; - var jz4 = jz.redSqr().redSqr(); - - var jx2 = jx.redSqr(); - var jy2 = jy.redSqr(); - - var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); - - var jxd4 = jx.redAdd(jx); - jxd4 = jxd4.redIAdd(jxd4); - var t1 = jxd4.redMul(jy2); - var nx = c.redSqr().redISub(t1.redAdd(t1)); - var t2 = t1.redISub(nx); - - var jyd8 = jy2.redSqr(); - jyd8 = jyd8.redIAdd(jyd8); - jyd8 = jyd8.redIAdd(jyd8); - jyd8 = jyd8.redIAdd(jyd8); - var ny = c.redMul(t2).redISub(jyd8); - var nz = jy.redAdd(jy).redMul(jz); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.trpl = function trpl() { - if (!this.curve.zeroA) - return this.dbl().add(this); - - // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl - // 5M + 10S + ... - - // XX = X1^2 - var xx = this.x.redSqr(); - // YY = Y1^2 - var yy = this.y.redSqr(); - // ZZ = Z1^2 - var zz = this.z.redSqr(); - // YYYY = YY^2 - var yyyy = yy.redSqr(); - // M = 3 * XX + a * ZZ2; a = 0 - var m = xx.redAdd(xx).redIAdd(xx); - // MM = M^2 - var mm = m.redSqr(); - // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM - var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); - e = e.redIAdd(e); - e = e.redAdd(e).redIAdd(e); - e = e.redISub(mm); - // EE = E^2 - var ee = e.redSqr(); - // T = 16*YYYY - var t = yyyy.redIAdd(yyyy); - t = t.redIAdd(t); - t = t.redIAdd(t); - t = t.redIAdd(t); - // U = (M + E)^2 - MM - EE - T - var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t); - // X3 = 4 * (X1 * EE - 4 * YY * U) - var yyu4 = yy.redMul(u); - yyu4 = yyu4.redIAdd(yyu4); - yyu4 = yyu4.redIAdd(yyu4); - var nx = this.x.redMul(ee).redISub(yyu4); - nx = nx.redIAdd(nx); - nx = nx.redIAdd(nx); - // Y3 = 8 * Y1 * (U * (T - U) - E * EE) - var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee))); - ny = ny.redIAdd(ny); - ny = ny.redIAdd(ny); - ny = ny.redIAdd(ny); - // Z3 = (Z1 + E)^2 - ZZ - EE - var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee); - - return this.curve.jpoint(nx, ny, nz); -}; - -JPoint.prototype.mul = function mul(k, kbase) { - k = new BN(k, kbase); - - return this.curve._wnafMul(this, k); -}; - -JPoint.prototype.eq = function eq(p) { - if (p.type === 'affine') - return this.eq(p.toJ()); - - if (this === p) - return true; - - // x1 * z2^2 == x2 * z1^2 - var z2 = this.z.redSqr(); - var pz2 = p.z.redSqr(); - if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0) - return false; - - // y1 * z2^3 == y2 * z1^3 - var z3 = z2.redMul(this.z); - var pz3 = pz2.redMul(p.z); - return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0; -}; - -JPoint.prototype.eqXToP = function eqXToP(x) { - var zs = this.z.redSqr(); - var rx = x.toRed(this.curve.red).redMul(zs); - if (this.x.cmp(rx) === 0) - return true; - - var xc = x.clone(); - var t = this.curve.redN.redMul(zs); - for (;;) { - xc.iadd(this.curve.n); - if (xc.cmp(this.curve.p) >= 0) - return false; - - rx.redIAdd(t); - if (this.x.cmp(rx) === 0) - return true; - } -}; - -JPoint.prototype.inspect = function inspect() { - if (this.isInfinity()) - return ''; - return ''; -}; - -JPoint.prototype.isInfinity = function isInfinity() { - // XXX This code assumes that zero is always zero in red - return this.z.cmpn(0) === 0; -}; - -},{"../utils":103,"./base":90,"bn.js":18,"inherits":146}],95:[function(require,module,exports){ -'use strict'; - -var curves = exports; - -var hash = require('hash.js'); -var curve = require('./curve'); -var utils = require('./utils'); - -var assert = utils.assert; - -function PresetCurve(options) { - if (options.type === 'short') - this.curve = new curve.short(options); - else if (options.type === 'edwards') - this.curve = new curve.edwards(options); - else - this.curve = new curve.mont(options); - this.g = this.curve.g; - this.n = this.curve.n; - this.hash = options.hash; - - assert(this.g.validate(), 'Invalid curve'); - assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O'); -} -curves.PresetCurve = PresetCurve; - -function defineCurve(name, options) { - Object.defineProperty(curves, name, { - configurable: true, - enumerable: true, - get: function() { - var curve = new PresetCurve(options); - Object.defineProperty(curves, name, { - configurable: true, - enumerable: true, - value: curve, - }); - return curve; - }, - }); -} - -defineCurve('p192', { - type: 'short', - prime: 'p192', - p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff', - a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc', - b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1', - n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831', - hash: hash.sha256, - gRed: false, - g: [ - '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012', - '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811', - ], -}); - -defineCurve('p224', { - type: 'short', - prime: 'p224', - p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001', - a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe', - b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4', - n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d', - hash: hash.sha256, - gRed: false, - g: [ - 'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21', - 'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34', - ], -}); - -defineCurve('p256', { - type: 'short', - prime: null, - p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff', - a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc', - b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b', - n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551', - hash: hash.sha256, - gRed: false, - g: [ - '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296', - '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5', - ], -}); - -defineCurve('p384', { - type: 'short', - prime: null, - p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'fffffffe ffffffff 00000000 00000000 ffffffff', - a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'fffffffe ffffffff 00000000 00000000 fffffffc', - b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' + - '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef', - n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' + - 'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973', - hash: hash.sha384, - gRed: false, - g: [ - 'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' + - '5502f25d bf55296c 3a545e38 72760ab7', - '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' + - '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f', - ], -}); - -defineCurve('p521', { - type: 'short', - prime: null, - p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff ffffffff', - a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff ffffffff ffffffff fffffffc', - b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' + - '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' + - '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00', - n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + - 'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' + - 'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409', - hash: hash.sha512, - gRed: false, - g: [ - '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' + - '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' + - 'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66', - '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' + - '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' + - '3fad0761 353c7086 a272c240 88be9476 9fd16650', - ], -}); - -defineCurve('curve25519', { - type: 'mont', - prime: 'p25519', - p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', - a: '76d06', - b: '1', - n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', - hash: hash.sha256, - gRed: false, - g: [ - '9', - ], -}); - -defineCurve('ed25519', { - type: 'edwards', - prime: 'p25519', - p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', - a: '-1', - c: '1', - // -121665 * (121666^(-1)) (mod P) - d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3', - n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', - hash: hash.sha256, - gRed: false, - g: [ - '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a', - - // 4/5 - '6666666666666666666666666666666666666666666666666666666666666658', - ], -}); - -var pre; -try { - pre = require('./precomputed/secp256k1'); -} catch (e) { - pre = undefined; -} - -defineCurve('secp256k1', { - type: 'short', - prime: 'k256', - p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f', - a: '0', - b: '7', - n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141', - h: '1', - hash: hash.sha256, - - // Precomputed endomorphism - beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', - lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', - basis: [ - { - a: '3086d221a7d46bcde86c90e49284eb15', - b: '-e4437ed6010e88286f547fa90abfe4c3', - }, - { - a: '114ca50f7a8e2f3f657c1108d9d44cfd8', - b: '3086d221a7d46bcde86c90e49284eb15', - }, - ], - - gRed: false, - g: [ - '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', - '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', - pre, - ], -}); - -},{"./curve":92,"./precomputed/secp256k1":102,"./utils":103,"hash.js":132}],96:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var HmacDRBG = require('hmac-drbg'); -var utils = require('../utils'); -var curves = require('../curves'); -var rand = require('brorand'); -var assert = utils.assert; - -var KeyPair = require('./key'); -var Signature = require('./signature'); - -function EC(options) { - if (!(this instanceof EC)) - return new EC(options); - - // Shortcut `elliptic.ec(curve-name)` - if (typeof options === 'string') { - assert(Object.prototype.hasOwnProperty.call(curves, options), - 'Unknown curve ' + options); - - options = curves[options]; - } - - // Shortcut for `elliptic.ec(elliptic.curves.curveName)` - if (options instanceof curves.PresetCurve) - options = { curve: options }; - - this.curve = options.curve.curve; - this.n = this.curve.n; - this.nh = this.n.ushrn(1); - this.g = this.curve.g; - - // Point on curve - this.g = options.curve.g; - this.g.precompute(options.curve.n.bitLength() + 1); - - // Hash for function for DRBG - this.hash = options.hash || options.curve.hash; -} -module.exports = EC; - -EC.prototype.keyPair = function keyPair(options) { - return new KeyPair(this, options); -}; - -EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { - return KeyPair.fromPrivate(this, priv, enc); -}; - -EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { - return KeyPair.fromPublic(this, pub, enc); -}; - -EC.prototype.genKeyPair = function genKeyPair(options) { - if (!options) - options = {}; - - // Instantiate Hmac_DRBG - var drbg = new HmacDRBG({ - hash: this.hash, - pers: options.pers, - persEnc: options.persEnc || 'utf8', - entropy: options.entropy || rand(this.hash.hmacStrength), - entropyEnc: options.entropy && options.entropyEnc || 'utf8', - nonce: this.n.toArray(), - }); - - var bytes = this.n.byteLength(); - var ns2 = this.n.sub(new BN(2)); - for (;;) { - var priv = new BN(drbg.generate(bytes)); - if (priv.cmp(ns2) > 0) - continue; - - priv.iaddn(1); - return this.keyFromPrivate(priv); - } -}; - -EC.prototype._truncateToN = function _truncateToN(msg, truncOnly) { - var delta = msg.byteLength() * 8 - this.n.bitLength(); - if (delta > 0) - msg = msg.ushrn(delta); - if (!truncOnly && msg.cmp(this.n) >= 0) - return msg.sub(this.n); - else - return msg; -}; - -EC.prototype.sign = function sign(msg, key, enc, options) { - if (typeof enc === 'object') { - options = enc; - enc = null; - } - if (!options) - options = {}; - - key = this.keyFromPrivate(key, enc); - msg = this._truncateToN(new BN(msg, 16)); - - // Zero-extend key to provide enough entropy - var bytes = this.n.byteLength(); - var bkey = key.getPrivate().toArray('be', bytes); - - // Zero-extend nonce to have the same byte size as N - var nonce = msg.toArray('be', bytes); - - // Instantiate Hmac_DRBG - var drbg = new HmacDRBG({ - hash: this.hash, - entropy: bkey, - nonce: nonce, - pers: options.pers, - persEnc: options.persEnc || 'utf8', - }); - - // Number of bytes to generate - var ns1 = this.n.sub(new BN(1)); - - for (var iter = 0; ; iter++) { - var k = options.k ? - options.k(iter) : - new BN(drbg.generate(this.n.byteLength())); - k = this._truncateToN(k, true); - if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) - continue; - - var kp = this.g.mul(k); - if (kp.isInfinity()) - continue; - - var kpX = kp.getX(); - var r = kpX.umod(this.n); - if (r.cmpn(0) === 0) - continue; - - var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); - s = s.umod(this.n); - if (s.cmpn(0) === 0) - continue; - - var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | - (kpX.cmp(r) !== 0 ? 2 : 0); - - // Use complement of `s`, if it is > `n / 2` - if (options.canonical && s.cmp(this.nh) > 0) { - s = this.n.sub(s); - recoveryParam ^= 1; - } - - return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); - } -}; - -EC.prototype.verify = function verify(msg, signature, key, enc) { - msg = this._truncateToN(new BN(msg, 16)); - key = this.keyFromPublic(key, enc); - signature = new Signature(signature, 'hex'); - - // Perform primitive values validation - var r = signature.r; - var s = signature.s; - if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) - return false; - if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) - return false; - - // Validate signature - var sinv = s.invm(this.n); - var u1 = sinv.mul(msg).umod(this.n); - var u2 = sinv.mul(r).umod(this.n); - var p; - - if (!this.curve._maxwellTrick) { - p = this.g.mulAdd(u1, key.getPublic(), u2); - if (p.isInfinity()) - return false; - - return p.getX().umod(this.n).cmp(r) === 0; - } - - // NOTE: Greg Maxwell's trick, inspired by: - // https://git.io/vad3K - - p = this.g.jmulAdd(u1, key.getPublic(), u2); - if (p.isInfinity()) - return false; - - // Compare `p.x` of Jacobian point with `r`, - // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the - // inverse of `p.z^2` - return p.eqXToP(r); -}; - -EC.prototype.recoverPubKey = function(msg, signature, j, enc) { - assert((3 & j) === j, 'The recovery param is more than two bits'); - signature = new Signature(signature, enc); - - var n = this.n; - var e = new BN(msg); - var r = signature.r; - var s = signature.s; - - // A set LSB signifies that the y-coordinate is odd - var isYOdd = j & 1; - var isSecondKey = j >> 1; - if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) - throw new Error('Unable to find sencond key candinate'); - - // 1.1. Let x = r + jn. - if (isSecondKey) - r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); - else - r = this.curve.pointFromX(r, isYOdd); - - var rInv = signature.r.invm(n); - var s1 = n.sub(e).mul(rInv).umod(n); - var s2 = s.mul(rInv).umod(n); - - // 1.6.1 Compute Q = r^-1 (sR - eG) - // Q = r^-1 (sR + -eG) - return this.g.mulAdd(s1, r, s2); -}; - -EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { - signature = new Signature(signature, enc); - if (signature.recoveryParam !== null) - return signature.recoveryParam; - - for (var i = 0; i < 4; i++) { - var Qprime; - try { - Qprime = this.recoverPubKey(e, signature, i); - } catch (e) { - continue; - } - - if (Qprime.eq(Q)) - return i; - } - throw new Error('Unable to find valid recovery factor'); -}; - -},{"../curves":95,"../utils":103,"./key":97,"./signature":98,"bn.js":18,"brorand":19,"hmac-drbg":144}],97:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var utils = require('../utils'); -var assert = utils.assert; - -function KeyPair(ec, options) { - this.ec = ec; - this.priv = null; - this.pub = null; - - // KeyPair(ec, { priv: ..., pub: ... }) - if (options.priv) - this._importPrivate(options.priv, options.privEnc); - if (options.pub) - this._importPublic(options.pub, options.pubEnc); -} -module.exports = KeyPair; - -KeyPair.fromPublic = function fromPublic(ec, pub, enc) { - if (pub instanceof KeyPair) - return pub; - - return new KeyPair(ec, { - pub: pub, - pubEnc: enc, - }); -}; - -KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) { - if (priv instanceof KeyPair) - return priv; - - return new KeyPair(ec, { - priv: priv, - privEnc: enc, - }); -}; - -KeyPair.prototype.validate = function validate() { - var pub = this.getPublic(); - - if (pub.isInfinity()) - return { result: false, reason: 'Invalid public key' }; - if (!pub.validate()) - return { result: false, reason: 'Public key is not a point' }; - if (!pub.mul(this.ec.curve.n).isInfinity()) - return { result: false, reason: 'Public key * N != O' }; - - return { result: true, reason: null }; -}; - -KeyPair.prototype.getPublic = function getPublic(compact, enc) { - // compact is optional argument - if (typeof compact === 'string') { - enc = compact; - compact = null; - } - - if (!this.pub) - this.pub = this.ec.g.mul(this.priv); - - if (!enc) - return this.pub; - - return this.pub.encode(enc, compact); -}; - -KeyPair.prototype.getPrivate = function getPrivate(enc) { - if (enc === 'hex') - return this.priv.toString(16, 2); - else - return this.priv; -}; - -KeyPair.prototype._importPrivate = function _importPrivate(key, enc) { - this.priv = new BN(key, enc || 16); - - // Ensure that the priv won't be bigger than n, otherwise we may fail - // in fixed multiplication method - this.priv = this.priv.umod(this.ec.curve.n); -}; - -KeyPair.prototype._importPublic = function _importPublic(key, enc) { - if (key.x || key.y) { - // Montgomery points only have an `x` coordinate. - // Weierstrass/Edwards points on the other hand have both `x` and - // `y` coordinates. - if (this.ec.curve.type === 'mont') { - assert(key.x, 'Need x coordinate'); - } else if (this.ec.curve.type === 'short' || - this.ec.curve.type === 'edwards') { - assert(key.x && key.y, 'Need both x and y coordinate'); - } - this.pub = this.ec.curve.point(key.x, key.y); - return; - } - this.pub = this.ec.curve.decodePoint(key, enc); -}; - -// ECDH -KeyPair.prototype.derive = function derive(pub) { - if(!pub.validate()) { - assert(pub.validate(), 'public point not validated'); - } - return pub.mul(this.priv).getX(); -}; - -// ECDSA -KeyPair.prototype.sign = function sign(msg, enc, options) { - return this.ec.sign(msg, this, enc, options); -}; - -KeyPair.prototype.verify = function verify(msg, signature) { - return this.ec.verify(msg, signature, this); -}; - -KeyPair.prototype.inspect = function inspect() { - return ''; -}; - -},{"../utils":103,"bn.js":18}],98:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); - -var utils = require('../utils'); -var assert = utils.assert; - -function Signature(options, enc) { - if (options instanceof Signature) - return options; - - if (this._importDER(options, enc)) - return; - - assert(options.r && options.s, 'Signature without r or s'); - this.r = new BN(options.r, 16); - this.s = new BN(options.s, 16); - if (options.recoveryParam === undefined) - this.recoveryParam = null; - else - this.recoveryParam = options.recoveryParam; -} -module.exports = Signature; - -function Position() { - this.place = 0; -} - -function getLength(buf, p) { - var initial = buf[p.place++]; - if (!(initial & 0x80)) { - return initial; - } - var octetLen = initial & 0xf; - - // Indefinite length or overflow - if (octetLen === 0 || octetLen > 4) { - return false; - } - - var val = 0; - for (var i = 0, off = p.place; i < octetLen; i++, off++) { - val <<= 8; - val |= buf[off]; - val >>>= 0; - } - - // Leading zeroes - if (val <= 0x7f) { - return false; - } - - p.place = off; - return val; -} - -function rmPadding(buf) { - var i = 0; - var len = buf.length - 1; - while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) { - i++; - } - if (i === 0) { - return buf; - } - return buf.slice(i); -} - -Signature.prototype._importDER = function _importDER(data, enc) { - data = utils.toArray(data, enc); - var p = new Position(); - if (data[p.place++] !== 0x30) { - return false; - } - var len = getLength(data, p); - if (len === false) { - return false; - } - if ((len + p.place) !== data.length) { - return false; - } - if (data[p.place++] !== 0x02) { - return false; - } - var rlen = getLength(data, p); - if (rlen === false) { - return false; - } - var r = data.slice(p.place, rlen + p.place); - p.place += rlen; - if (data[p.place++] !== 0x02) { - return false; - } - var slen = getLength(data, p); - if (slen === false) { - return false; - } - if (data.length !== slen + p.place) { - return false; - } - var s = data.slice(p.place, slen + p.place); - if (r[0] === 0) { - if (r[1] & 0x80) { - r = r.slice(1); - } else { - // Leading zeroes - return false; - } - } - if (s[0] === 0) { - if (s[1] & 0x80) { - s = s.slice(1); - } else { - // Leading zeroes - return false; - } - } - - this.r = new BN(r); - this.s = new BN(s); - this.recoveryParam = null; - - return true; -}; - -function constructLength(arr, len) { - if (len < 0x80) { - arr.push(len); - return; - } - var octets = 1 + (Math.log(len) / Math.LN2 >>> 3); - arr.push(octets | 0x80); - while (--octets) { - arr.push((len >>> (octets << 3)) & 0xff); - } - arr.push(len); -} - -Signature.prototype.toDER = function toDER(enc) { - var r = this.r.toArray(); - var s = this.s.toArray(); - - // Pad values - if (r[0] & 0x80) - r = [ 0 ].concat(r); - // Pad values - if (s[0] & 0x80) - s = [ 0 ].concat(s); - - r = rmPadding(r); - s = rmPadding(s); - - while (!s[0] && !(s[1] & 0x80)) { - s = s.slice(1); - } - var arr = [ 0x02 ]; - constructLength(arr, r.length); - arr = arr.concat(r); - arr.push(0x02); - constructLength(arr, s.length); - var backHalf = arr.concat(s); - var res = [ 0x30 ]; - constructLength(res, backHalf.length); - res = res.concat(backHalf); - return utils.encode(res, enc); -}; - -},{"../utils":103,"bn.js":18}],99:[function(require,module,exports){ -'use strict'; - -var hash = require('hash.js'); -var curves = require('../curves'); -var utils = require('../utils'); -var assert = utils.assert; -var parseBytes = utils.parseBytes; -var KeyPair = require('./key'); -var Signature = require('./signature'); - -function EDDSA(curve) { - assert(curve === 'ed25519', 'only tested with ed25519 so far'); - - if (!(this instanceof EDDSA)) - return new EDDSA(curve); - - curve = curves[curve].curve; - this.curve = curve; - this.g = curve.g; - this.g.precompute(curve.n.bitLength() + 1); - - this.pointClass = curve.point().constructor; - this.encodingLength = Math.ceil(curve.n.bitLength() / 8); - this.hash = hash.sha512; -} - -module.exports = EDDSA; - -/** -* @param {Array|String} message - message bytes -* @param {Array|String|KeyPair} secret - secret bytes or a keypair -* @returns {Signature} - signature -*/ -EDDSA.prototype.sign = function sign(message, secret) { - message = parseBytes(message); - var key = this.keyFromSecret(secret); - var r = this.hashInt(key.messagePrefix(), message); - var R = this.g.mul(r); - var Rencoded = this.encodePoint(R); - var s_ = this.hashInt(Rencoded, key.pubBytes(), message) - .mul(key.priv()); - var S = r.add(s_).umod(this.curve.n); - return this.makeSignature({ R: R, S: S, Rencoded: Rencoded }); -}; - -/** -* @param {Array} message - message bytes -* @param {Array|String|Signature} sig - sig bytes -* @param {Array|String|Point|KeyPair} pub - public key -* @returns {Boolean} - true if public key matches sig of message -*/ -EDDSA.prototype.verify = function verify(message, sig, pub) { - message = parseBytes(message); - sig = this.makeSignature(sig); - var key = this.keyFromPublic(pub); - var h = this.hashInt(sig.Rencoded(), key.pubBytes(), message); - var SG = this.g.mul(sig.S()); - var RplusAh = sig.R().add(key.pub().mul(h)); - return RplusAh.eq(SG); -}; - -EDDSA.prototype.hashInt = function hashInt() { - var hash = this.hash(); - for (var i = 0; i < arguments.length; i++) - hash.update(arguments[i]); - return utils.intFromLE(hash.digest()).umod(this.curve.n); -}; - -EDDSA.prototype.keyFromPublic = function keyFromPublic(pub) { - return KeyPair.fromPublic(this, pub); -}; - -EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) { - return KeyPair.fromSecret(this, secret); -}; - -EDDSA.prototype.makeSignature = function makeSignature(sig) { - if (sig instanceof Signature) - return sig; - return new Signature(this, sig); -}; - -/** -* * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2 -* -* EDDSA defines methods for encoding and decoding points and integers. These are -* helper convenience methods, that pass along to utility functions implied -* parameters. -* -*/ -EDDSA.prototype.encodePoint = function encodePoint(point) { - var enc = point.getY().toArray('le', this.encodingLength); - enc[this.encodingLength - 1] |= point.getX().isOdd() ? 0x80 : 0; - return enc; -}; - -EDDSA.prototype.decodePoint = function decodePoint(bytes) { - bytes = utils.parseBytes(bytes); - - var lastIx = bytes.length - 1; - var normed = bytes.slice(0, lastIx).concat(bytes[lastIx] & ~0x80); - var xIsOdd = (bytes[lastIx] & 0x80) !== 0; - - var y = utils.intFromLE(normed); - return this.curve.pointFromY(y, xIsOdd); -}; - -EDDSA.prototype.encodeInt = function encodeInt(num) { - return num.toArray('le', this.encodingLength); -}; - -EDDSA.prototype.decodeInt = function decodeInt(bytes) { - return utils.intFromLE(bytes); -}; - -EDDSA.prototype.isPoint = function isPoint(val) { - return val instanceof this.pointClass; -}; - -},{"../curves":95,"../utils":103,"./key":100,"./signature":101,"hash.js":132}],100:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var assert = utils.assert; -var parseBytes = utils.parseBytes; -var cachedProperty = utils.cachedProperty; - -/** -* @param {EDDSA} eddsa - instance -* @param {Object} params - public/private key parameters -* -* @param {Array} [params.secret] - secret seed bytes -* @param {Point} [params.pub] - public key point (aka `A` in eddsa terms) -* @param {Array} [params.pub] - public key point encoded as bytes -* -*/ -function KeyPair(eddsa, params) { - this.eddsa = eddsa; - this._secret = parseBytes(params.secret); - if (eddsa.isPoint(params.pub)) - this._pub = params.pub; - else - this._pubBytes = parseBytes(params.pub); -} - -KeyPair.fromPublic = function fromPublic(eddsa, pub) { - if (pub instanceof KeyPair) - return pub; - return new KeyPair(eddsa, { pub: pub }); -}; - -KeyPair.fromSecret = function fromSecret(eddsa, secret) { - if (secret instanceof KeyPair) - return secret; - return new KeyPair(eddsa, { secret: secret }); -}; - -KeyPair.prototype.secret = function secret() { - return this._secret; -}; - -cachedProperty(KeyPair, 'pubBytes', function pubBytes() { - return this.eddsa.encodePoint(this.pub()); -}); - -cachedProperty(KeyPair, 'pub', function pub() { - if (this._pubBytes) - return this.eddsa.decodePoint(this._pubBytes); - return this.eddsa.g.mul(this.priv()); -}); - -cachedProperty(KeyPair, 'privBytes', function privBytes() { - var eddsa = this.eddsa; - var hash = this.hash(); - var lastIx = eddsa.encodingLength - 1; - - var a = hash.slice(0, eddsa.encodingLength); - a[0] &= 248; - a[lastIx] &= 127; - a[lastIx] |= 64; - - return a; -}); - -cachedProperty(KeyPair, 'priv', function priv() { - return this.eddsa.decodeInt(this.privBytes()); -}); - -cachedProperty(KeyPair, 'hash', function hash() { - return this.eddsa.hash().update(this.secret()).digest(); -}); - -cachedProperty(KeyPair, 'messagePrefix', function messagePrefix() { - return this.hash().slice(this.eddsa.encodingLength); -}); - -KeyPair.prototype.sign = function sign(message) { - assert(this._secret, 'KeyPair can only verify'); - return this.eddsa.sign(message, this); -}; - -KeyPair.prototype.verify = function verify(message, sig) { - return this.eddsa.verify(message, sig, this); -}; - -KeyPair.prototype.getSecret = function getSecret(enc) { - assert(this._secret, 'KeyPair is public only'); - return utils.encode(this.secret(), enc); -}; - -KeyPair.prototype.getPublic = function getPublic(enc) { - return utils.encode(this.pubBytes(), enc); -}; - -module.exports = KeyPair; - -},{"../utils":103}],101:[function(require,module,exports){ -'use strict'; - -var BN = require('bn.js'); -var utils = require('../utils'); -var assert = utils.assert; -var cachedProperty = utils.cachedProperty; -var parseBytes = utils.parseBytes; - -/** -* @param {EDDSA} eddsa - eddsa instance -* @param {Array|Object} sig - -* @param {Array|Point} [sig.R] - R point as Point or bytes -* @param {Array|bn} [sig.S] - S scalar as bn or bytes -* @param {Array} [sig.Rencoded] - R point encoded -* @param {Array} [sig.Sencoded] - S scalar encoded -*/ -function Signature(eddsa, sig) { - this.eddsa = eddsa; - - if (typeof sig !== 'object') - sig = parseBytes(sig); - - if (Array.isArray(sig)) { - sig = { - R: sig.slice(0, eddsa.encodingLength), - S: sig.slice(eddsa.encodingLength), - }; - } - - assert(sig.R && sig.S, 'Signature without R or S'); - - if (eddsa.isPoint(sig.R)) - this._R = sig.R; - if (sig.S instanceof BN) - this._S = sig.S; - - this._Rencoded = Array.isArray(sig.R) ? sig.R : sig.Rencoded; - this._Sencoded = Array.isArray(sig.S) ? sig.S : sig.Sencoded; -} - -cachedProperty(Signature, 'S', function S() { - return this.eddsa.decodeInt(this.Sencoded()); -}); - -cachedProperty(Signature, 'R', function R() { - return this.eddsa.decodePoint(this.Rencoded()); -}); - -cachedProperty(Signature, 'Rencoded', function Rencoded() { - return this.eddsa.encodePoint(this.R()); -}); - -cachedProperty(Signature, 'Sencoded', function Sencoded() { - return this.eddsa.encodeInt(this.S()); -}); - -Signature.prototype.toBytes = function toBytes() { - return this.Rencoded().concat(this.Sencoded()); -}; - -Signature.prototype.toHex = function toHex() { - return utils.encode(this.toBytes(), 'hex').toUpperCase(); -}; - -module.exports = Signature; - -},{"../utils":103,"bn.js":18}],102:[function(require,module,exports){ -module.exports = { - doubles: { - step: 4, - points: [ - [ - 'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a', - 'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821', - ], - [ - '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508', - '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf', - ], - [ - '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739', - 'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695', - ], - [ - '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640', - '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9', - ], - [ - '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c', - '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36', - ], - [ - '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda', - '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f', - ], - [ - 'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa', - '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999', - ], - [ - '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0', - 'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09', - ], - [ - 'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d', - '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d', - ], - [ - 'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d', - 'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088', - ], - [ - 'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1', - '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d', - ], - [ - '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0', - '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8', - ], - [ - '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047', - '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a', - ], - [ - '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862', - '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453', - ], - [ - '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7', - '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160', - ], - [ - '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd', - '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0', - ], - [ - '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83', - '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6', - ], - [ - '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a', - '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589', - ], - [ - '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8', - 'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17', - ], - [ - 'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d', - '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda', - ], - [ - 'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725', - '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd', - ], - [ - '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754', - '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2', - ], - [ - '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c', - '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6', - ], - [ - 'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6', - '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f', - ], - [ - '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39', - 'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01', - ], - [ - 'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891', - '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3', - ], - [ - 'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b', - 'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f', - ], - [ - 'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03', - '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7', - ], - [ - 'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d', - 'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78', - ], - [ - 'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070', - '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1', - ], - [ - '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4', - 'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150', - ], - [ - '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da', - '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82', - ], - [ - 'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11', - '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc', - ], - [ - '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e', - 'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b', - ], - [ - 'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41', - '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51', - ], - [ - 'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef', - '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45', - ], - [ - 'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8', - 'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120', - ], - [ - '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d', - '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84', - ], - [ - '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96', - '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d', - ], - [ - '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd', - 'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d', - ], - [ - '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5', - '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8', - ], - [ - 'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266', - '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8', - ], - [ - '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71', - '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac', - ], - [ - '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac', - 'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f', - ], - [ - '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751', - '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962', - ], - [ - 'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e', - '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907', - ], - [ - '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241', - 'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec', - ], - [ - 'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3', - 'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d', - ], - [ - 'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f', - '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414', - ], - [ - '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19', - 'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd', - ], - [ - '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be', - 'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0', - ], - [ - 'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9', - '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811', - ], - [ - 'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2', - '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1', - ], - [ - 'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13', - '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c', - ], - [ - '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c', - 'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73', - ], - [ - '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba', - '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd', - ], - [ - 'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151', - 'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405', - ], - [ - '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073', - 'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589', - ], - [ - '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458', - '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e', - ], - [ - '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b', - '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27', - ], - [ - 'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366', - 'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1', - ], - [ - '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa', - '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482', - ], - [ - '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0', - '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945', - ], - [ - 'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787', - '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573', - ], - [ - 'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e', - 'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82', - ], - ], - }, - naf: { - wnd: 7, - points: [ - [ - 'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9', - '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672', - ], - [ - '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4', - 'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6', - ], - [ - '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc', - '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da', - ], - [ - 'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe', - 'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37', - ], - [ - '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb', - 'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b', - ], - [ - 'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8', - 'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81', - ], - [ - 'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e', - '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58', - ], - [ - 'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34', - '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77', - ], - [ - '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c', - '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a', - ], - [ - '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5', - '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c', - ], - [ - '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f', - '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67', - ], - [ - '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714', - '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402', - ], - [ - 'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729', - 'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55', - ], - [ - 'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db', - '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482', - ], - [ - '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4', - 'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82', - ], - [ - '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5', - 'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396', - ], - [ - '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479', - '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49', - ], - [ - '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d', - '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf', - ], - [ - '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f', - '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a', - ], - [ - '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb', - 'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7', - ], - [ - 'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9', - 'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933', - ], - [ - '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963', - '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a', - ], - [ - '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74', - '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6', - ], - [ - 'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530', - 'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37', - ], - [ - '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b', - '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e', - ], - [ - 'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247', - 'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6', - ], - [ - 'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1', - 'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476', - ], - [ - '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120', - '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40', - ], - [ - '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435', - '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61', - ], - [ - '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18', - '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683', - ], - [ - 'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8', - '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5', - ], - [ - '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb', - '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b', - ], - [ - 'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f', - '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417', - ], - [ - '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143', - 'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868', - ], - [ - '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba', - 'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a', - ], - [ - 'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45', - 'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6', - ], - [ - '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a', - '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996', - ], - [ - '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e', - 'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e', - ], - [ - 'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8', - 'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d', - ], - [ - '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c', - '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2', - ], - [ - '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519', - 'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e', - ], - [ - '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab', - '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437', - ], - [ - '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca', - 'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311', - ], - [ - 'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf', - '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4', - ], - [ - '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610', - '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575', - ], - [ - '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4', - 'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d', - ], - [ - '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c', - 'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d', - ], - [ - 'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940', - 'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629', - ], - [ - 'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980', - 'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06', - ], - [ - '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3', - '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374', - ], - [ - '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf', - '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee', - ], - [ - 'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63', - '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1', - ], - [ - 'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448', - 'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b', - ], - [ - '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf', - '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661', - ], - [ - '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5', - '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6', - ], - [ - 'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6', - '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e', - ], - [ - '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5', - '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d', - ], - [ - 'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99', - 'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc', - ], - [ - '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51', - 'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4', - ], - [ - '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5', - '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c', - ], - [ - 'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5', - '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b', - ], - [ - 'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997', - '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913', - ], - [ - '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881', - '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154', - ], - [ - '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5', - '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865', - ], - [ - '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66', - 'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc', - ], - [ - '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726', - 'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224', - ], - [ - '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede', - '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e', - ], - [ - '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94', - '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6', - ], - [ - '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31', - '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511', - ], - [ - '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51', - 'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b', - ], - [ - 'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252', - 'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2', - ], - [ - '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5', - 'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c', - ], - [ - 'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b', - '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3', - ], - [ - 'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4', - '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d', - ], - [ - 'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f', - '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700', - ], - [ - 'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889', - '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4', - ], - [ - '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246', - 'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196', - ], - [ - '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984', - '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4', - ], - [ - '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a', - 'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257', - ], - [ - 'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030', - 'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13', - ], - [ - 'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197', - '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096', - ], - [ - 'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593', - 'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38', - ], - [ - 'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef', - '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f', - ], - [ - '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38', - '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448', - ], - [ - 'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a', - '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a', - ], - [ - 'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111', - '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4', - ], - [ - '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502', - '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437', - ], - [ - '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea', - 'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7', - ], - [ - 'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26', - '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d', - ], - [ - 'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986', - '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a', - ], - [ - 'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e', - '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54', - ], - [ - '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4', - '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77', - ], - [ - 'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda', - 'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517', - ], - [ - '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859', - 'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10', - ], - [ - 'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f', - 'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125', - ], - [ - 'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c', - '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e', - ], - [ - '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942', - 'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1', - ], - [ - 'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a', - '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2', - ], - [ - 'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80', - '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423', - ], - [ - 'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d', - '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8', - ], - [ - '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1', - 'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758', - ], - [ - '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63', - 'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375', - ], - [ - 'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352', - '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d', - ], - [ - '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193', - 'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec', - ], - [ - '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00', - '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0', - ], - [ - '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58', - 'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c', - ], - [ - 'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7', - 'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4', - ], - [ - '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8', - 'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f', - ], - [ - '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e', - '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649', - ], - [ - '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d', - 'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826', - ], - [ - '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b', - '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5', - ], - [ - 'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f', - 'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87', - ], - [ - '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6', - '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b', - ], - [ - 'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297', - '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc', - ], - [ - '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a', - '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c', - ], - [ - 'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c', - 'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f', - ], - [ - 'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52', - '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a', - ], - [ - 'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb', - 'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46', - ], - [ - '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065', - 'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f', - ], - [ - '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917', - '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03', - ], - [ - '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9', - 'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08', - ], - [ - '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3', - '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8', - ], - [ - '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57', - '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373', - ], - [ - '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66', - 'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3', - ], - [ - '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8', - '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8', - ], - [ - '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721', - '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1', - ], - [ - '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180', - '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9', - ], - ], - }, -}; - -},{}],103:[function(require,module,exports){ -'use strict'; - -var utils = exports; -var BN = require('bn.js'); -var minAssert = require('minimalistic-assert'); -var minUtils = require('minimalistic-crypto-utils'); - -utils.assert = minAssert; -utils.toArray = minUtils.toArray; -utils.zero2 = minUtils.zero2; -utils.toHex = minUtils.toHex; -utils.encode = minUtils.encode; - -// Represent num in a w-NAF form -function getNAF(num, w, bits) { - var naf = new Array(Math.max(num.bitLength(), bits) + 1); - naf.fill(0); - - var ws = 1 << (w + 1); - var k = num.clone(); - - for (var i = 0; i < naf.length; i++) { - var z; - var mod = k.andln(ws - 1); - if (k.isOdd()) { - if (mod > (ws >> 1) - 1) - z = (ws >> 1) - mod; - else - z = mod; - k.isubn(z); - } else { - z = 0; - } - - naf[i] = z; - k.iushrn(1); - } - - return naf; -} -utils.getNAF = getNAF; - -// Represent k1, k2 in a Joint Sparse Form -function getJSF(k1, k2) { - var jsf = [ - [], - [], - ]; - - k1 = k1.clone(); - k2 = k2.clone(); - var d1 = 0; - var d2 = 0; - var m8; - while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { - // First phase - var m14 = (k1.andln(3) + d1) & 3; - var m24 = (k2.andln(3) + d2) & 3; - if (m14 === 3) - m14 = -1; - if (m24 === 3) - m24 = -1; - var u1; - if ((m14 & 1) === 0) { - u1 = 0; - } else { - m8 = (k1.andln(7) + d1) & 7; - if ((m8 === 3 || m8 === 5) && m24 === 2) - u1 = -m14; - else - u1 = m14; - } - jsf[0].push(u1); - - var u2; - if ((m24 & 1) === 0) { - u2 = 0; - } else { - m8 = (k2.andln(7) + d2) & 7; - if ((m8 === 3 || m8 === 5) && m14 === 2) - u2 = -m24; - else - u2 = m24; - } - jsf[1].push(u2); - - // Second phase - if (2 * d1 === u1 + 1) - d1 = 1 - d1; - if (2 * d2 === u2 + 1) - d2 = 1 - d2; - k1.iushrn(1); - k2.iushrn(1); - } - - return jsf; -} -utils.getJSF = getJSF; - -function cachedProperty(obj, name, computer) { - var key = '_' + name; - obj.prototype[name] = function cachedProperty() { - return this[key] !== undefined ? this[key] : - this[key] = computer.call(this); - }; -} -utils.cachedProperty = cachedProperty; - -function parseBytes(bytes) { - return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : - bytes; -} -utils.parseBytes = parseBytes; - -function intFromLE(bytes) { - return new BN(bytes, 'hex', 'le'); -} -utils.intFromLE = intFromLE; - - -},{"bn.js":18,"minimalistic-assert":223,"minimalistic-crypto-utils":224}],104:[function(require,module,exports){ -module.exports={ - "name": "elliptic", - "version": "6.5.4", - "description": "EC cryptography", - "main": "lib/elliptic.js", - "files": [ - "lib" - ], - "scripts": { - "lint": "eslint lib test", - "lint:fix": "npm run lint -- --fix", - "unit": "istanbul test _mocha --reporter=spec test/index.js", - "test": "npm run lint && npm run unit", - "version": "grunt dist && git add dist/" - }, - "repository": { - "type": "git", - "url": "git@github.com:indutny/elliptic" - }, - "keywords": [ - "EC", - "Elliptic", - "curve", - "Cryptography" - ], - "author": "Fedor Indutny ", - "license": "MIT", - "bugs": { - "url": "https://github.com/indutny/elliptic/issues" - }, - "homepage": "https://github.com/indutny/elliptic", - "devDependencies": { - "brfs": "^2.0.2", - "coveralls": "^3.1.0", - "eslint": "^7.6.0", - "grunt": "^1.2.1", - "grunt-browserify": "^5.3.0", - "grunt-cli": "^1.3.2", - "grunt-contrib-connect": "^3.0.0", - "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-uglify": "^5.0.0", - "grunt-mocha-istanbul": "^5.0.2", - "grunt-saucelabs": "^9.0.1", - "istanbul": "^0.4.5", - "mocha": "^8.0.1" - }, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } -} - -},{}],105:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -var R = typeof Reflect === 'object' ? Reflect : null -var ReflectApply = R && typeof R.apply === 'function' - ? R.apply - : function ReflectApply(target, receiver, args) { - return Function.prototype.apply.call(target, receiver, args); - } - -var ReflectOwnKeys -if (R && typeof R.ownKeys === 'function') { - ReflectOwnKeys = R.ownKeys -} else if (Object.getOwnPropertySymbols) { - ReflectOwnKeys = function ReflectOwnKeys(target) { - return Object.getOwnPropertyNames(target) - .concat(Object.getOwnPropertySymbols(target)); - }; -} else { - ReflectOwnKeys = function ReflectOwnKeys(target) { - return Object.getOwnPropertyNames(target); - }; -} - -function ProcessEmitWarning(warning) { - if (console && console.warn) console.warn(warning); -} - -var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { - return value !== value; -} - -function EventEmitter() { - EventEmitter.init.call(this); -} -module.exports = EventEmitter; -module.exports.once = once; - -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; - -EventEmitter.prototype._events = undefined; -EventEmitter.prototype._eventsCount = 0; -EventEmitter.prototype._maxListeners = undefined; - -// By default EventEmitters will print a warning if more than 10 listeners are -// added to it. This is a useful default which helps finding memory leaks. -var defaultMaxListeners = 10; - -function checkListener(listener) { - if (typeof listener !== 'function') { - throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); - } -} - -Object.defineProperty(EventEmitter, 'defaultMaxListeners', { - enumerable: true, - get: function() { - return defaultMaxListeners; - }, - set: function(arg) { - if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { - throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); - } - defaultMaxListeners = arg; - } -}); - -EventEmitter.init = function() { - - if (this._events === undefined || - this._events === Object.getPrototypeOf(this)._events) { - this._events = Object.create(null); - this._eventsCount = 0; - } - - this._maxListeners = this._maxListeners || undefined; -}; - -// Obviously not all Emitters should be limited to 10. This function allows -// that to be increased. Set to zero for unlimited. -EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { - throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); - } - this._maxListeners = n; - return this; -}; - -function _getMaxListeners(that) { - if (that._maxListeners === undefined) - return EventEmitter.defaultMaxListeners; - return that._maxListeners; -} - -EventEmitter.prototype.getMaxListeners = function getMaxListeners() { - return _getMaxListeners(this); -}; - -EventEmitter.prototype.emit = function emit(type) { - var args = []; - for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); - var doError = (type === 'error'); - - var events = this._events; - if (events !== undefined) - doError = (doError && events.error === undefined); - else if (!doError) - return false; - - // If there is no 'error' event listener then throw. - if (doError) { - var er; - if (args.length > 0) - er = args[0]; - if (er instanceof Error) { - // Note: The comments on the `throw` lines are intentional, they show - // up in Node's output if this results in an unhandled exception. - throw er; // Unhandled 'error' event - } - // At least give some kind of context to the user - var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); - err.context = er; - throw err; // Unhandled 'error' event - } - - var handler = events[type]; - - if (handler === undefined) - return false; - - if (typeof handler === 'function') { - ReflectApply(handler, this, args); - } else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - ReflectApply(listeners[i], this, args); - } - - return true; -}; - -function _addListener(target, type, listener, prepend) { - var m; - var events; - var existing; - - checkListener(listener); - - events = target._events; - if (events === undefined) { - events = target._events = Object.create(null); - target._eventsCount = 0; - } else { - // To avoid recursion in the case that type === "newListener"! Before - // adding it to the listeners, first emit "newListener". - if (events.newListener !== undefined) { - target.emit('newListener', type, - listener.listener ? listener.listener : listener); - - // Re-assign `events` because a newListener handler could have caused the - // this._events to be assigned to a new object - events = target._events; - } - existing = events[type]; - } - - if (existing === undefined) { - // Optimize the case of one listener. Don't need the extra array object. - existing = events[type] = listener; - ++target._eventsCount; - } else { - if (typeof existing === 'function') { - // Adding the second element, need to change to array. - existing = events[type] = - prepend ? [listener, existing] : [existing, listener]; - // If we've already got an array, just append. - } else if (prepend) { - existing.unshift(listener); - } else { - existing.push(listener); - } - - // Check for listener leak - m = _getMaxListeners(target); - if (m > 0 && existing.length > m && !existing.warned) { - existing.warned = true; - // No error code for this since it is a Warning - // eslint-disable-next-line no-restricted-syntax - var w = new Error('Possible EventEmitter memory leak detected. ' + - existing.length + ' ' + String(type) + ' listeners ' + - 'added. Use emitter.setMaxListeners() to ' + - 'increase limit'); - w.name = 'MaxListenersExceededWarning'; - w.emitter = target; - w.type = type; - w.count = existing.length; - ProcessEmitWarning(w); - } - } - - return target; -} - -EventEmitter.prototype.addListener = function addListener(type, listener) { - return _addListener(this, type, listener, false); -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.prependListener = - function prependListener(type, listener) { - return _addListener(this, type, listener, true); - }; - -function onceWrapper() { - if (!this.fired) { - this.target.removeListener(this.type, this.wrapFn); - this.fired = true; - if (arguments.length === 0) - return this.listener.call(this.target); - return this.listener.apply(this.target, arguments); - } -} - -function _onceWrap(target, type, listener) { - var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; - var wrapped = onceWrapper.bind(state); - wrapped.listener = listener; - state.wrapFn = wrapped; - return wrapped; -} - -EventEmitter.prototype.once = function once(type, listener) { - checkListener(listener); - this.on(type, _onceWrap(this, type, listener)); - return this; -}; - -EventEmitter.prototype.prependOnceListener = - function prependOnceListener(type, listener) { - checkListener(listener); - this.prependListener(type, _onceWrap(this, type, listener)); - return this; - }; - -// Emits a 'removeListener' event if and only if the listener was removed. -EventEmitter.prototype.removeListener = - function removeListener(type, listener) { - var list, events, position, i, originalListener; - - checkListener(listener); - - events = this._events; - if (events === undefined) - return this; - - list = events[type]; - if (list === undefined) - return this; - - if (list === listener || list.listener === listener) { - if (--this._eventsCount === 0) - this._events = Object.create(null); - else { - delete events[type]; - if (events.removeListener) - this.emit('removeListener', type, list.listener || listener); - } - } else if (typeof list !== 'function') { - position = -1; - - for (i = list.length - 1; i >= 0; i--) { - if (list[i] === listener || list[i].listener === listener) { - originalListener = list[i].listener; - position = i; - break; - } - } - - if (position < 0) - return this; - - if (position === 0) - list.shift(); - else { - spliceOne(list, position); - } - - if (list.length === 1) - events[type] = list[0]; - - if (events.removeListener !== undefined) - this.emit('removeListener', type, originalListener || listener); - } - - return this; - }; - -EventEmitter.prototype.off = EventEmitter.prototype.removeListener; - -EventEmitter.prototype.removeAllListeners = - function removeAllListeners(type) { - var listeners, events, i; - - events = this._events; - if (events === undefined) - return this; - - // not listening for removeListener, no need to emit - if (events.removeListener === undefined) { - if (arguments.length === 0) { - this._events = Object.create(null); - this._eventsCount = 0; - } else if (events[type] !== undefined) { - if (--this._eventsCount === 0) - this._events = Object.create(null); - else - delete events[type]; - } - return this; - } - - // emit removeListener for all listeners on all events - if (arguments.length === 0) { - var keys = Object.keys(events); - var key; - for (i = 0; i < keys.length; ++i) { - key = keys[i]; - if (key === 'removeListener') continue; - this.removeAllListeners(key); - } - this.removeAllListeners('removeListener'); - this._events = Object.create(null); - this._eventsCount = 0; - return this; - } - - listeners = events[type]; - - if (typeof listeners === 'function') { - this.removeListener(type, listeners); - } else if (listeners !== undefined) { - // LIFO order - for (i = listeners.length - 1; i >= 0; i--) { - this.removeListener(type, listeners[i]); - } - } - - return this; - }; - -function _listeners(target, type, unwrap) { - var events = target._events; - - if (events === undefined) - return []; - - var evlistener = events[type]; - if (evlistener === undefined) - return []; - - if (typeof evlistener === 'function') - return unwrap ? [evlistener.listener || evlistener] : [evlistener]; - - return unwrap ? - unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); -} - -EventEmitter.prototype.listeners = function listeners(type) { - return _listeners(this, type, true); -}; - -EventEmitter.prototype.rawListeners = function rawListeners(type) { - return _listeners(this, type, false); -}; - -EventEmitter.listenerCount = function(emitter, type) { - if (typeof emitter.listenerCount === 'function') { - return emitter.listenerCount(type); - } else { - return listenerCount.call(emitter, type); - } -}; - -EventEmitter.prototype.listenerCount = listenerCount; -function listenerCount(type) { - var events = this._events; - - if (events !== undefined) { - var evlistener = events[type]; - - if (typeof evlistener === 'function') { - return 1; - } else if (evlistener !== undefined) { - return evlistener.length; - } - } - - return 0; -} - -EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; -}; - -function arrayClone(arr, n) { - var copy = new Array(n); - for (var i = 0; i < n; ++i) - copy[i] = arr[i]; - return copy; -} - -function spliceOne(list, index) { - for (; index + 1 < list.length; index++) - list[index] = list[index + 1]; - list.pop(); -} - -function unwrapListeners(arr) { - var ret = new Array(arr.length); - for (var i = 0; i < ret.length; ++i) { - ret[i] = arr[i].listener || arr[i]; - } - return ret; -} - -function once(emitter, name) { - return new Promise(function (resolve, reject) { - function errorListener(err) { - emitter.removeListener(name, resolver); - reject(err); - } - - function resolver() { - if (typeof emitter.removeListener === 'function') { - emitter.removeListener('error', errorListener); - } - resolve([].slice.call(arguments)); - }; - - eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); - if (name !== 'error') { - addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); - } - }); -} - -function addErrorHandlerIfEventEmitter(emitter, handler, flags) { - if (typeof emitter.on === 'function') { - eventTargetAgnosticAddListener(emitter, 'error', handler, flags); - } -} - -function eventTargetAgnosticAddListener(emitter, name, listener, flags) { - if (typeof emitter.on === 'function') { - if (flags.once) { - emitter.once(name, listener); - } else { - emitter.on(name, listener); - } - } else if (typeof emitter.addEventListener === 'function') { - // EventTarget does not have `error` event semantics like Node - // EventEmitters, we do not listen for `error` events here. - emitter.addEventListener(name, function wrapListener(arg) { - // IE does not have builtin `{ once: true }` support so we - // have to do it manually. - if (flags.once) { - emitter.removeEventListener(name, wrapListener); - } - listener(arg); - }); - } else { - throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); - } -} - -},{}],106:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var MD5 = require('md5.js') - -/* eslint-disable camelcase */ -function EVP_BytesToKey (password, salt, keyBits, ivLen) { - if (!Buffer.isBuffer(password)) password = Buffer.from(password, 'binary') - if (salt) { - if (!Buffer.isBuffer(salt)) salt = Buffer.from(salt, 'binary') - if (salt.length !== 8) throw new RangeError('salt should be Buffer with 8 byte length') - } - - var keyLen = keyBits / 8 - var key = Buffer.alloc(keyLen) - var iv = Buffer.alloc(ivLen || 0) - var tmp = Buffer.alloc(0) - - while (keyLen > 0 || ivLen > 0) { - var hash = new MD5() - hash.update(tmp) - hash.update(password) - if (salt) hash.update(salt) - tmp = hash.digest() - - var used = 0 - - if (keyLen > 0) { - var keyStart = key.length - keyLen - used = Math.min(keyLen, tmp.length) - tmp.copy(key, keyStart, 0, used) - keyLen -= used - } - - if (used < tmp.length && ivLen > 0) { - var ivStart = iv.length - ivLen - var length = Math.min(ivLen, tmp.length - used) - tmp.copy(iv, ivStart, used, used + length) - ivLen -= length - } - } - - tmp.fill(0) - return { key: key, iv: iv } -} - -module.exports = EVP_BytesToKey - -},{"md5.js":221,"safe-buffer":250}],107:[function(require,module,exports){ -'use strict'; - -var isCallable = require('is-callable'); - -var toStr = Object.prototype.toString; -var hasOwnProperty = Object.prototype.hasOwnProperty; - -var forEachArray = function forEachArray(array, iterator, receiver) { - for (var i = 0, len = array.length; i < len; i++) { - if (hasOwnProperty.call(array, i)) { - if (receiver == null) { - iterator(array[i], i, array); - } else { - iterator.call(receiver, array[i], i, array); - } - } - } -}; - -var forEachString = function forEachString(string, iterator, receiver) { - for (var i = 0, len = string.length; i < len; i++) { - // no such thing as a sparse string. - if (receiver == null) { - iterator(string.charAt(i), i, string); - } else { - iterator.call(receiver, string.charAt(i), i, string); - } - } -}; - -var forEachObject = function forEachObject(object, iterator, receiver) { - for (var k in object) { - if (hasOwnProperty.call(object, k)) { - if (receiver == null) { - iterator(object[k], k, object); - } else { - iterator.call(receiver, object[k], k, object); - } - } - } -}; - -var forEach = function forEach(list, iterator, thisArg) { - if (!isCallable(iterator)) { - throw new TypeError('iterator must be a function'); - } - - var receiver; - if (arguments.length >= 3) { - receiver = thisArg; - } - - if (toStr.call(list) === '[object Array]') { - forEachArray(list, iterator, receiver); - } else if (typeof list === 'string') { - forEachString(list, iterator, receiver); - } else { - forEachObject(list, iterator, receiver); - } -}; - -module.exports = forEach; - -},{"is-callable":148}],108:[function(require,module,exports){ -'use strict'; - -/* eslint no-invalid-this: 1 */ - -var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; -var slice = Array.prototype.slice; -var toStr = Object.prototype.toString; -var funcType = '[object Function]'; - -module.exports = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr.call(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); - } - var args = slice.call(arguments, 1); - - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - args.concat(slice.call(arguments)) - ); - if (Object(result) === result) { - return result; - } - return this; - } else { - return target.apply( - that, - args.concat(slice.call(arguments)) - ); - } - }; - - var boundLength = Math.max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs.push('$' + i); - } - - bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); - - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; - } - - return bound; -}; - -},{}],109:[function(require,module,exports){ -'use strict'; - -var implementation = require('./implementation'); - -module.exports = Function.prototype.bind || implementation; - -},{"./implementation":108}],110:[function(require,module,exports){ -'use strict'; - -var undefined; - -var $SyntaxError = SyntaxError; -var $Function = Function; -var $TypeError = TypeError; - -// eslint-disable-next-line consistent-return -var getEvalledConstructor = function (expressionSyntax) { - try { - return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')(); - } catch (e) {} -}; - -var $gOPD = Object.getOwnPropertyDescriptor; -if ($gOPD) { - try { - $gOPD({}, ''); - } catch (e) { - $gOPD = null; // this is IE 8, which has a broken gOPD - } -} - -var throwTypeError = function () { - throw new $TypeError(); -}; -var ThrowTypeError = $gOPD - ? (function () { - try { - // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties - arguments.callee; // IE 8 does not throw here - return throwTypeError; - } catch (calleeThrows) { - try { - // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '') - return $gOPD(arguments, 'callee').get; - } catch (gOPDthrows) { - return throwTypeError; - } - } - }()) - : throwTypeError; - -var hasSymbols = require('has-symbols')(); - -var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto - -var needsEval = {}; - -var TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array); - -var INTRINSICS = { - '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError, - '%Array%': Array, - '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer, - '%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined, - '%AsyncFromSyncIteratorPrototype%': undefined, - '%AsyncFunction%': needsEval, - '%AsyncGenerator%': needsEval, - '%AsyncGeneratorFunction%': needsEval, - '%AsyncIteratorPrototype%': needsEval, - '%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics, - '%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt, - '%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array, - '%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array, - '%Boolean%': Boolean, - '%DataView%': typeof DataView === 'undefined' ? undefined : DataView, - '%Date%': Date, - '%decodeURI%': decodeURI, - '%decodeURIComponent%': decodeURIComponent, - '%encodeURI%': encodeURI, - '%encodeURIComponent%': encodeURIComponent, - '%Error%': Error, - '%eval%': eval, // eslint-disable-line no-eval - '%EvalError%': EvalError, - '%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array, - '%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array, - '%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry, - '%Function%': $Function, - '%GeneratorFunction%': needsEval, - '%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array, - '%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array, - '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array, - '%isFinite%': isFinite, - '%isNaN%': isNaN, - '%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined, - '%JSON%': typeof JSON === 'object' ? JSON : undefined, - '%Map%': typeof Map === 'undefined' ? undefined : Map, - '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()), - '%Math%': Math, - '%Number%': Number, - '%Object%': Object, - '%parseFloat%': parseFloat, - '%parseInt%': parseInt, - '%Promise%': typeof Promise === 'undefined' ? undefined : Promise, - '%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy, - '%RangeError%': RangeError, - '%ReferenceError%': ReferenceError, - '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect, - '%RegExp%': RegExp, - '%Set%': typeof Set === 'undefined' ? undefined : Set, - '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()), - '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer, - '%String%': String, - '%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined, - '%Symbol%': hasSymbols ? Symbol : undefined, - '%SyntaxError%': $SyntaxError, - '%ThrowTypeError%': ThrowTypeError, - '%TypedArray%': TypedArray, - '%TypeError%': $TypeError, - '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array, - '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, - '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array, - '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array, - '%URIError%': URIError, - '%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap, - '%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef, - '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet -}; - -try { - null.error; // eslint-disable-line no-unused-expressions -} catch (e) { - // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229 - var errorProto = getProto(getProto(e)); - INTRINSICS['%Error.prototype%'] = errorProto; -} - -var doEval = function doEval(name) { - var value; - if (name === '%AsyncFunction%') { - value = getEvalledConstructor('async function () {}'); - } else if (name === '%GeneratorFunction%') { - value = getEvalledConstructor('function* () {}'); - } else if (name === '%AsyncGeneratorFunction%') { - value = getEvalledConstructor('async function* () {}'); - } else if (name === '%AsyncGenerator%') { - var fn = doEval('%AsyncGeneratorFunction%'); - if (fn) { - value = fn.prototype; - } - } else if (name === '%AsyncIteratorPrototype%') { - var gen = doEval('%AsyncGenerator%'); - if (gen) { - value = getProto(gen.prototype); - } - } - - INTRINSICS[name] = value; - - return value; -}; - -var LEGACY_ALIASES = { - '%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'], - '%ArrayPrototype%': ['Array', 'prototype'], - '%ArrayProto_entries%': ['Array', 'prototype', 'entries'], - '%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'], - '%ArrayProto_keys%': ['Array', 'prototype', 'keys'], - '%ArrayProto_values%': ['Array', 'prototype', 'values'], - '%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'], - '%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'], - '%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'], - '%BooleanPrototype%': ['Boolean', 'prototype'], - '%DataViewPrototype%': ['DataView', 'prototype'], - '%DatePrototype%': ['Date', 'prototype'], - '%ErrorPrototype%': ['Error', 'prototype'], - '%EvalErrorPrototype%': ['EvalError', 'prototype'], - '%Float32ArrayPrototype%': ['Float32Array', 'prototype'], - '%Float64ArrayPrototype%': ['Float64Array', 'prototype'], - '%FunctionPrototype%': ['Function', 'prototype'], - '%Generator%': ['GeneratorFunction', 'prototype'], - '%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'], - '%Int8ArrayPrototype%': ['Int8Array', 'prototype'], - '%Int16ArrayPrototype%': ['Int16Array', 'prototype'], - '%Int32ArrayPrototype%': ['Int32Array', 'prototype'], - '%JSONParse%': ['JSON', 'parse'], - '%JSONStringify%': ['JSON', 'stringify'], - '%MapPrototype%': ['Map', 'prototype'], - '%NumberPrototype%': ['Number', 'prototype'], - '%ObjectPrototype%': ['Object', 'prototype'], - '%ObjProto_toString%': ['Object', 'prototype', 'toString'], - '%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'], - '%PromisePrototype%': ['Promise', 'prototype'], - '%PromiseProto_then%': ['Promise', 'prototype', 'then'], - '%Promise_all%': ['Promise', 'all'], - '%Promise_reject%': ['Promise', 'reject'], - '%Promise_resolve%': ['Promise', 'resolve'], - '%RangeErrorPrototype%': ['RangeError', 'prototype'], - '%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'], - '%RegExpPrototype%': ['RegExp', 'prototype'], - '%SetPrototype%': ['Set', 'prototype'], - '%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'], - '%StringPrototype%': ['String', 'prototype'], - '%SymbolPrototype%': ['Symbol', 'prototype'], - '%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'], - '%TypedArrayPrototype%': ['TypedArray', 'prototype'], - '%TypeErrorPrototype%': ['TypeError', 'prototype'], - '%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'], - '%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'], - '%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'], - '%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'], - '%URIErrorPrototype%': ['URIError', 'prototype'], - '%WeakMapPrototype%': ['WeakMap', 'prototype'], - '%WeakSetPrototype%': ['WeakSet', 'prototype'] -}; - -var bind = require('function-bind'); -var hasOwn = require('has'); -var $concat = bind.call(Function.call, Array.prototype.concat); -var $spliceApply = bind.call(Function.apply, Array.prototype.splice); -var $replace = bind.call(Function.call, String.prototype.replace); -var $strSlice = bind.call(Function.call, String.prototype.slice); -var $exec = bind.call(Function.call, RegExp.prototype.exec); - -/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */ -var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g; -var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */ -var stringToPath = function stringToPath(string) { - var first = $strSlice(string, 0, 1); - var last = $strSlice(string, -1); - if (first === '%' && last !== '%') { - throw new $SyntaxError('invalid intrinsic syntax, expected closing `%`'); - } else if (last === '%' && first !== '%') { - throw new $SyntaxError('invalid intrinsic syntax, expected opening `%`'); - } - var result = []; - $replace(string, rePropName, function (match, number, quote, subString) { - result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match; - }); - return result; -}; -/* end adaptation */ - -var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) { - var intrinsicName = name; - var alias; - if (hasOwn(LEGACY_ALIASES, intrinsicName)) { - alias = LEGACY_ALIASES[intrinsicName]; - intrinsicName = '%' + alias[0] + '%'; - } - - if (hasOwn(INTRINSICS, intrinsicName)) { - var value = INTRINSICS[intrinsicName]; - if (value === needsEval) { - value = doEval(intrinsicName); - } - if (typeof value === 'undefined' && !allowMissing) { - throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!'); - } - - return { - alias: alias, - name: intrinsicName, - value: value - }; - } - - throw new $SyntaxError('intrinsic ' + name + ' does not exist!'); -}; - -module.exports = function GetIntrinsic(name, allowMissing) { - if (typeof name !== 'string' || name.length === 0) { - throw new $TypeError('intrinsic name must be a non-empty string'); - } - if (arguments.length > 1 && typeof allowMissing !== 'boolean') { - throw new $TypeError('"allowMissing" argument must be a boolean'); - } - - if ($exec(/^%?[^%]*%?$/, name) === null) { - throw new $SyntaxError('`%` may not be present anywhere but at the beginning and end of the intrinsic name'); - } - var parts = stringToPath(name); - var intrinsicBaseName = parts.length > 0 ? parts[0] : ''; - - var intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing); - var intrinsicRealName = intrinsic.name; - var value = intrinsic.value; - var skipFurtherCaching = false; - - var alias = intrinsic.alias; - if (alias) { - intrinsicBaseName = alias[0]; - $spliceApply(parts, $concat([0, 1], alias)); - } - - for (var i = 1, isOwn = true; i < parts.length; i += 1) { - var part = parts[i]; - var first = $strSlice(part, 0, 1); - var last = $strSlice(part, -1); - if ( - ( - (first === '"' || first === "'" || first === '`') - || (last === '"' || last === "'" || last === '`') - ) - && first !== last - ) { - throw new $SyntaxError('property names with quotes must have matching quotes'); - } - if (part === 'constructor' || !isOwn) { - skipFurtherCaching = true; - } - - intrinsicBaseName += '.' + part; - intrinsicRealName = '%' + intrinsicBaseName + '%'; - - if (hasOwn(INTRINSICS, intrinsicRealName)) { - value = INTRINSICS[intrinsicRealName]; - } else if (value != null) { - if (!(part in value)) { - if (!allowMissing) { - throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.'); - } - return void undefined; - } - if ($gOPD && (i + 1) >= parts.length) { - var desc = $gOPD(value, part); - isOwn = !!desc; - - // By convention, when a data property is converted to an accessor - // property to emulate a data property that does not suffer from - // the override mistake, that accessor's getter is marked with - // an `originalValue` property. Here, when we detect this, we - // uphold the illusion by pretending to see that original data - // property, i.e., returning the value rather than the getter - // itself. - if (isOwn && 'get' in desc && !('originalValue' in desc.get)) { - value = desc.get; - } else { - value = value[part]; - } - } else { - isOwn = hasOwn(value, part); - value = value[part]; - } - - if (isOwn && !skipFurtherCaching) { - INTRINSICS[intrinsicRealName] = value; - } - } - } - return value; -}; - -},{"function-bind":109,"has":115,"has-symbols":112}],111:[function(require,module,exports){ -'use strict'; - -var GetIntrinsic = require('get-intrinsic'); - -var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true); - -if ($gOPD) { - try { - $gOPD([], 'length'); - } catch (e) { - // IE 8 has a broken gOPD - $gOPD = null; - } -} - -module.exports = $gOPD; - -},{"get-intrinsic":110}],112:[function(require,module,exports){ -'use strict'; - -var origSymbol = typeof Symbol !== 'undefined' && Symbol; -var hasSymbolSham = require('./shams'); - -module.exports = function hasNativeSymbols() { - if (typeof origSymbol !== 'function') { return false; } - if (typeof Symbol !== 'function') { return false; } - if (typeof origSymbol('foo') !== 'symbol') { return false; } - if (typeof Symbol('bar') !== 'symbol') { return false; } - - return hasSymbolSham(); -}; - -},{"./shams":113}],113:[function(require,module,exports){ -'use strict'; - -/* eslint complexity: [2, 18], max-statements: [2, 33] */ -module.exports = function hasSymbols() { - if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } - if (typeof Symbol.iterator === 'symbol') { return true; } - - var obj = {}; - var sym = Symbol('test'); - var symObj = Object(sym); - if (typeof sym === 'string') { return false; } - - if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } - if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } - - // temp disabled per https://github.com/ljharb/object.assign/issues/17 - // if (sym instanceof Symbol) { return false; } - // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 - // if (!(symObj instanceof Symbol)) { return false; } - - // if (typeof Symbol.prototype.toString !== 'function') { return false; } - // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } - - var symVal = 42; - obj[sym] = symVal; - for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop - if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } - - if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } - - var syms = Object.getOwnPropertySymbols(obj); - if (syms.length !== 1 || syms[0] !== sym) { return false; } - - if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } - - if (typeof Object.getOwnPropertyDescriptor === 'function') { - var descriptor = Object.getOwnPropertyDescriptor(obj, sym); - if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } - } - - return true; -}; - -},{}],114:[function(require,module,exports){ -'use strict'; - -var hasSymbols = require('has-symbols/shams'); - -module.exports = function hasToStringTagShams() { - return hasSymbols() && !!Symbol.toStringTag; -}; - -},{"has-symbols/shams":113}],115:[function(require,module,exports){ -'use strict'; - -var bind = require('function-bind'); - -module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty); - -},{"function-bind":109}],116:[function(require,module,exports){ -'use strict' -var Buffer = require('safe-buffer').Buffer -var Transform = require('readable-stream').Transform -var inherits = require('inherits') - -function throwIfNotStringOrBuffer (val, prefix) { - if (!Buffer.isBuffer(val) && typeof val !== 'string') { - throw new TypeError(prefix + ' must be a string or a buffer') - } -} - -function HashBase (blockSize) { - Transform.call(this) - - this._block = Buffer.allocUnsafe(blockSize) - this._blockSize = blockSize - this._blockOffset = 0 - this._length = [0, 0, 0, 0] - - this._finalized = false -} - -inherits(HashBase, Transform) - -HashBase.prototype._transform = function (chunk, encoding, callback) { - var error = null - try { - this.update(chunk, encoding) - } catch (err) { - error = err - } - - callback(error) -} - -HashBase.prototype._flush = function (callback) { - var error = null - try { - this.push(this.digest()) - } catch (err) { - error = err - } - - callback(error) -} - -HashBase.prototype.update = function (data, encoding) { - throwIfNotStringOrBuffer(data, 'Data') - if (this._finalized) throw new Error('Digest already called') - if (!Buffer.isBuffer(data)) data = Buffer.from(data, encoding) - - // consume data - var block = this._block - var offset = 0 - while (this._blockOffset + data.length - offset >= this._blockSize) { - for (var i = this._blockOffset; i < this._blockSize;) block[i++] = data[offset++] - this._update() - this._blockOffset = 0 - } - while (offset < data.length) block[this._blockOffset++] = data[offset++] - - // update length - for (var j = 0, carry = data.length * 8; carry > 0; ++j) { - this._length[j] += carry - carry = (this._length[j] / 0x0100000000) | 0 - if (carry > 0) this._length[j] -= 0x0100000000 * carry - } - - return this -} - -HashBase.prototype._update = function () { - throw new Error('_update is not implemented') -} - -HashBase.prototype.digest = function (encoding) { - if (this._finalized) throw new Error('Digest already called') - this._finalized = true - - var digest = this._digest() - if (encoding !== undefined) digest = digest.toString(encoding) - - // reset state - this._block.fill(0) - this._blockOffset = 0 - for (var i = 0; i < 4; ++i) this._length[i] = 0 - - return digest -} - -HashBase.prototype._digest = function () { - throw new Error('_digest is not implemented') -} - -module.exports = HashBase - -},{"inherits":146,"readable-stream":131,"safe-buffer":250}],117:[function(require,module,exports){ -arguments[4][50][0].apply(exports,arguments) -},{"dup":50}],118:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. -'use strict'; -/**/ - -var objectKeys = Object.keys || function (obj) { - var keys = []; - - for (var key in obj) { - keys.push(key); - } - - return keys; -}; -/**/ - - -module.exports = Duplex; - -var Readable = require('./_stream_readable'); - -var Writable = require('./_stream_writable'); - -require('inherits')(Duplex, Readable); - -{ - // Allow the keys array to be GC'ed. - var keys = objectKeys(Writable.prototype); - - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } -} - -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - Readable.call(this, options); - Writable.call(this, options); - this.allowHalfOpen = true; - - if (options) { - if (options.readable === false) this.readable = false; - if (options.writable === false) this.writable = false; - - if (options.allowHalfOpen === false) { - this.allowHalfOpen = false; - this.once('end', onend); - } - } -} - -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); -Object.defineProperty(Duplex.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); -Object.defineProperty(Duplex.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); // the no-half-open enforcer - -function onend() { - // If the writable side ended, then we're ok. - if (this._writableState.ended) return; // no more data can be written. - // But allow more writes to happen in this tick. - - process.nextTick(onEndNT, this); -} - -function onEndNT(self) { - self.end(); -} - -Object.defineProperty(Duplex.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined || this._writableState === undefined) { - return false; - } - - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); -}).call(this)}).call(this,require('_process')) - -},{"./_stream_readable":120,"./_stream_writable":122,"_process":237,"inherits":146}],119:[function(require,module,exports){ -arguments[4][52][0].apply(exports,arguments) -},{"./_stream_transform":121,"dup":52,"inherits":146}],120:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -'use strict'; - -module.exports = Readable; -/**/ - -var Duplex; -/**/ - -Readable.ReadableState = ReadableState; -/**/ - -var EE = require('events').EventEmitter; - -var EElistenerCount = function EElistenerCount(emitter, type) { - return emitter.listeners(type).length; -}; -/**/ - -/**/ - - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} -/**/ - - -var debugUtil = require('util'); - -var debug; - -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function debug() {}; -} -/**/ - - -var BufferList = require('./internal/streams/buffer_list'); - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance. - - -var StringDecoder; -var createReadableStreamAsyncIterator; -var from; - -require('inherits')(Readable, Stream); - -var errorOrDestroy = destroyImpl.errorOrDestroy; -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} - -function ReadableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - - this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - - this.sync = true; // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - this.paused = true; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish') - - this.autoDestroy = !!options.autoDestroy; // has it been destroyed - - this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s - - this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled - - this.readingMore = false; - this.decoder = null; - this.encoding = null; - - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} - -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside - // the ReadableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - this._readableState = new ReadableState(options, this, isDuplex); // legacy - - this.readable = true; - - if (options) { - if (typeof options.read === 'function') this._read = options.read; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - - Stream.call(this); -} - -Object.defineProperty(Readable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined) { - return false; - } - - return this._readableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - } -}); -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; - -Readable.prototype._destroy = function (err, cb) { - cb(err); -}; // Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. - - -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } - - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; // Unshift should *always* be something directly out of read() - - -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; - -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - debug('readableAddChunk', chunk); - var state = stream._readableState; - - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - - if (er) { - errorOrDestroy(stream, er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (addToFront) { - if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed) { - return false; - } else { - state.reading = false; - - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); - } - } // We can push more data if we are below the highWaterMark. - // Also, if we have no data yet, we can stand some more bytes. - // This is to work around cases where hwm=0, such as the repl. - - - return !state.ended && (state.length < state.highWaterMark || state.length === 0); -} - -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - state.awaitDrain = 0; - stream.emit('data', chunk); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - if (state.needReadable) emitReadable(stream); - } - - maybeReadMore(stream, state); -} - -function chunkInvalid(state, chunk) { - var er; - - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); - } - - return er; -} - -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; // backwards compatibility. - - -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - var decoder = new StringDecoder(enc); - this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8 - - this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers: - - var p = this._readableState.buffer.head; - var content = ''; - - while (p !== null) { - content += decoder.write(p.data); - p = p.next; - } - - this._readableState.buffer.clear(); - - if (content !== '') this._readableState.buffer.push(content); - this._readableState.length = content.length; - return this; -}; // Don't raise the hwm > 1GB - - -var MAX_HWM = 0x40000000; - -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - - return n; -} // This function is designed to be inlinable, so please take care when making -// changes to the function body. - - -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } // If we're asking for more than the current hwm, then raise the hwm. - - - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; // Don't have enough - - if (!state.ended) { - state.needReadable = true; - return 0; - } - - return state.length; -} // you can override either this method, or the async _read(n) below. - - -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - - if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - - n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. - - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - // if we need a readable event, then we need to do some reading. - - - var doRead = state.needReadable; - debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some - - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - - - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; // if the length is currently zero, then we *need* a readable event. - - if (state.length === 0) state.needReadable = true; // call internal read method - - this._read(state.highWaterMark); - - state.sync = false; // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - - if (!state.reading) n = howMuchToRead(nOrig, state); - } - - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; - - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n; - state.awaitDrain = 0; - } - - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. - - if (nOrig !== n && state.ended) endReadable(this); - } - - if (ret !== null) this.emit('data', ret); - return ret; -}; - -function onEofChunk(stream, state) { - debug('onEofChunk'); - if (state.ended) return; - - if (state.decoder) { - var chunk = state.decoder.end(); - - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - - state.ended = true; - - if (state.sync) { - // if we are sync, wait until next tick to emit the data. - // Otherwise we risk emitting data in the flow() - // the readable code triggers during a read() call - emitReadable(stream); - } else { - // emit 'readable' now to make sure it gets picked up. - state.needReadable = false; - - if (!state.emittedReadable) { - state.emittedReadable = true; - emitReadable_(stream); - } - } -} // Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. - - -function emitReadable(stream) { - var state = stream._readableState; - debug('emitReadable', state.needReadable, state.emittedReadable); - state.needReadable = false; - - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - process.nextTick(emitReadable_, stream); - } -} - -function emitReadable_(stream) { - var state = stream._readableState; - debug('emitReadable_', state.destroyed, state.length, state.ended); - - if (!state.destroyed && (state.length || state.ended)) { - stream.emit('readable'); - state.emittedReadable = false; - } // The stream needs another readable event if - // 1. It is not flowing, as the flow mechanism will take - // care of it. - // 2. It is not ended. - // 3. It is below the highWaterMark, so we can schedule - // another readable later. - - - state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; - flow(stream); -} // at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. - - -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - process.nextTick(maybeReadMore_, stream, state); - } -} - -function maybeReadMore_(stream, state) { - // Attempt to read more data if we should. - // - // The conditions for reading more data are (one of): - // - Not enough data buffered (state.length < state.highWaterMark). The loop - // is responsible for filling the buffer with enough data if such data - // is available. If highWaterMark is 0 and we are not in the flowing mode - // we should _not_ attempt to buffer any extra data. We'll get more data - // when the stream consumer calls read() instead. - // - No data in the buffer, and the stream is in flowing mode. In this mode - // the loop below is responsible for ensuring read() is called. Failing to - // call read here would abort the flow and there's no other mechanism for - // continuing the flow if the stream consumer has just subscribed to the - // 'data' event. - // - // In addition to the above conditions to keep reading data, the following - // conditions prevent the data from being read: - // - The stream has ended (state.ended). - // - There is already a pending 'read' operation (state.reading). This is a - // case where the the stream has called the implementation defined _read() - // method, but they are processing the call asynchronously and have _not_ - // called push() with new data. In this case we skip performing more - // read()s. The execution ends in this method again after the _read() ends - // up calling push() with more data. - while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { - var len = state.length; - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) // didn't get any data, stop spinning. - break; - } - - state.readingMore = false; -} // abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. - - -Readable.prototype._read = function (n) { - errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); -}; - -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - - case 1: - state.pipes = [state.pipes, dest]; - break; - - default: - state.pipes.push(dest); - break; - } - - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); - dest.on('unpipe', onunpipe); - - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - debug('onend'); - dest.end(); - } // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - - - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - var cleanedUp = false; - - function cleanup() { - debug('cleanup'); // cleanup event handlers once the pipe is broken - - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - cleanedUp = true; // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - - src.on('data', ondata); - - function ondata(chunk) { - debug('ondata'); - var ret = dest.write(chunk); - debug('dest.write', ret); - - if (ret === false) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', state.awaitDrain); - state.awaitDrain++; - } - - src.pause(); - } - } // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - - - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); - } // Make sure our error handler is attached before userland ones. - - - prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. - - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - - dest.once('close', onclose); - - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - - dest.once('finish', onfinish); - - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } // tell the dest that it's being piped to - - - dest.emit('pipe', src); // start the flow if it hasn't been started already. - - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } - - return dest; -}; - -function pipeOnDrain(src) { - return function pipeOnDrainFunctionResult() { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); - } - }; -} - -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { - hasUnpiped: false - }; // if we're not piping anywhere, then do nothing. - - if (state.pipesCount === 0) return this; // just one destination. most common case. - - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - if (!dest) dest = state.pipes; // got a match. - - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } // slow case. multiple pipe destinations. - - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, { - hasUnpiped: false - }); - } - - return this; - } // try to find the right one. - - - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - dest.emit('unpipe', this, unpipeInfo); - return this; -}; // set up data events if they are asked for -// Ensure readable listeners eventually get something - - -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - var state = this._readableState; - - if (ev === 'data') { - // update readableListening so that resume() may be a no-op - // a few lines down. This is needed to support once('readable'). - state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused - - if (state.flowing !== false) this.resume(); - } else if (ev === 'readable') { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - debug('on readable', state.length, state.reading); - - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - process.nextTick(nReadingNextTick, this); - } - } - } - - return res; -}; - -Readable.prototype.addListener = Readable.prototype.on; - -Readable.prototype.removeListener = function (ev, fn) { - var res = Stream.prototype.removeListener.call(this, ev, fn); - - if (ev === 'readable') { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; -}; - -Readable.prototype.removeAllListeners = function (ev) { - var res = Stream.prototype.removeAllListeners.apply(this, arguments); - - if (ev === 'readable' || ev === undefined) { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; -}; - -function updateReadableListening(self) { - var state = self._readableState; - state.readableListening = self.listenerCount('readable') > 0; - - if (state.resumeScheduled && !state.paused) { - // flowing needs to be set to true now, otherwise - // the upcoming resume will not flow. - state.flowing = true; // crude way to check if we should resume - } else if (self.listenerCount('data') > 0) { - self.resume(); - } -} - -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} // pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. - - -Readable.prototype.resume = function () { - var state = this._readableState; - - if (!state.flowing) { - debug('resume'); // we flow only if there is no one listening - // for readable, but we still have to call - // resume() - - state.flowing = !state.readableListening; - resume(this, state); - } - - state.paused = false; - return this; -}; - -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - process.nextTick(resume_, stream, state); - } -} - -function resume_(stream, state) { - debug('resume', state.reading); - - if (!state.reading) { - stream.read(0); - } - - state.resumeScheduled = false; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} - -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - - if (this._readableState.flowing !== false) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - - this._readableState.paused = true; - return this; -}; - -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - - while (state.flowing && stream.read() !== null) { - ; - } -} // wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. - - -Readable.prototype.wrap = function (stream) { - var _this = this; - - var state = this._readableState; - var paused = false; - stream.on('end', function () { - debug('wrapped end'); - - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); - } - - _this.push(null); - }); - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode - - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - - var ret = _this.push(chunk); - - if (!ret) { - paused = true; - stream.pause(); - } - }); // proxy all the other methods. - // important when wrapping filters and duplexes. - - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function methodWrap(method) { - return function methodWrapReturnFunction() { - return stream[method].apply(stream, arguments); - }; - }(i); - } - } // proxy certain important events. - - - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } // when we try to consume some more bytes, simply unpause the - // underlying stream. - - - this._read = function (n) { - debug('wrapped _read', n); - - if (paused) { - paused = false; - stream.resume(); - } - }; - - return this; -}; - -if (typeof Symbol === 'function') { - Readable.prototype[Symbol.asyncIterator] = function () { - if (createReadableStreamAsyncIterator === undefined) { - createReadableStreamAsyncIterator = require('./internal/streams/async_iterator'); - } - - return createReadableStreamAsyncIterator(this); - }; -} - -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.highWaterMark; - } -}); -Object.defineProperty(Readable.prototype, 'readableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState && this._readableState.buffer; - } -}); -Object.defineProperty(Readable.prototype, 'readableFlowing', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.flowing; - }, - set: function set(state) { - if (this._readableState) { - this._readableState.flowing = state; - } - } -}); // exposed for testing purposes only. - -Readable._fromList = fromList; -Object.defineProperty(Readable.prototype, 'readableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.length; - } -}); // Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. - -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = state.buffer.consume(n, state.decoder); - } - return ret; -} - -function endReadable(stream) { - var state = stream._readableState; - debug('endReadable', state.endEmitted); - - if (!state.endEmitted) { - state.ended = true; - process.nextTick(endReadableNT, state, stream); - } -} - -function endReadableNT(state, stream) { - debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift. - - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the writable side is ready for autoDestroy as well - var wState = stream._writableState; - - if (!wState || wState.autoDestroy && wState.finished) { - stream.destroy(); - } - } - } -} - -if (typeof Symbol === 'function') { - Readable.from = function (iterable, opts) { - if (from === undefined) { - from = require('./internal/streams/from'); - } - - return from(Readable, iterable, opts); - }; -} - -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - - return -1; -} -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../errors":117,"./_stream_duplex":118,"./internal/streams/async_iterator":123,"./internal/streams/buffer_list":124,"./internal/streams/destroy":125,"./internal/streams/from":127,"./internal/streams/state":129,"./internal/streams/stream":130,"_process":237,"buffer":68,"events":105,"inherits":146,"string_decoder/":279,"util":20}],121:[function(require,module,exports){ -arguments[4][54][0].apply(exports,arguments) -},{"../errors":117,"./_stream_duplex":118,"dup":54,"inherits":146}],122:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. -'use strict'; - -module.exports = Writable; -/* */ - -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} // It seems a linked list but it is not -// there will be only 2 of these for each stream - - -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* */ - -/**/ - - -var Duplex; -/**/ - -Writable.WritableState = WritableState; -/**/ - -var internalUtil = { - deprecate: require('util-deprecate') -}; -/**/ - -/**/ - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, - ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, - ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, - ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; - -var errorOrDestroy = destroyImpl.errorOrDestroy; - -require('inherits')(Writable, Stream); - -function nop() {} - -function WritableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream, - // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream - // contains buffers or objects. - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - - this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called - - this.finalCalled = false; // drain event flag. - - this.needDrain = false; // at the start of calling end() - - this.ending = false; // when end() has been called, and returned - - this.ended = false; // when 'finish' is emitted - - this.finished = false; // has it been destroyed - - this.destroyed = false; // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - - this.length = 0; // a flag to see when we're in the middle of a write. - - this.writing = false; // when true all writes will be buffered until .uncork() call - - this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - - this.sync = true; // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - - this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) - - this.onwrite = function (er) { - onwrite(stream, er); - }; // the callback that the user supplies to write(chunk,encoding,cb) - - - this.writecb = null; // the amount that is being written when _write is called. - - this.writelen = 0; - this.bufferedRequest = null; - this.lastBufferedRequest = null; // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - - this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - - this.prefinished = false; // True if the error was already emitted and should not be thrown again - - this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end') - - this.autoDestroy = !!options.autoDestroy; // count buffered requests - - this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - - this.corkedRequestsFree = new CorkedRequest(this); -} - -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - - while (current) { - out.push(current); - current = current.next; - } - - return out; -}; - -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function writableStateBufferGetter() { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); // Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. - - -var realHasInstance; - -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function value(object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function realHasInstance(object) { - return object instanceof this; - }; -} - -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - // Checking for a Stream.Duplex instance is faster here instead of inside - // the WritableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); - this._writableState = new WritableState(options, this, isDuplex); // legacy. - - this.writable = true; - - if (options) { - if (typeof options.write === 'function') this._write = options.write; - if (typeof options.writev === 'function') this._writev = options.writev; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - if (typeof options.final === 'function') this._final = options.final; - } - - Stream.call(this); -} // Otherwise people can pipe Writable streams, which is just wrong. - - -Writable.prototype.pipe = function () { - errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); -}; - -function writeAfterEnd(stream, cb) { - var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb - - errorOrDestroy(stream, er); - process.nextTick(cb, er); -} // Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. - - -function validChunk(stream, state, chunk, cb) { - var er; - - if (chunk === null) { - er = new ERR_STREAM_NULL_VALUES(); - } else if (typeof chunk !== 'string' && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); - } - - if (er) { - errorOrDestroy(stream, er); - process.nextTick(cb, er); - return false; - } - - return true; -} - -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - - var isBuf = !state.objectMode && _isUint8Array(chunk); - - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - if (typeof cb !== 'function') cb = nop; - if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - return ret; -}; - -Writable.prototype.cork = function () { - this._writableState.corked++; -}; - -Writable.prototype.uncork = function () { - var state = this._writableState; - - if (state.corked) { - state.corked--; - if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; - -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; - -Object.defineProperty(Writable.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); - -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - - return chunk; -} - -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); // if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. - -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - - var len = state.objectMode ? 1 : chunk.length; - state.length += len; - var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. - - if (!ret) state.needDrain = true; - - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - - return ret; -} - -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} - -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - process.nextTick(cb, er); // this can emit finish, and it will always happen - // after error - - process.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); // this can emit finish, but finish must - // always follow error - - finishMaybe(stream, state); - } -} - -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} - -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); - onwriteStateUpdate(state); - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state) || stream.destroyed; - - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - - if (sync) { - process.nextTick(afterWrite, stream, state, finished, cb); - } else { - afterWrite(stream, state, finished, cb); - } - } -} - -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} // Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. - - -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} // if there's something in the buffer waiting, then process it - - -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - var count = 0; - var allBuffers = true; - - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - - buffer.allBuffers = allBuffers; - doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - - state.pendingcb++; - state.lastBufferedRequest = null; - - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - - if (state.writing) { - break; - } - } - - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks - - if (state.corked) { - state.corked = 1; - this.uncork(); - } // ignore unnecessary end() calls. - - - if (!state.ending) endWritable(this, state, cb); - return this; -}; - -Object.defineProperty(Writable.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); - -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} - -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - - if (err) { - errorOrDestroy(stream, err); - } - - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} - -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function' && !state.destroyed) { - state.pendingcb++; - state.finalCalled = true; - process.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - - if (need) { - prefinish(stream, state); - - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the readable side is ready for autoDestroy as well - var rState = stream._readableState; - - if (!rState || rState.autoDestroy && rState.endEmitted) { - stream.destroy(); - } - } - } - } - - return need; -} - -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - - if (cb) { - if (state.finished) process.nextTick(cb);else stream.once('finish', cb); - } - - state.ended = true; - stream.writable = false; -} - -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } // reuse the free corkReq. - - - state.corkedRequestsFree.next = corkReq; -} - -Object.defineProperty(Writable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._writableState === undefined) { - return false; - } - - return this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._writableState.destroyed = value; - } -}); -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; - -Writable.prototype._destroy = function (err, cb) { - cb(err); -}; -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../errors":117,"./_stream_duplex":118,"./internal/streams/destroy":125,"./internal/streams/state":129,"./internal/streams/stream":130,"_process":237,"buffer":68,"inherits":146,"util-deprecate":283}],123:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; - -var _Object$setPrototypeO; - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -var finished = require('./end-of-stream'); - -var kLastResolve = Symbol('lastResolve'); -var kLastReject = Symbol('lastReject'); -var kError = Symbol('error'); -var kEnded = Symbol('ended'); -var kLastPromise = Symbol('lastPromise'); -var kHandlePromise = Symbol('handlePromise'); -var kStream = Symbol('stream'); - -function createIterResult(value, done) { - return { - value: value, - done: done - }; -} - -function readAndResolve(iter) { - var resolve = iter[kLastResolve]; - - if (resolve !== null) { - var data = iter[kStream].read(); // we defer if data is null - // we can be expecting either 'end' or - // 'error' - - if (data !== null) { - iter[kLastPromise] = null; - iter[kLastResolve] = null; - iter[kLastReject] = null; - resolve(createIterResult(data, false)); - } - } -} - -function onReadable(iter) { - // we wait for the next tick, because it might - // emit an error with process.nextTick - process.nextTick(readAndResolve, iter); -} - -function wrapForNext(lastPromise, iter) { - return function (resolve, reject) { - lastPromise.then(function () { - if (iter[kEnded]) { - resolve(createIterResult(undefined, true)); - return; - } - - iter[kHandlePromise](resolve, reject); - }, reject); - }; -} - -var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); -var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { - get stream() { - return this[kStream]; - }, - - next: function next() { - var _this = this; - - // if we have detected an error in the meanwhile - // reject straight away - var error = this[kError]; - - if (error !== null) { - return Promise.reject(error); - } - - if (this[kEnded]) { - return Promise.resolve(createIterResult(undefined, true)); - } - - if (this[kStream].destroyed) { - // We need to defer via nextTick because if .destroy(err) is - // called, the error will be emitted via nextTick, and - // we cannot guarantee that there is no error lingering around - // waiting to be emitted. - return new Promise(function (resolve, reject) { - process.nextTick(function () { - if (_this[kError]) { - reject(_this[kError]); - } else { - resolve(createIterResult(undefined, true)); - } - }); - }); - } // if we have multiple next() calls - // we will wait for the previous Promise to finish - // this logic is optimized to support for await loops, - // where next() is only called once at a time - - - var lastPromise = this[kLastPromise]; - var promise; - - if (lastPromise) { - promise = new Promise(wrapForNext(lastPromise, this)); - } else { - // fast path needed to support multiple this.push() - // without triggering the next() queue - var data = this[kStream].read(); - - if (data !== null) { - return Promise.resolve(createIterResult(data, false)); - } - - promise = new Promise(this[kHandlePromise]); - } - - this[kLastPromise] = promise; - return promise; - } -}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { - return this; -}), _defineProperty(_Object$setPrototypeO, "return", function _return() { - var _this2 = this; - - // destroy(err, cb) is a private API - // we can guarantee we have that here, because we control the - // Readable class this is attached to - return new Promise(function (resolve, reject) { - _this2[kStream].destroy(null, function (err) { - if (err) { - reject(err); - return; - } - - resolve(createIterResult(undefined, true)); - }); - }); -}), _Object$setPrototypeO), AsyncIteratorPrototype); - -var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { - var _Object$create; - - var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { - value: stream, - writable: true - }), _defineProperty(_Object$create, kLastResolve, { - value: null, - writable: true - }), _defineProperty(_Object$create, kLastReject, { - value: null, - writable: true - }), _defineProperty(_Object$create, kError, { - value: null, - writable: true - }), _defineProperty(_Object$create, kEnded, { - value: stream._readableState.endEmitted, - writable: true - }), _defineProperty(_Object$create, kHandlePromise, { - value: function value(resolve, reject) { - var data = iterator[kStream].read(); - - if (data) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(data, false)); - } else { - iterator[kLastResolve] = resolve; - iterator[kLastReject] = reject; - } - }, - writable: true - }), _Object$create)); - iterator[kLastPromise] = null; - finished(stream, function (err) { - if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { - var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise - // returned by next() and store the error - - if (reject !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - reject(err); - } - - iterator[kError] = err; - return; - } - - var resolve = iterator[kLastResolve]; - - if (resolve !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(undefined, true)); - } - - iterator[kEnded] = true; - }); - stream.on('readable', onReadable.bind(null, iterator)); - return iterator; -}; - -module.exports = createReadableStreamAsyncIterator; -}).call(this)}).call(this,require('_process')) - -},{"./end-of-stream":126,"_process":237}],124:[function(require,module,exports){ -arguments[4][57][0].apply(exports,arguments) -},{"buffer":68,"dup":57,"util":20}],125:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; // undocumented cb() API, needed for core, not for public API - -function destroy(err, cb) { - var _this = this; - - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; - - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err) { - if (!this._writableState) { - process.nextTick(emitErrorNT, this, err); - } else if (!this._writableState.errorEmitted) { - this._writableState.errorEmitted = true; - process.nextTick(emitErrorNT, this, err); - } - } - - return this; - } // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - - if (this._readableState) { - this._readableState.destroyed = true; - } // if this is a duplex stream mark the writable part as destroyed as well - - - if (this._writableState) { - this._writableState.destroyed = true; - } - - this._destroy(err || null, function (err) { - if (!cb && err) { - if (!_this._writableState) { - process.nextTick(emitErrorAndCloseNT, _this, err); - } else if (!_this._writableState.errorEmitted) { - _this._writableState.errorEmitted = true; - process.nextTick(emitErrorAndCloseNT, _this, err); - } else { - process.nextTick(emitCloseNT, _this); - } - } else if (cb) { - process.nextTick(emitCloseNT, _this); - cb(err); - } else { - process.nextTick(emitCloseNT, _this); - } - }); - - return this; -} - -function emitErrorAndCloseNT(self, err) { - emitErrorNT(self, err); - emitCloseNT(self); -} - -function emitCloseNT(self) { - if (self._writableState && !self._writableState.emitClose) return; - if (self._readableState && !self._readableState.emitClose) return; - self.emit('close'); -} - -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finalCalled = false; - this._writableState.prefinished = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} - -function emitErrorNT(self, err) { - self.emit('error', err); -} - -function errorOrDestroy(stream, err) { - // We have tests that rely on errors being emitted - // in the same tick, so changing this is semver major. - // For now when you opt-in to autoDestroy we allow - // the error to be emitted nextTick. In a future - // semver major update we should change the default to this. - var rState = stream._readableState; - var wState = stream._writableState; - if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); -} - -module.exports = { - destroy: destroy, - undestroy: undestroy, - errorOrDestroy: errorOrDestroy -}; -}).call(this)}).call(this,require('_process')) - -},{"_process":237}],126:[function(require,module,exports){ -arguments[4][59][0].apply(exports,arguments) -},{"../../../errors":117,"dup":59}],127:[function(require,module,exports){ -arguments[4][60][0].apply(exports,arguments) -},{"dup":60}],128:[function(require,module,exports){ -arguments[4][61][0].apply(exports,arguments) -},{"../../../errors":117,"./end-of-stream":126,"dup":61}],129:[function(require,module,exports){ -arguments[4][62][0].apply(exports,arguments) -},{"../../../errors":117,"dup":62}],130:[function(require,module,exports){ -arguments[4][63][0].apply(exports,arguments) -},{"dup":63,"events":105}],131:[function(require,module,exports){ -arguments[4][64][0].apply(exports,arguments) -},{"./lib/_stream_duplex.js":118,"./lib/_stream_passthrough.js":119,"./lib/_stream_readable.js":120,"./lib/_stream_transform.js":121,"./lib/_stream_writable.js":122,"./lib/internal/streams/end-of-stream.js":126,"./lib/internal/streams/pipeline.js":128,"dup":64}],132:[function(require,module,exports){ -var hash = exports; - -hash.utils = require('./hash/utils'); -hash.common = require('./hash/common'); -hash.sha = require('./hash/sha'); -hash.ripemd = require('./hash/ripemd'); -hash.hmac = require('./hash/hmac'); - -// Proxy hash functions to the main object -hash.sha1 = hash.sha.sha1; -hash.sha256 = hash.sha.sha256; -hash.sha224 = hash.sha.sha224; -hash.sha384 = hash.sha.sha384; -hash.sha512 = hash.sha.sha512; -hash.ripemd160 = hash.ripemd.ripemd160; - -},{"./hash/common":133,"./hash/hmac":134,"./hash/ripemd":135,"./hash/sha":136,"./hash/utils":143}],133:[function(require,module,exports){ -'use strict'; - -var utils = require('./utils'); -var assert = require('minimalistic-assert'); - -function BlockHash() { - this.pending = null; - this.pendingTotal = 0; - this.blockSize = this.constructor.blockSize; - this.outSize = this.constructor.outSize; - this.hmacStrength = this.constructor.hmacStrength; - this.padLength = this.constructor.padLength / 8; - this.endian = 'big'; - - this._delta8 = this.blockSize / 8; - this._delta32 = this.blockSize / 32; -} -exports.BlockHash = BlockHash; - -BlockHash.prototype.update = function update(msg, enc) { - // Convert message to array, pad it, and join into 32bit blocks - msg = utils.toArray(msg, enc); - if (!this.pending) - this.pending = msg; - else - this.pending = this.pending.concat(msg); - this.pendingTotal += msg.length; - - // Enough data, try updating - if (this.pending.length >= this._delta8) { - msg = this.pending; - - // Process pending data in blocks - var r = msg.length % this._delta8; - this.pending = msg.slice(msg.length - r, msg.length); - if (this.pending.length === 0) - this.pending = null; - - msg = utils.join32(msg, 0, msg.length - r, this.endian); - for (var i = 0; i < msg.length; i += this._delta32) - this._update(msg, i, i + this._delta32); - } - - return this; -}; - -BlockHash.prototype.digest = function digest(enc) { - this.update(this._pad()); - assert(this.pending === null); - - return this._digest(enc); -}; - -BlockHash.prototype._pad = function pad() { - var len = this.pendingTotal; - var bytes = this._delta8; - var k = bytes - ((len + this.padLength) % bytes); - var res = new Array(k + this.padLength); - res[0] = 0x80; - for (var i = 1; i < k; i++) - res[i] = 0; - - // Append length - len <<= 3; - if (this.endian === 'big') { - for (var t = 8; t < this.padLength; t++) - res[i++] = 0; - - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - res[i++] = (len >>> 24) & 0xff; - res[i++] = (len >>> 16) & 0xff; - res[i++] = (len >>> 8) & 0xff; - res[i++] = len & 0xff; - } else { - res[i++] = len & 0xff; - res[i++] = (len >>> 8) & 0xff; - res[i++] = (len >>> 16) & 0xff; - res[i++] = (len >>> 24) & 0xff; - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - res[i++] = 0; - - for (t = 8; t < this.padLength; t++) - res[i++] = 0; - } - - return res; -}; - -},{"./utils":143,"minimalistic-assert":223}],134:[function(require,module,exports){ -'use strict'; - -var utils = require('./utils'); -var assert = require('minimalistic-assert'); - -function Hmac(hash, key, enc) { - if (!(this instanceof Hmac)) - return new Hmac(hash, key, enc); - this.Hash = hash; - this.blockSize = hash.blockSize / 8; - this.outSize = hash.outSize / 8; - this.inner = null; - this.outer = null; - - this._init(utils.toArray(key, enc)); -} -module.exports = Hmac; - -Hmac.prototype._init = function init(key) { - // Shorten key, if needed - if (key.length > this.blockSize) - key = new this.Hash().update(key).digest(); - assert(key.length <= this.blockSize); - - // Add padding to key - for (var i = key.length; i < this.blockSize; i++) - key.push(0); - - for (i = 0; i < key.length; i++) - key[i] ^= 0x36; - this.inner = new this.Hash().update(key); - - // 0x36 ^ 0x5c = 0x6a - for (i = 0; i < key.length; i++) - key[i] ^= 0x6a; - this.outer = new this.Hash().update(key); -}; - -Hmac.prototype.update = function update(msg, enc) { - this.inner.update(msg, enc); - return this; -}; - -Hmac.prototype.digest = function digest(enc) { - this.outer.update(this.inner.digest()); - return this.outer.digest(enc); -}; - -},{"./utils":143,"minimalistic-assert":223}],135:[function(require,module,exports){ -'use strict'; - -var utils = require('./utils'); -var common = require('./common'); - -var rotl32 = utils.rotl32; -var sum32 = utils.sum32; -var sum32_3 = utils.sum32_3; -var sum32_4 = utils.sum32_4; -var BlockHash = common.BlockHash; - -function RIPEMD160() { - if (!(this instanceof RIPEMD160)) - return new RIPEMD160(); - - BlockHash.call(this); - - this.h = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]; - this.endian = 'little'; -} -utils.inherits(RIPEMD160, BlockHash); -exports.ripemd160 = RIPEMD160; - -RIPEMD160.blockSize = 512; -RIPEMD160.outSize = 160; -RIPEMD160.hmacStrength = 192; -RIPEMD160.padLength = 64; - -RIPEMD160.prototype._update = function update(msg, start) { - var A = this.h[0]; - var B = this.h[1]; - var C = this.h[2]; - var D = this.h[3]; - var E = this.h[4]; - var Ah = A; - var Bh = B; - var Ch = C; - var Dh = D; - var Eh = E; - for (var j = 0; j < 80; j++) { - var T = sum32( - rotl32( - sum32_4(A, f(j, B, C, D), msg[r[j] + start], K(j)), - s[j]), - E); - A = E; - E = D; - D = rotl32(C, 10); - C = B; - B = T; - T = sum32( - rotl32( - sum32_4(Ah, f(79 - j, Bh, Ch, Dh), msg[rh[j] + start], Kh(j)), - sh[j]), - Eh); - Ah = Eh; - Eh = Dh; - Dh = rotl32(Ch, 10); - Ch = Bh; - Bh = T; - } - T = sum32_3(this.h[1], C, Dh); - this.h[1] = sum32_3(this.h[2], D, Eh); - this.h[2] = sum32_3(this.h[3], E, Ah); - this.h[3] = sum32_3(this.h[4], A, Bh); - this.h[4] = sum32_3(this.h[0], B, Ch); - this.h[0] = T; -}; - -RIPEMD160.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'little'); - else - return utils.split32(this.h, 'little'); -}; - -function f(j, x, y, z) { - if (j <= 15) - return x ^ y ^ z; - else if (j <= 31) - return (x & y) | ((~x) & z); - else if (j <= 47) - return (x | (~y)) ^ z; - else if (j <= 63) - return (x & z) | (y & (~z)); - else - return x ^ (y | (~z)); -} - -function K(j) { - if (j <= 15) - return 0x00000000; - else if (j <= 31) - return 0x5a827999; - else if (j <= 47) - return 0x6ed9eba1; - else if (j <= 63) - return 0x8f1bbcdc; - else - return 0xa953fd4e; -} - -function Kh(j) { - if (j <= 15) - return 0x50a28be6; - else if (j <= 31) - return 0x5c4dd124; - else if (j <= 47) - return 0x6d703ef3; - else if (j <= 63) - return 0x7a6d76e9; - else - return 0x00000000; -} - -var r = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 -]; - -var rh = [ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 -]; - -var s = [ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 -]; - -var sh = [ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 -]; - -},{"./common":133,"./utils":143}],136:[function(require,module,exports){ -'use strict'; - -exports.sha1 = require('./sha/1'); -exports.sha224 = require('./sha/224'); -exports.sha256 = require('./sha/256'); -exports.sha384 = require('./sha/384'); -exports.sha512 = require('./sha/512'); - -},{"./sha/1":137,"./sha/224":138,"./sha/256":139,"./sha/384":140,"./sha/512":141}],137:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var common = require('../common'); -var shaCommon = require('./common'); - -var rotl32 = utils.rotl32; -var sum32 = utils.sum32; -var sum32_5 = utils.sum32_5; -var ft_1 = shaCommon.ft_1; -var BlockHash = common.BlockHash; - -var sha1_K = [ - 0x5A827999, 0x6ED9EBA1, - 0x8F1BBCDC, 0xCA62C1D6 -]; - -function SHA1() { - if (!(this instanceof SHA1)) - return new SHA1(); - - BlockHash.call(this); - this.h = [ - 0x67452301, 0xefcdab89, 0x98badcfe, - 0x10325476, 0xc3d2e1f0 ]; - this.W = new Array(80); -} - -utils.inherits(SHA1, BlockHash); -module.exports = SHA1; - -SHA1.blockSize = 512; -SHA1.outSize = 160; -SHA1.hmacStrength = 80; -SHA1.padLength = 64; - -SHA1.prototype._update = function _update(msg, start) { - var W = this.W; - - for (var i = 0; i < 16; i++) - W[i] = msg[start + i]; - - for(; i < W.length; i++) - W[i] = rotl32(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); - - var a = this.h[0]; - var b = this.h[1]; - var c = this.h[2]; - var d = this.h[3]; - var e = this.h[4]; - - for (i = 0; i < W.length; i++) { - var s = ~~(i / 20); - var t = sum32_5(rotl32(a, 5), ft_1(s, b, c, d), e, W[i], sha1_K[s]); - e = d; - d = c; - c = rotl32(b, 30); - b = a; - a = t; - } - - this.h[0] = sum32(this.h[0], a); - this.h[1] = sum32(this.h[1], b); - this.h[2] = sum32(this.h[2], c); - this.h[3] = sum32(this.h[3], d); - this.h[4] = sum32(this.h[4], e); -}; - -SHA1.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'big'); - else - return utils.split32(this.h, 'big'); -}; - -},{"../common":133,"../utils":143,"./common":142}],138:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var SHA256 = require('./256'); - -function SHA224() { - if (!(this instanceof SHA224)) - return new SHA224(); - - SHA256.call(this); - this.h = [ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ]; -} -utils.inherits(SHA224, SHA256); -module.exports = SHA224; - -SHA224.blockSize = 512; -SHA224.outSize = 224; -SHA224.hmacStrength = 192; -SHA224.padLength = 64; - -SHA224.prototype._digest = function digest(enc) { - // Just truncate output - if (enc === 'hex') - return utils.toHex32(this.h.slice(0, 7), 'big'); - else - return utils.split32(this.h.slice(0, 7), 'big'); -}; - - -},{"../utils":143,"./256":139}],139:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var common = require('../common'); -var shaCommon = require('./common'); -var assert = require('minimalistic-assert'); - -var sum32 = utils.sum32; -var sum32_4 = utils.sum32_4; -var sum32_5 = utils.sum32_5; -var ch32 = shaCommon.ch32; -var maj32 = shaCommon.maj32; -var s0_256 = shaCommon.s0_256; -var s1_256 = shaCommon.s1_256; -var g0_256 = shaCommon.g0_256; -var g1_256 = shaCommon.g1_256; - -var BlockHash = common.BlockHash; - -var sha256_K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -]; - -function SHA256() { - if (!(this instanceof SHA256)) - return new SHA256(); - - BlockHash.call(this); - this.h = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 - ]; - this.k = sha256_K; - this.W = new Array(64); -} -utils.inherits(SHA256, BlockHash); -module.exports = SHA256; - -SHA256.blockSize = 512; -SHA256.outSize = 256; -SHA256.hmacStrength = 192; -SHA256.padLength = 64; - -SHA256.prototype._update = function _update(msg, start) { - var W = this.W; - - for (var i = 0; i < 16; i++) - W[i] = msg[start + i]; - for (; i < W.length; i++) - W[i] = sum32_4(g1_256(W[i - 2]), W[i - 7], g0_256(W[i - 15]), W[i - 16]); - - var a = this.h[0]; - var b = this.h[1]; - var c = this.h[2]; - var d = this.h[3]; - var e = this.h[4]; - var f = this.h[5]; - var g = this.h[6]; - var h = this.h[7]; - - assert(this.k.length === W.length); - for (i = 0; i < W.length; i++) { - var T1 = sum32_5(h, s1_256(e), ch32(e, f, g), this.k[i], W[i]); - var T2 = sum32(s0_256(a), maj32(a, b, c)); - h = g; - g = f; - f = e; - e = sum32(d, T1); - d = c; - c = b; - b = a; - a = sum32(T1, T2); - } - - this.h[0] = sum32(this.h[0], a); - this.h[1] = sum32(this.h[1], b); - this.h[2] = sum32(this.h[2], c); - this.h[3] = sum32(this.h[3], d); - this.h[4] = sum32(this.h[4], e); - this.h[5] = sum32(this.h[5], f); - this.h[6] = sum32(this.h[6], g); - this.h[7] = sum32(this.h[7], h); -}; - -SHA256.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'big'); - else - return utils.split32(this.h, 'big'); -}; - -},{"../common":133,"../utils":143,"./common":142,"minimalistic-assert":223}],140:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); - -var SHA512 = require('./512'); - -function SHA384() { - if (!(this instanceof SHA384)) - return new SHA384(); - - SHA512.call(this); - this.h = [ - 0xcbbb9d5d, 0xc1059ed8, - 0x629a292a, 0x367cd507, - 0x9159015a, 0x3070dd17, - 0x152fecd8, 0xf70e5939, - 0x67332667, 0xffc00b31, - 0x8eb44a87, 0x68581511, - 0xdb0c2e0d, 0x64f98fa7, - 0x47b5481d, 0xbefa4fa4 ]; -} -utils.inherits(SHA384, SHA512); -module.exports = SHA384; - -SHA384.blockSize = 1024; -SHA384.outSize = 384; -SHA384.hmacStrength = 192; -SHA384.padLength = 128; - -SHA384.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h.slice(0, 12), 'big'); - else - return utils.split32(this.h.slice(0, 12), 'big'); -}; - -},{"../utils":143,"./512":141}],141:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var common = require('../common'); -var assert = require('minimalistic-assert'); - -var rotr64_hi = utils.rotr64_hi; -var rotr64_lo = utils.rotr64_lo; -var shr64_hi = utils.shr64_hi; -var shr64_lo = utils.shr64_lo; -var sum64 = utils.sum64; -var sum64_hi = utils.sum64_hi; -var sum64_lo = utils.sum64_lo; -var sum64_4_hi = utils.sum64_4_hi; -var sum64_4_lo = utils.sum64_4_lo; -var sum64_5_hi = utils.sum64_5_hi; -var sum64_5_lo = utils.sum64_5_lo; - -var BlockHash = common.BlockHash; - -var sha512_K = [ - 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, - 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, - 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, - 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, - 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, - 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, - 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, - 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, - 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, - 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, - 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, - 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, - 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, - 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, - 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, - 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, - 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, - 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, - 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, - 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, - 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, - 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, - 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, - 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, - 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, - 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, - 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, - 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, - 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, - 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, - 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, - 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, - 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, - 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, - 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, - 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, - 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, - 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, - 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, - 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 -]; - -function SHA512() { - if (!(this instanceof SHA512)) - return new SHA512(); - - BlockHash.call(this); - this.h = [ - 0x6a09e667, 0xf3bcc908, - 0xbb67ae85, 0x84caa73b, - 0x3c6ef372, 0xfe94f82b, - 0xa54ff53a, 0x5f1d36f1, - 0x510e527f, 0xade682d1, - 0x9b05688c, 0x2b3e6c1f, - 0x1f83d9ab, 0xfb41bd6b, - 0x5be0cd19, 0x137e2179 ]; - this.k = sha512_K; - this.W = new Array(160); -} -utils.inherits(SHA512, BlockHash); -module.exports = SHA512; - -SHA512.blockSize = 1024; -SHA512.outSize = 512; -SHA512.hmacStrength = 192; -SHA512.padLength = 128; - -SHA512.prototype._prepareBlock = function _prepareBlock(msg, start) { - var W = this.W; - - // 32 x 32bit words - for (var i = 0; i < 32; i++) - W[i] = msg[start + i]; - for (; i < W.length; i += 2) { - var c0_hi = g1_512_hi(W[i - 4], W[i - 3]); // i - 2 - var c0_lo = g1_512_lo(W[i - 4], W[i - 3]); - var c1_hi = W[i - 14]; // i - 7 - var c1_lo = W[i - 13]; - var c2_hi = g0_512_hi(W[i - 30], W[i - 29]); // i - 15 - var c2_lo = g0_512_lo(W[i - 30], W[i - 29]); - var c3_hi = W[i - 32]; // i - 16 - var c3_lo = W[i - 31]; - - W[i] = sum64_4_hi( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo); - W[i + 1] = sum64_4_lo( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo); - } -}; - -SHA512.prototype._update = function _update(msg, start) { - this._prepareBlock(msg, start); - - var W = this.W; - - var ah = this.h[0]; - var al = this.h[1]; - var bh = this.h[2]; - var bl = this.h[3]; - var ch = this.h[4]; - var cl = this.h[5]; - var dh = this.h[6]; - var dl = this.h[7]; - var eh = this.h[8]; - var el = this.h[9]; - var fh = this.h[10]; - var fl = this.h[11]; - var gh = this.h[12]; - var gl = this.h[13]; - var hh = this.h[14]; - var hl = this.h[15]; - - assert(this.k.length === W.length); - for (var i = 0; i < W.length; i += 2) { - var c0_hi = hh; - var c0_lo = hl; - var c1_hi = s1_512_hi(eh, el); - var c1_lo = s1_512_lo(eh, el); - var c2_hi = ch64_hi(eh, el, fh, fl, gh, gl); - var c2_lo = ch64_lo(eh, el, fh, fl, gh, gl); - var c3_hi = this.k[i]; - var c3_lo = this.k[i + 1]; - var c4_hi = W[i]; - var c4_lo = W[i + 1]; - - var T1_hi = sum64_5_hi( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo, - c4_hi, c4_lo); - var T1_lo = sum64_5_lo( - c0_hi, c0_lo, - c1_hi, c1_lo, - c2_hi, c2_lo, - c3_hi, c3_lo, - c4_hi, c4_lo); - - c0_hi = s0_512_hi(ah, al); - c0_lo = s0_512_lo(ah, al); - c1_hi = maj64_hi(ah, al, bh, bl, ch, cl); - c1_lo = maj64_lo(ah, al, bh, bl, ch, cl); - - var T2_hi = sum64_hi(c0_hi, c0_lo, c1_hi, c1_lo); - var T2_lo = sum64_lo(c0_hi, c0_lo, c1_hi, c1_lo); - - hh = gh; - hl = gl; - - gh = fh; - gl = fl; - - fh = eh; - fl = el; - - eh = sum64_hi(dh, dl, T1_hi, T1_lo); - el = sum64_lo(dl, dl, T1_hi, T1_lo); - - dh = ch; - dl = cl; - - ch = bh; - cl = bl; - - bh = ah; - bl = al; - - ah = sum64_hi(T1_hi, T1_lo, T2_hi, T2_lo); - al = sum64_lo(T1_hi, T1_lo, T2_hi, T2_lo); - } - - sum64(this.h, 0, ah, al); - sum64(this.h, 2, bh, bl); - sum64(this.h, 4, ch, cl); - sum64(this.h, 6, dh, dl); - sum64(this.h, 8, eh, el); - sum64(this.h, 10, fh, fl); - sum64(this.h, 12, gh, gl); - sum64(this.h, 14, hh, hl); -}; - -SHA512.prototype._digest = function digest(enc) { - if (enc === 'hex') - return utils.toHex32(this.h, 'big'); - else - return utils.split32(this.h, 'big'); -}; - -function ch64_hi(xh, xl, yh, yl, zh) { - var r = (xh & yh) ^ ((~xh) & zh); - if (r < 0) - r += 0x100000000; - return r; -} - -function ch64_lo(xh, xl, yh, yl, zh, zl) { - var r = (xl & yl) ^ ((~xl) & zl); - if (r < 0) - r += 0x100000000; - return r; -} - -function maj64_hi(xh, xl, yh, yl, zh) { - var r = (xh & yh) ^ (xh & zh) ^ (yh & zh); - if (r < 0) - r += 0x100000000; - return r; -} - -function maj64_lo(xh, xl, yh, yl, zh, zl) { - var r = (xl & yl) ^ (xl & zl) ^ (yl & zl); - if (r < 0) - r += 0x100000000; - return r; -} - -function s0_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 28); - var c1_hi = rotr64_hi(xl, xh, 2); // 34 - var c2_hi = rotr64_hi(xl, xh, 7); // 39 - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function s0_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 28); - var c1_lo = rotr64_lo(xl, xh, 2); // 34 - var c2_lo = rotr64_lo(xl, xh, 7); // 39 - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -function s1_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 14); - var c1_hi = rotr64_hi(xh, xl, 18); - var c2_hi = rotr64_hi(xl, xh, 9); // 41 - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function s1_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 14); - var c1_lo = rotr64_lo(xh, xl, 18); - var c2_lo = rotr64_lo(xl, xh, 9); // 41 - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -function g0_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 1); - var c1_hi = rotr64_hi(xh, xl, 8); - var c2_hi = shr64_hi(xh, xl, 7); - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function g0_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 1); - var c1_lo = rotr64_lo(xh, xl, 8); - var c2_lo = shr64_lo(xh, xl, 7); - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -function g1_512_hi(xh, xl) { - var c0_hi = rotr64_hi(xh, xl, 19); - var c1_hi = rotr64_hi(xl, xh, 29); // 61 - var c2_hi = shr64_hi(xh, xl, 6); - - var r = c0_hi ^ c1_hi ^ c2_hi; - if (r < 0) - r += 0x100000000; - return r; -} - -function g1_512_lo(xh, xl) { - var c0_lo = rotr64_lo(xh, xl, 19); - var c1_lo = rotr64_lo(xl, xh, 29); // 61 - var c2_lo = shr64_lo(xh, xl, 6); - - var r = c0_lo ^ c1_lo ^ c2_lo; - if (r < 0) - r += 0x100000000; - return r; -} - -},{"../common":133,"../utils":143,"minimalistic-assert":223}],142:[function(require,module,exports){ -'use strict'; - -var utils = require('../utils'); -var rotr32 = utils.rotr32; - -function ft_1(s, x, y, z) { - if (s === 0) - return ch32(x, y, z); - if (s === 1 || s === 3) - return p32(x, y, z); - if (s === 2) - return maj32(x, y, z); -} -exports.ft_1 = ft_1; - -function ch32(x, y, z) { - return (x & y) ^ ((~x) & z); -} -exports.ch32 = ch32; - -function maj32(x, y, z) { - return (x & y) ^ (x & z) ^ (y & z); -} -exports.maj32 = maj32; - -function p32(x, y, z) { - return x ^ y ^ z; -} -exports.p32 = p32; - -function s0_256(x) { - return rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22); -} -exports.s0_256 = s0_256; - -function s1_256(x) { - return rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25); -} -exports.s1_256 = s1_256; - -function g0_256(x) { - return rotr32(x, 7) ^ rotr32(x, 18) ^ (x >>> 3); -} -exports.g0_256 = g0_256; - -function g1_256(x) { - return rotr32(x, 17) ^ rotr32(x, 19) ^ (x >>> 10); -} -exports.g1_256 = g1_256; - -},{"../utils":143}],143:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); - -exports.inherits = inherits; - -function isSurrogatePair(msg, i) { - if ((msg.charCodeAt(i) & 0xFC00) !== 0xD800) { - return false; - } - if (i < 0 || i + 1 >= msg.length) { - return false; - } - return (msg.charCodeAt(i + 1) & 0xFC00) === 0xDC00; -} - -function toArray(msg, enc) { - if (Array.isArray(msg)) - return msg.slice(); - if (!msg) - return []; - var res = []; - if (typeof msg === 'string') { - if (!enc) { - // Inspired by stringToUtf8ByteArray() in closure-library by Google - // https://github.com/google/closure-library/blob/8598d87242af59aac233270742c8984e2b2bdbe0/closure/goog/crypt/crypt.js#L117-L143 - // Apache License 2.0 - // https://github.com/google/closure-library/blob/master/LICENSE - var p = 0; - for (var i = 0; i < msg.length; i++) { - var c = msg.charCodeAt(i); - if (c < 128) { - res[p++] = c; - } else if (c < 2048) { - res[p++] = (c >> 6) | 192; - res[p++] = (c & 63) | 128; - } else if (isSurrogatePair(msg, i)) { - c = 0x10000 + ((c & 0x03FF) << 10) + (msg.charCodeAt(++i) & 0x03FF); - res[p++] = (c >> 18) | 240; - res[p++] = ((c >> 12) & 63) | 128; - res[p++] = ((c >> 6) & 63) | 128; - res[p++] = (c & 63) | 128; - } else { - res[p++] = (c >> 12) | 224; - res[p++] = ((c >> 6) & 63) | 128; - res[p++] = (c & 63) | 128; - } - } - } else if (enc === 'hex') { - msg = msg.replace(/[^a-z0-9]+/ig, ''); - if (msg.length % 2 !== 0) - msg = '0' + msg; - for (i = 0; i < msg.length; i += 2) - res.push(parseInt(msg[i] + msg[i + 1], 16)); - } - } else { - for (i = 0; i < msg.length; i++) - res[i] = msg[i] | 0; - } - return res; -} -exports.toArray = toArray; - -function toHex(msg) { - var res = ''; - for (var i = 0; i < msg.length; i++) - res += zero2(msg[i].toString(16)); - return res; -} -exports.toHex = toHex; - -function htonl(w) { - var res = (w >>> 24) | - ((w >>> 8) & 0xff00) | - ((w << 8) & 0xff0000) | - ((w & 0xff) << 24); - return res >>> 0; -} -exports.htonl = htonl; - -function toHex32(msg, endian) { - var res = ''; - for (var i = 0; i < msg.length; i++) { - var w = msg[i]; - if (endian === 'little') - w = htonl(w); - res += zero8(w.toString(16)); - } - return res; -} -exports.toHex32 = toHex32; - -function zero2(word) { - if (word.length === 1) - return '0' + word; - else - return word; -} -exports.zero2 = zero2; - -function zero8(word) { - if (word.length === 7) - return '0' + word; - else if (word.length === 6) - return '00' + word; - else if (word.length === 5) - return '000' + word; - else if (word.length === 4) - return '0000' + word; - else if (word.length === 3) - return '00000' + word; - else if (word.length === 2) - return '000000' + word; - else if (word.length === 1) - return '0000000' + word; - else - return word; -} -exports.zero8 = zero8; - -function join32(msg, start, end, endian) { - var len = end - start; - assert(len % 4 === 0); - var res = new Array(len / 4); - for (var i = 0, k = start; i < res.length; i++, k += 4) { - var w; - if (endian === 'big') - w = (msg[k] << 24) | (msg[k + 1] << 16) | (msg[k + 2] << 8) | msg[k + 3]; - else - w = (msg[k + 3] << 24) | (msg[k + 2] << 16) | (msg[k + 1] << 8) | msg[k]; - res[i] = w >>> 0; - } - return res; -} -exports.join32 = join32; - -function split32(msg, endian) { - var res = new Array(msg.length * 4); - for (var i = 0, k = 0; i < msg.length; i++, k += 4) { - var m = msg[i]; - if (endian === 'big') { - res[k] = m >>> 24; - res[k + 1] = (m >>> 16) & 0xff; - res[k + 2] = (m >>> 8) & 0xff; - res[k + 3] = m & 0xff; - } else { - res[k + 3] = m >>> 24; - res[k + 2] = (m >>> 16) & 0xff; - res[k + 1] = (m >>> 8) & 0xff; - res[k] = m & 0xff; - } - } - return res; -} -exports.split32 = split32; - -function rotr32(w, b) { - return (w >>> b) | (w << (32 - b)); -} -exports.rotr32 = rotr32; - -function rotl32(w, b) { - return (w << b) | (w >>> (32 - b)); -} -exports.rotl32 = rotl32; - -function sum32(a, b) { - return (a + b) >>> 0; -} -exports.sum32 = sum32; - -function sum32_3(a, b, c) { - return (a + b + c) >>> 0; -} -exports.sum32_3 = sum32_3; - -function sum32_4(a, b, c, d) { - return (a + b + c + d) >>> 0; -} -exports.sum32_4 = sum32_4; - -function sum32_5(a, b, c, d, e) { - return (a + b + c + d + e) >>> 0; -} -exports.sum32_5 = sum32_5; - -function sum64(buf, pos, ah, al) { - var bh = buf[pos]; - var bl = buf[pos + 1]; - - var lo = (al + bl) >>> 0; - var hi = (lo < al ? 1 : 0) + ah + bh; - buf[pos] = hi >>> 0; - buf[pos + 1] = lo; -} -exports.sum64 = sum64; - -function sum64_hi(ah, al, bh, bl) { - var lo = (al + bl) >>> 0; - var hi = (lo < al ? 1 : 0) + ah + bh; - return hi >>> 0; -} -exports.sum64_hi = sum64_hi; - -function sum64_lo(ah, al, bh, bl) { - var lo = al + bl; - return lo >>> 0; -} -exports.sum64_lo = sum64_lo; - -function sum64_4_hi(ah, al, bh, bl, ch, cl, dh, dl) { - var carry = 0; - var lo = al; - lo = (lo + bl) >>> 0; - carry += lo < al ? 1 : 0; - lo = (lo + cl) >>> 0; - carry += lo < cl ? 1 : 0; - lo = (lo + dl) >>> 0; - carry += lo < dl ? 1 : 0; - - var hi = ah + bh + ch + dh + carry; - return hi >>> 0; -} -exports.sum64_4_hi = sum64_4_hi; - -function sum64_4_lo(ah, al, bh, bl, ch, cl, dh, dl) { - var lo = al + bl + cl + dl; - return lo >>> 0; -} -exports.sum64_4_lo = sum64_4_lo; - -function sum64_5_hi(ah, al, bh, bl, ch, cl, dh, dl, eh, el) { - var carry = 0; - var lo = al; - lo = (lo + bl) >>> 0; - carry += lo < al ? 1 : 0; - lo = (lo + cl) >>> 0; - carry += lo < cl ? 1 : 0; - lo = (lo + dl) >>> 0; - carry += lo < dl ? 1 : 0; - lo = (lo + el) >>> 0; - carry += lo < el ? 1 : 0; - - var hi = ah + bh + ch + dh + eh + carry; - return hi >>> 0; -} -exports.sum64_5_hi = sum64_5_hi; - -function sum64_5_lo(ah, al, bh, bl, ch, cl, dh, dl, eh, el) { - var lo = al + bl + cl + dl + el; - - return lo >>> 0; -} -exports.sum64_5_lo = sum64_5_lo; - -function rotr64_hi(ah, al, num) { - var r = (al << (32 - num)) | (ah >>> num); - return r >>> 0; -} -exports.rotr64_hi = rotr64_hi; - -function rotr64_lo(ah, al, num) { - var r = (ah << (32 - num)) | (al >>> num); - return r >>> 0; -} -exports.rotr64_lo = rotr64_lo; - -function shr64_hi(ah, al, num) { - return ah >>> num; -} -exports.shr64_hi = shr64_hi; - -function shr64_lo(ah, al, num) { - var r = (ah << (32 - num)) | (al >>> num); - return r >>> 0; -} -exports.shr64_lo = shr64_lo; - -},{"inherits":146,"minimalistic-assert":223}],144:[function(require,module,exports){ -'use strict'; - -var hash = require('hash.js'); -var utils = require('minimalistic-crypto-utils'); -var assert = require('minimalistic-assert'); - -function HmacDRBG(options) { - if (!(this instanceof HmacDRBG)) - return new HmacDRBG(options); - this.hash = options.hash; - this.predResist = !!options.predResist; - - this.outLen = this.hash.outSize; - this.minEntropy = options.minEntropy || this.hash.hmacStrength; - - this._reseed = null; - this.reseedInterval = null; - this.K = null; - this.V = null; - - var entropy = utils.toArray(options.entropy, options.entropyEnc || 'hex'); - var nonce = utils.toArray(options.nonce, options.nonceEnc || 'hex'); - var pers = utils.toArray(options.pers, options.persEnc || 'hex'); - assert(entropy.length >= (this.minEntropy / 8), - 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); - this._init(entropy, nonce, pers); -} -module.exports = HmacDRBG; - -HmacDRBG.prototype._init = function init(entropy, nonce, pers) { - var seed = entropy.concat(nonce).concat(pers); - - this.K = new Array(this.outLen / 8); - this.V = new Array(this.outLen / 8); - for (var i = 0; i < this.V.length; i++) { - this.K[i] = 0x00; - this.V[i] = 0x01; - } - - this._update(seed); - this._reseed = 1; - this.reseedInterval = 0x1000000000000; // 2^48 -}; - -HmacDRBG.prototype._hmac = function hmac() { - return new hash.hmac(this.hash, this.K); -}; - -HmacDRBG.prototype._update = function update(seed) { - var kmac = this._hmac() - .update(this.V) - .update([ 0x00 ]); - if (seed) - kmac = kmac.update(seed); - this.K = kmac.digest(); - this.V = this._hmac().update(this.V).digest(); - if (!seed) - return; - - this.K = this._hmac() - .update(this.V) - .update([ 0x01 ]) - .update(seed) - .digest(); - this.V = this._hmac().update(this.V).digest(); -}; - -HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) { - // Optional entropy enc - if (typeof entropyEnc !== 'string') { - addEnc = add; - add = entropyEnc; - entropyEnc = null; - } - - entropy = utils.toArray(entropy, entropyEnc); - add = utils.toArray(add, addEnc); - - assert(entropy.length >= (this.minEntropy / 8), - 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); - - this._update(entropy.concat(add || [])); - this._reseed = 1; -}; - -HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) { - if (this._reseed > this.reseedInterval) - throw new Error('Reseed is required'); - - // Optional encoding - if (typeof enc !== 'string') { - addEnc = add; - add = enc; - enc = null; - } - - // Optional additional data - if (add) { - add = utils.toArray(add, addEnc || 'hex'); - this._update(add); - } - - var temp = []; - while (temp.length < len) { - this.V = this._hmac().update(this.V).digest(); - temp = temp.concat(this.V); - } - - var res = temp.slice(0, len); - this._update(add); - this._reseed++; - return utils.encode(res, enc); -}; - -},{"hash.js":132,"minimalistic-assert":223,"minimalistic-crypto-utils":224}],145:[function(require,module,exports){ -/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] - - i += d - - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -} - -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - - value = Math.abs(value) - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } - - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = ((value * c) - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128 -} - -},{}],146:[function(require,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - if (superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }) - } - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - if (superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } - } -} - -},{}],147:[function(require,module,exports){ -'use strict'; - -var hasToStringTag = require('has-tostringtag/shams')(); -var callBound = require('call-bind/callBound'); - -var $toString = callBound('Object.prototype.toString'); - -var isStandardArguments = function isArguments(value) { - if (hasToStringTag && value && typeof value === 'object' && Symbol.toStringTag in value) { - return false; - } - return $toString(value) === '[object Arguments]'; -}; - -var isLegacyArguments = function isArguments(value) { - if (isStandardArguments(value)) { - return true; - } - return value !== null && - typeof value === 'object' && - typeof value.length === 'number' && - value.length >= 0 && - $toString(value) !== '[object Array]' && - $toString(value.callee) === '[object Function]'; -}; - -var supportsStandardArguments = (function () { - return isStandardArguments(arguments); -}()); - -isStandardArguments.isLegacyArguments = isLegacyArguments; // for tests - -module.exports = supportsStandardArguments ? isStandardArguments : isLegacyArguments; - -},{"call-bind/callBound":69,"has-tostringtag/shams":114}],148:[function(require,module,exports){ -'use strict'; - -var fnToStr = Function.prototype.toString; -var reflectApply = typeof Reflect === 'object' && Reflect !== null && Reflect.apply; -var badArrayLike; -var isCallableMarker; -if (typeof reflectApply === 'function' && typeof Object.defineProperty === 'function') { - try { - badArrayLike = Object.defineProperty({}, 'length', { - get: function () { - throw isCallableMarker; - } - }); - isCallableMarker = {}; - // eslint-disable-next-line no-throw-literal - reflectApply(function () { throw 42; }, null, badArrayLike); - } catch (_) { - if (_ !== isCallableMarker) { - reflectApply = null; - } - } -} else { - reflectApply = null; -} - -var constructorRegex = /^\s*class\b/; -var isES6ClassFn = function isES6ClassFunction(value) { - try { - var fnStr = fnToStr.call(value); - return constructorRegex.test(fnStr); - } catch (e) { - return false; // not a function - } -}; - -var tryFunctionObject = function tryFunctionToStr(value) { - try { - if (isES6ClassFn(value)) { return false; } - fnToStr.call(value); - return true; - } catch (e) { - return false; - } -}; -var toStr = Object.prototype.toString; -var objectClass = '[object Object]'; -var fnClass = '[object Function]'; -var genClass = '[object GeneratorFunction]'; -var ddaClass = '[object HTMLAllCollection]'; // IE 11 -var ddaClass2 = '[object HTML document.all class]'; -var ddaClass3 = '[object HTMLCollection]'; // IE 9-10 -var hasToStringTag = typeof Symbol === 'function' && !!Symbol.toStringTag; // better: use `has-tostringtag` - -var isIE68 = !(0 in [,]); // eslint-disable-line no-sparse-arrays, comma-spacing - -var isDDA = function isDocumentDotAll() { return false; }; -if (typeof document === 'object') { - // Firefox 3 canonicalizes DDA to undefined when it's not accessed directly - var all = document.all; - if (toStr.call(all) === toStr.call(document.all)) { - isDDA = function isDocumentDotAll(value) { - /* globals document: false */ - // in IE 6-8, typeof document.all is "object" and it's truthy - if ((isIE68 || !value) && (typeof value === 'undefined' || typeof value === 'object')) { - try { - var str = toStr.call(value); - return ( - str === ddaClass - || str === ddaClass2 - || str === ddaClass3 // opera 12.16 - || str === objectClass // IE 6-8 - ) && value('') == null; // eslint-disable-line eqeqeq - } catch (e) { /**/ } - } - return false; - }; - } -} - -module.exports = reflectApply - ? function isCallable(value) { - if (isDDA(value)) { return true; } - if (!value) { return false; } - if (typeof value !== 'function' && typeof value !== 'object') { return false; } - try { - reflectApply(value, null, badArrayLike); - } catch (e) { - if (e !== isCallableMarker) { return false; } - } - return !isES6ClassFn(value) && tryFunctionObject(value); - } - : function isCallable(value) { - if (isDDA(value)) { return true; } - if (!value) { return false; } - if (typeof value !== 'function' && typeof value !== 'object') { return false; } - if (hasToStringTag) { return tryFunctionObject(value); } - if (isES6ClassFn(value)) { return false; } - var strClass = toStr.call(value); - if (strClass !== fnClass && strClass !== genClass && !(/^\[object HTML/).test(strClass)) { return false; } - return tryFunctionObject(value); - }; - -},{}],149:[function(require,module,exports){ -'use strict'; - -var toStr = Object.prototype.toString; -var fnToStr = Function.prototype.toString; -var isFnRegex = /^\s*(?:function)?\*/; -var hasToStringTag = require('has-tostringtag/shams')(); -var getProto = Object.getPrototypeOf; -var getGeneratorFunc = function () { // eslint-disable-line consistent-return - if (!hasToStringTag) { - return false; - } - try { - return Function('return function*() {}')(); - } catch (e) { - } -}; -var GeneratorFunction; - -module.exports = function isGeneratorFunction(fn) { - if (typeof fn !== 'function') { - return false; - } - if (isFnRegex.test(fnToStr.call(fn))) { - return true; - } - if (!hasToStringTag) { - var str = toStr.call(fn); - return str === '[object GeneratorFunction]'; - } - if (!getProto) { - return false; - } - if (typeof GeneratorFunction === 'undefined') { - var generatorFunc = getGeneratorFunc(); - GeneratorFunction = generatorFunc ? getProto(generatorFunc) : false; - } - return getProto(fn) === GeneratorFunction; -}; - -},{"has-tostringtag/shams":114}],150:[function(require,module,exports){ -(function (global){(function (){ -'use strict'; - -var forEach = require('for-each'); -var availableTypedArrays = require('available-typed-arrays'); -var callBound = require('call-bind/callBound'); - -var $toString = callBound('Object.prototype.toString'); -var hasToStringTag = require('has-tostringtag/shams')(); -var gOPD = require('gopd'); - -var g = typeof globalThis === 'undefined' ? global : globalThis; -var typedArrays = availableTypedArrays(); - -var $indexOf = callBound('Array.prototype.indexOf', true) || function indexOf(array, value) { - for (var i = 0; i < array.length; i += 1) { - if (array[i] === value) { - return i; - } - } - return -1; -}; -var $slice = callBound('String.prototype.slice'); -var toStrTags = {}; -var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof'); -if (hasToStringTag && gOPD && getPrototypeOf) { - forEach(typedArrays, function (typedArray) { - var arr = new g[typedArray](); - if (Symbol.toStringTag in arr) { - var proto = getPrototypeOf(arr); - var descriptor = gOPD(proto, Symbol.toStringTag); - if (!descriptor) { - var superProto = getPrototypeOf(proto); - descriptor = gOPD(superProto, Symbol.toStringTag); - } - toStrTags[typedArray] = descriptor.get; - } - }); -} - -var tryTypedArrays = function tryAllTypedArrays(value) { - var anyTrue = false; - forEach(toStrTags, function (getter, typedArray) { - if (!anyTrue) { - try { - anyTrue = getter.call(value) === typedArray; - } catch (e) { /**/ } - } - }); - return anyTrue; -}; - -module.exports = function isTypedArray(value) { - if (!value || typeof value !== 'object') { return false; } - if (!hasToStringTag || !(Symbol.toStringTag in value)) { - var tag = $slice($toString(value), 8, -1); - return $indexOf(typedArrays, tag) > -1; - } - if (!gOPD) { return false; } - return tryTypedArrays(value); -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"available-typed-arrays":16,"call-bind/callBound":69,"for-each":107,"gopd":111,"has-tostringtag/shams":114}],151:[function(require,module,exports){ -/* -* loglevel - https://github.com/pimterry/loglevel -* -* Copyright (c) 2013 Tim Perry -* Licensed under the MIT license. -*/ -(function (root, definition) { - "use strict"; - if (typeof define === 'function' && define.amd) { - define(definition); - } else if (typeof module === 'object' && module.exports) { - module.exports = definition(); - } else { - root.log = definition(); - } -}(this, function () { - "use strict"; - - // Slightly dubious tricks to cut down minimized file size - var noop = function() {}; - var undefinedType = "undefined"; - var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && ( - /Trident\/|MSIE /.test(window.navigator.userAgent) - ); - - var logMethods = [ - "trace", - "debug", - "info", - "warn", - "error" - ]; - - // Cross-browser bind equivalent that works at least back to IE6 - function bindMethod(obj, methodName) { - var method = obj[methodName]; - if (typeof method.bind === 'function') { - return method.bind(obj); - } else { - try { - return Function.prototype.bind.call(method, obj); - } catch (e) { - // Missing bind shim or IE8 + Modernizr, fallback to wrapping - return function() { - return Function.prototype.apply.apply(method, [obj, arguments]); - }; - } - } - } - - // Trace() doesn't print the message in IE, so for that case we need to wrap it - function traceForIE() { - if (console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - // In old IE, native console methods themselves don't have apply(). - Function.prototype.apply.apply(console.log, [console, arguments]); - } - } - if (console.trace) console.trace(); - } - - // Build the best logging method possible for this env - // Wherever possible we want to bind, not wrap, to preserve stack traces - function realMethod(methodName) { - if (methodName === 'debug') { - methodName = 'log'; - } - - if (typeof console === undefinedType) { - return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives - } else if (methodName === 'trace' && isIE) { - return traceForIE; - } else if (console[methodName] !== undefined) { - return bindMethod(console, methodName); - } else if (console.log !== undefined) { - return bindMethod(console, 'log'); - } else { - return noop; - } - } - - // These private functions always need `this` to be set properly - - function replaceLoggingMethods(level, loggerName) { - /*jshint validthis:true */ - for (var i = 0; i < logMethods.length; i++) { - var methodName = logMethods[i]; - this[methodName] = (i < level) ? - noop : - this.methodFactory(methodName, level, loggerName); - } - - // Define log.log as an alias for log.debug - this.log = this.debug; - } - - // In old IE versions, the console isn't present until you first open it. - // We build realMethod() replacements here that regenerate logging methods - function enableLoggingWhenConsoleArrives(methodName, level, loggerName) { - return function () { - if (typeof console !== undefinedType) { - replaceLoggingMethods.call(this, level, loggerName); - this[methodName].apply(this, arguments); - } - }; - } - - // By default, we use closely bound real methods wherever possible, and - // otherwise we wait for a console to appear, and then try again. - function defaultMethodFactory(methodName, level, loggerName) { - /*jshint validthis:true */ - return realMethod(methodName) || - enableLoggingWhenConsoleArrives.apply(this, arguments); - } - - function Logger(name, defaultLevel, factory) { - var self = this; - var currentLevel; - defaultLevel = defaultLevel == null ? "WARN" : defaultLevel; - - var storageKey = "loglevel"; - if (typeof name === "string") { - storageKey += ":" + name; - } else if (typeof name === "symbol") { - storageKey = undefined; - } - - function persistLevelIfPossible(levelNum) { - var levelName = (logMethods[levelNum] || 'silent').toUpperCase(); - - if (typeof window === undefinedType || !storageKey) return; - - // Use localStorage if available - try { - window.localStorage[storageKey] = levelName; - return; - } catch (ignore) {} - - // Use session cookie as fallback - try { - window.document.cookie = - encodeURIComponent(storageKey) + "=" + levelName + ";"; - } catch (ignore) {} - } - - function getPersistedLevel() { - var storedLevel; - - if (typeof window === undefinedType || !storageKey) return; - - try { - storedLevel = window.localStorage[storageKey]; - } catch (ignore) {} - - // Fallback to cookies if local storage gives us nothing - if (typeof storedLevel === undefinedType) { - try { - var cookie = window.document.cookie; - var location = cookie.indexOf( - encodeURIComponent(storageKey) + "="); - if (location !== -1) { - storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1]; - } - } catch (ignore) {} - } - - // If the stored level is not valid, treat it as if nothing was stored. - if (self.levels[storedLevel] === undefined) { - storedLevel = undefined; - } - - return storedLevel; - } - - function clearPersistedLevel() { - if (typeof window === undefinedType || !storageKey) return; - - // Use localStorage if available - try { - window.localStorage.removeItem(storageKey); - return; - } catch (ignore) {} - - // Use session cookie as fallback - try { - window.document.cookie = - encodeURIComponent(storageKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC"; - } catch (ignore) {} - } - - /* - * - * Public logger API - see https://github.com/pimterry/loglevel for details - * - */ - - self.name = name; - - self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3, - "ERROR": 4, "SILENT": 5}; - - self.methodFactory = factory || defaultMethodFactory; - - self.getLevel = function () { - return currentLevel; - }; - - self.setLevel = function (level, persist) { - if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) { - level = self.levels[level.toUpperCase()]; - } - if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) { - currentLevel = level; - if (persist !== false) { // defaults to true - persistLevelIfPossible(level); - } - replaceLoggingMethods.call(self, level, name); - if (typeof console === undefinedType && level < self.levels.SILENT) { - return "No console available for logging"; - } - } else { - throw "log.setLevel() called with invalid level: " + level; - } - }; - - self.setDefaultLevel = function (level) { - defaultLevel = level; - if (!getPersistedLevel()) { - self.setLevel(level, false); - } - }; - - self.resetLevel = function () { - self.setLevel(defaultLevel, false); - clearPersistedLevel(); - }; - - self.enableAll = function(persist) { - self.setLevel(self.levels.TRACE, persist); - }; - - self.disableAll = function(persist) { - self.setLevel(self.levels.SILENT, persist); - }; - - // Initialize with the right level - var initialLevel = getPersistedLevel(); - if (initialLevel == null) { - initialLevel = defaultLevel; - } - self.setLevel(initialLevel, false); - } - - /* - * - * Top-level API - * - */ - - var defaultLogger = new Logger(); - - var _loggersByName = {}; - defaultLogger.getLogger = function getLogger(name) { - if ((typeof name !== "symbol" && typeof name !== "string") || name === "") { - throw new TypeError("You must supply a name when creating a logger."); - } - - var logger = _loggersByName[name]; - if (!logger) { - logger = _loggersByName[name] = new Logger( - name, defaultLogger.getLevel(), defaultLogger.methodFactory); - } - return logger; - }; - - // Grab the current global log variable in case of overwrite - var _log = (typeof window !== undefinedType) ? window.log : undefined; - defaultLogger.noConflict = function() { - if (typeof window !== undefinedType && - window.log === defaultLogger) { - window.log = _log; - } - - return defaultLogger; - }; - - defaultLogger.getLoggers = function getLoggers() { - return _loggersByName; - }; - - // ES6 default export, for compatibility - defaultLogger['default'] = defaultLogger; - - return defaultLogger; -})); - -},{}],152:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ExtensibleEvents = void 0; - -var _NamespacedMap = require("./NamespacedMap"); - -var _InvalidEventError = require("./InvalidEventError"); - -var _MRoomMessage = require("./interpreters/legacy/MRoomMessage"); - -var _MMessage = require("./interpreters/modern/MMessage"); - -var _message_types = require("./events/message_types"); - -var _poll_types = require("./events/poll_types"); - -var _MPoll = require("./interpreters/modern/MPoll"); - -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * Utility class for parsing and identifying event types in a renderable form. An - * instance of this class can be created to change rendering preference depending - * on use-case. - */ -var ExtensibleEvents = /*#__PURE__*/function () { - function ExtensibleEvents() { - _classCallCheck(this, ExtensibleEvents); - - _defineProperty(this, "interpreters", new _NamespacedMap.NamespacedMap([// Remember to add your unit test when adding to this! ("known events" test description) - [_MRoomMessage.LEGACY_M_ROOM_MESSAGE, _MRoomMessage.parseMRoomMessage], [_message_types.M_MESSAGE, _MMessage.parseMMessage], [_message_types.M_EMOTE, _MMessage.parseMMessage], [_message_types.M_NOTICE, _MMessage.parseMMessage], [_poll_types.M_POLL_START, _MPoll.parseMPoll], [_poll_types.M_POLL_RESPONSE, _MPoll.parseMPoll], [_poll_types.M_POLL_END, _MPoll.parseMPoll]])); - - _defineProperty(this, "_unknownInterpretOrder", [_message_types.M_MESSAGE]); - } - /** - * Gets the default instance for all extensible event parsing. - */ - - - _createClass(ExtensibleEvents, [{ - key: "unknownInterpretOrder", - get: - /** - * Gets the order the internal processor will use for unknown primary - * event types. - */ - function get() { - var _this$_unknownInterpr; - - return (_this$_unknownInterpr = this._unknownInterpretOrder) !== null && _this$_unknownInterpr !== void 0 ? _this$_unknownInterpr : []; - } - /** - * Sets the order the internal processor will use for unknown primary - * event types. - * @param {NamespacedValue[]} val The parsing order. - */ - , - set: function set(val) { - this._unknownInterpretOrder = val; - } - /** - * Gets the order the internal processor will use for unknown primary - * event types. - */ - - }, { - key: "registerInterpreter", - value: - /** - * Registers a primary event type interpreter. Note that the interpreter might be - * called with non-primary events if the event is being parsed as a fallback. - * @param {NamespacedValue} wireEventType The event type. - * @param {EventInterpreter} interpreter The interpreter. - */ - function registerInterpreter(wireEventType, interpreter) { - this.interpreters.set(wireEventType, interpreter); - } - /** - * Registers a primary event type interpreter. Note that the interpreter might be - * called with non-primary events if the event is being parsed as a fallback. - * @param {NamespacedValue} wireEventType The event type. - * @param {EventInterpreter} interpreter The interpreter. - */ - - }, { - key: "parse", - value: - /** - * Parses an event, trying the primary event type first. If the primary type is not known - * then the content will be inspected to find the most suitable fallback. - * - * If the parsing failed or was a completely unknown type, this will return falsy. - * @param {IPartialEvent} wireFormat The event to parse. - * @returns {Optional} The parsed extensible event. - */ - function parse(wireFormat) { - try { - if (this.interpreters.hasNamespaced(wireFormat.type)) { - return this.interpreters.getNamespaced(wireFormat.type)(wireFormat); - } - - var _iterator = _createForOfIteratorHelper(this.unknownInterpretOrder), - _step; - - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var tryType = _step.value; - - if (this.interpreters.has(tryType)) { - var val = this.interpreters.get(tryType)(wireFormat); - if (val) return val; - } - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - - return null; // cannot be parsed - } catch (e) { - if (e instanceof _InvalidEventError.InvalidEventError) { - return null; // fail parsing and move on - } - - throw e; // re-throw everything else - } - } - /** - * Parses an event, trying the primary event type first. If the primary type is not known - * then the content will be inspected to find the most suitable fallback. - * - * If the parsing failed or was a completely unknown type, this will return falsy. - * @param {IPartialEvent} wireFormat The event to parse. - * @returns {Optional} The parsed extensible event. - */ - - }], [{ - key: "defaultInstance", - get: function get() { - return ExtensibleEvents._defaultInstance; - } - }, { - key: "unknownInterpretOrder", - get: function get() { - return ExtensibleEvents.defaultInstance.unknownInterpretOrder; - } - /** - * Sets the order the internal processor will use for unknown primary - * event types. - * @param {NamespacedValue[]} val The parsing order. - */ - , - set: function set(val) { - ExtensibleEvents.defaultInstance.unknownInterpretOrder = val; - } - }, { - key: "registerInterpreter", - value: function registerInterpreter(wireEventType, interpreter) { - ExtensibleEvents.defaultInstance.registerInterpreter(wireEventType, interpreter); - } - }, { - key: "parse", - value: function parse(wireFormat) { - return ExtensibleEvents.defaultInstance.parse(wireFormat); - } - }]); - - return ExtensibleEvents; -}(); - -exports.ExtensibleEvents = ExtensibleEvents; - -_defineProperty(ExtensibleEvents, "_defaultInstance", new ExtensibleEvents()); -},{"./InvalidEventError":154,"./NamespacedMap":155,"./events/message_types":164,"./events/poll_types":165,"./interpreters/legacy/MRoomMessage":168,"./interpreters/modern/MMessage":169,"./interpreters/modern/MPoll":170}],153:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -},{}],154:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.InvalidEventError = void 0; - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } - -function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Thrown when an event is unforgivably unparsable. - */ -var InvalidEventError = /*#__PURE__*/function (_Error) { - _inherits(InvalidEventError, _Error); - - var _super = _createSuper(InvalidEventError); - - function InvalidEventError(message) { - _classCallCheck(this, InvalidEventError); - - return _super.call(this, message); - } - - return _createClass(InvalidEventError); -}( /*#__PURE__*/_wrapNativeSuper(Error)); - -exports.InvalidEventError = InvalidEventError; -},{}],155:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.NamespacedMap = void 0; - -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * A `Map` implementation which accepts a NamespacedValue as a key, and arbitrary value. The - * namespaced value must be a string type. - */ -var NamespacedMap = /*#__PURE__*/function () { - // protected to make tests happy for access - - /** - * Creates a new map with optional seed data. - * @param {Array<[NS, V]>} initial The seed data. - */ - function NamespacedMap(initial) { - _classCallCheck(this, NamespacedMap); - - _defineProperty(this, "internalMap", new Map()); - - if (initial) { - var _iterator = _createForOfIteratorHelper(initial), - _step; - - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var val = _step.value; - this.set(val[0], val[1]); - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - } - } - /** - * Gets a value from the map. If the value does not exist under - * either namespace option, falsy is returned. - * @param {NS} key The key. - * @returns {Optional} The value, or falsy. - */ - - - _createClass(NamespacedMap, [{ - key: "get", - value: function get(key) { - if (key.name && this.internalMap.has(key.name)) { - return this.internalMap.get(key.name); - } - - if (key.altName && this.internalMap.has(key.altName)) { - return this.internalMap.get(key.altName); - } - - return null; - } - /** - * Sets a value in the map. - * @param {NS} key The key. - * @param {V} val The value. - */ - - }, { - key: "set", - value: function set(key, val) { - if (key.name) { - this.internalMap.set(key.name, val); - } - - if (key.altName) { - this.internalMap.set(key.altName, val); - } - } - /** - * Determines if any of the valid namespaced values are present - * in the map. - * @param {NS} key The key. - * @returns {boolean} True if present. - */ - - }, { - key: "has", - value: function has(key) { - return !!this.get(key); - } - /** - * Removes all the namespaced values from the map. - * @param {NS} key The key. - */ - - }, { - key: "delete", - value: function _delete(key) { - if (key.name) { - this.internalMap["delete"](key.name); - } - - if (key.altName) { - this.internalMap["delete"](key.altName); - } - } - /** - * Determines if the map contains a specific namespaced value - * instead of the parent NS type. - * @param {string} key The key. - * @returns {boolean} True if present. - */ - - }, { - key: "hasNamespaced", - value: function hasNamespaced(key) { - return this.internalMap.has(key); - } - /** - * Gets a specific namespaced value from the map instead of the - * parent NS type. Returns falsy if not found. - * @param {string} key The key. - * @returns {Optional} The value, or falsy. - */ - - }, { - key: "getNamespaced", - value: function getNamespaced(key) { - return this.internalMap.get(key); - } - }]); - - return NamespacedMap; -}(); - -exports.NamespacedMap = NamespacedMap; -},{}],156:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.UnstableValue = exports.NamespacedValue = void 0; - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -/* -Copyright 2021 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Represents a simple Matrix namespaced value. This will assume that if a stable prefix - * is provided that the stable prefix should be used when representing the identifier. - */ -var NamespacedValue = /*#__PURE__*/function () { - // Stable is optional, but one of the two parameters is required, hence the weird-looking types. - // Goal is to have developers explicitly say there is no stable value (if applicable). - function NamespacedValue(stable, unstable) { - _classCallCheck(this, NamespacedValue); - - this.stable = stable; - this.unstable = unstable; - - if (!this.unstable && !this.stable) { - throw new Error("One of stable or unstable values must be supplied"); - } - } - - _createClass(NamespacedValue, [{ - key: "name", - get: function get() { - if (this.stable) { - return this.stable; - } - - return this.unstable; - } - }, { - key: "altName", - get: function get() { - if (!this.stable) { - return null; - } - - return this.unstable; - } - }, { - key: "matches", - value: function matches(val) { - return !!this.name && this.name === val || !!this.altName && this.altName === val; - } // this desperately wants https://github.com/microsoft/TypeScript/pull/26349 at the top level of the class - // so we can instantiate `NamespacedValue` as a default type for that namespace. - - }, { - key: "findIn", - value: function findIn(obj) { - var val; - - if (this.name) { - val = obj === null || obj === void 0 ? void 0 : obj[this.name]; - } - - if (!val && this.altName) { - val = obj === null || obj === void 0 ? void 0 : obj[this.altName]; - } - - return val; - } - }, { - key: "includedIn", - value: function includedIn(arr) { - var included = false; - - if (this.name) { - included = arr.includes(this.name); - } - - if (!included && this.altName) { - included = arr.includes(this.altName); - } - - return included; - } - }]); - - return NamespacedValue; -}(); -/** - * Represents a namespaced value which prioritizes the unstable value over the stable - * value. - */ - - -exports.NamespacedValue = NamespacedValue; - -var UnstableValue = /*#__PURE__*/function (_NamespacedValue) { - _inherits(UnstableValue, _NamespacedValue); - - var _super = _createSuper(UnstableValue); - - // Note: Constructor difference is that `unstable` is *required*. - function UnstableValue(stable, unstable) { - var _this; - - _classCallCheck(this, UnstableValue); - - _this = _super.call(this, stable, unstable); - - if (!_this.unstable) { - throw new Error("Unstable value must be supplied"); - } - - return _this; - } - - _createClass(UnstableValue, [{ - key: "name", - get: function get() { - return this.unstable; - } - }, { - key: "altName", - get: function get() { - return this.stable; - } - }]); - - return UnstableValue; -}(NamespacedValue); - -exports.UnstableValue = UnstableValue; -},{}],157:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.EmoteEvent = void 0; - -var _MessageEvent2 = require("./MessageEvent"); - -var _message_types = require("./message_types"); - -var _events = require("../utility/events"); - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } - -function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -// Emote events are just decorated message events - -/** - * Represents an emote. This is essentially a MessageEvent with - * emote characteristics considered. - */ -var EmoteEvent = /*#__PURE__*/function (_MessageEvent) { - _inherits(EmoteEvent, _MessageEvent); - - var _super = _createSuper(EmoteEvent); - - function EmoteEvent(wireFormat) { - _classCallCheck(this, EmoteEvent); - - return _super.call(this, wireFormat); - } - - _createClass(EmoteEvent, [{ - key: "isEmote", - get: function get() { - return true; // override - } - }, { - key: "isEquivalentTo", - value: function isEquivalentTo(primaryEventType) { - return (0, _events.isEventTypeSame)(primaryEventType, _message_types.M_EMOTE) || _get(_getPrototypeOf(EmoteEvent.prototype), "isEquivalentTo", this).call(this, primaryEventType); - } - }, { - key: "serialize", - value: function serialize() { - var message = _get(_getPrototypeOf(EmoteEvent.prototype), "serialize", this).call(this); - - message.content['msgtype'] = "m.emote"; - return message; - } - /** - * Creates a new EmoteEvent from text and HTML. - * @param {string} text The text. - * @param {string} html Optional HTML. - * @returns {MessageEvent} The representative message event. - */ - - }], [{ - key: "from", - value: function from(text, html) { - var _content; - - return new EmoteEvent({ - type: _message_types.M_EMOTE.name, - content: (_content = {}, _defineProperty(_content, _message_types.M_TEXT.name, text), _defineProperty(_content, _message_types.M_HTML.name, html), _content) - }); - } - }]); - - return EmoteEvent; -}(_MessageEvent2.MessageEvent); - -exports.EmoteEvent = EmoteEvent; -},{"../utility/events":173,"./MessageEvent":159,"./message_types":164}],158:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ExtensibleEvent = void 0; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -/* -Copyright 2021 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Represents an Extensible Event in Matrix. - */ -var ExtensibleEvent = /*#__PURE__*/function () { - function ExtensibleEvent(wireFormat) { - _classCallCheck(this, ExtensibleEvent); - - this.wireFormat = wireFormat; - } - /** - * Shortcut to wireFormat.content - */ - - - _createClass(ExtensibleEvent, [{ - key: "wireContent", - get: function get() { - return this.wireFormat.content; - } - /** - * Serializes the event into a format which can be used to send the - * event to the room. - * @returns {IPartialEvent} The serialized event. - */ - - }]); - - return ExtensibleEvent; -}(); - -exports.ExtensibleEvent = ExtensibleEvent; -},{}],159:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.MessageEvent = void 0; - -var _ExtensibleEvent2 = require("./ExtensibleEvent"); - -var _types = require("../types"); - -var _InvalidEventError = require("../InvalidEventError"); - -var _message_types = require("./message_types"); - -var _events = require("../utility/events"); - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * Represents a message event. Message events are the simplest form of event with - * just text (optionally of different mimetypes, like HTML). - * - * Message events can additionally be an Emote or Notice, though typically those - * are represented as EmoteEvent and NoticeEvent respectively. - */ -var MessageEvent = /*#__PURE__*/function (_ExtensibleEvent) { - _inherits(MessageEvent, _ExtensibleEvent); - - var _super = _createSuper(MessageEvent); - - /** - * The default text for the event. - */ - - /** - * The default HTML for the event, if provided. - */ - - /** - * All the different renderings of the message. Note that this is the same - * format as an m.message body but may contain elements not found directly - * in the event content: this is because this is interpreted based off the - * other information available in the event. - */ - - /** - * Creates a new MessageEvent from a pure format. Note that the event is - * *not* parsed here: it will be treated as a literal m.message primary - * typed event. - * @param {IPartialEvent} wireFormat The event. - */ - function MessageEvent(wireFormat) { - var _this; - - _classCallCheck(this, MessageEvent); - - _this = _super.call(this, wireFormat); - - _defineProperty(_assertThisInitialized(_this), "text", void 0); - - _defineProperty(_assertThisInitialized(_this), "html", void 0); - - _defineProperty(_assertThisInitialized(_this), "renderings", void 0); - - var mmessage = _message_types.M_MESSAGE.findIn(_this.wireContent); - - var mtext = _message_types.M_TEXT.findIn(_this.wireContent); - - var mhtml = _message_types.M_HTML.findIn(_this.wireContent); - - if ((0, _types.isProvided)(mmessage)) { - if (!Array.isArray(mmessage)) { - throw new _InvalidEventError.InvalidEventError("m.message contents must be an array"); - } - - var text = mmessage.find(function (r) { - return !(0, _types.isProvided)(r.mimetype) || r.mimetype === "text/plain"; - }); - var html = mmessage.find(function (r) { - return r.mimetype === "text/html"; - }); - if (!text) throw new _InvalidEventError.InvalidEventError("m.message is missing a plain text representation"); - _this.text = text.body; - _this.html = html === null || html === void 0 ? void 0 : html.body; - _this.renderings = mmessage; - } else if ((0, _types.isOptionalAString)(mtext)) { - _this.text = mtext; - _this.html = mhtml; - _this.renderings = [{ - body: mtext, - mimetype: "text/plain" - }]; - - if (_this.html) { - _this.renderings.push({ - body: _this.html, - mimetype: "text/html" - }); - } - } else { - throw new _InvalidEventError.InvalidEventError("Missing textual representation for event"); - } - - return _this; - } - /** - * Gets whether this message is considered an "emote". Note that a message - * might be an emote and notice at the same time: while technically possible, - * the event should be interpreted as one or the other. - */ - - - _createClass(MessageEvent, [{ - key: "isEmote", - get: function get() { - return _message_types.M_EMOTE.matches(this.wireFormat.type) || (0, _types.isProvided)(_message_types.M_EMOTE.findIn(this.wireFormat.content)); - } - /** - * Gets whether this message is considered a "notice". Note that a message - * might be an emote and notice at the same time: while technically possible, - * the event should be interpreted as one or the other. - */ - - }, { - key: "isNotice", - get: function get() { - return _message_types.M_NOTICE.matches(this.wireFormat.type) || (0, _types.isProvided)(_message_types.M_NOTICE.findIn(this.wireFormat.content)); - } - }, { - key: "isEquivalentTo", - value: function isEquivalentTo(primaryEventType) { - return (0, _events.isEventTypeSame)(primaryEventType, _message_types.M_MESSAGE); - } - }, { - key: "serializeMMessageOnly", - value: function serializeMMessageOnly() { - var messageRendering = _defineProperty({}, _message_types.M_MESSAGE.name, this.renderings); // Use the shorthand if it's just a simple text event - - - if (this.renderings.length === 1) { - var mime = this.renderings[0].mimetype; - - if (mime === undefined || mime === "text/plain") { - messageRendering = _defineProperty({}, _message_types.M_TEXT.name, this.renderings[0].body); - } - } - - return messageRendering; - } - }, { - key: "serialize", - value: function serialize() { - var _this$html; - - return { - type: "m.room.message", - content: _objectSpread(_objectSpread({}, this.serializeMMessageOnly()), {}, { - body: this.text, - msgtype: "m.text", - format: this.html ? "org.matrix.custom.html" : undefined, - formatted_body: (_this$html = this.html) !== null && _this$html !== void 0 ? _this$html : undefined - }) - }; - } - /** - * Creates a new MessageEvent from text and HTML. - * @param {string} text The text. - * @param {string} html Optional HTML. - * @returns {MessageEvent} The representative message event. - */ - - }], [{ - key: "from", - value: function from(text, html) { - var _content; - - return new MessageEvent({ - type: _message_types.M_MESSAGE.name, - content: (_content = {}, _defineProperty(_content, _message_types.M_TEXT.name, text), _defineProperty(_content, _message_types.M_HTML.name, html), _content) - }); - } - }]); - - return MessageEvent; -}(_ExtensibleEvent2.ExtensibleEvent); - -exports.MessageEvent = MessageEvent; -},{"../InvalidEventError":154,"../types":171,"../utility/events":173,"./ExtensibleEvent":158,"./message_types":164}],160:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.NoticeEvent = void 0; - -var _MessageEvent2 = require("./MessageEvent"); - -var _message_types = require("./message_types"); - -var _events = require("../utility/events"); - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } - -function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -// Notice events are just decorated message events - -/** - * Represents a notice. This is essentially a MessageEvent with - * notice characteristics considered. - */ -var NoticeEvent = /*#__PURE__*/function (_MessageEvent) { - _inherits(NoticeEvent, _MessageEvent); - - var _super = _createSuper(NoticeEvent); - - function NoticeEvent(wireFormat) { - _classCallCheck(this, NoticeEvent); - - return _super.call(this, wireFormat); - } - - _createClass(NoticeEvent, [{ - key: "isNotice", - get: function get() { - return true; // override - } - }, { - key: "isEquivalentTo", - value: function isEquivalentTo(primaryEventType) { - return (0, _events.isEventTypeSame)(primaryEventType, _message_types.M_NOTICE) || _get(_getPrototypeOf(NoticeEvent.prototype), "isEquivalentTo", this).call(this, primaryEventType); - } - }, { - key: "serialize", - value: function serialize() { - var message = _get(_getPrototypeOf(NoticeEvent.prototype), "serialize", this).call(this); - - message.content['msgtype'] = "m.notice"; - return message; - } - /** - * Creates a new NoticeEvent from text and HTML. - * @param {string} text The text. - * @param {string} html Optional HTML. - * @returns {MessageEvent} The representative message event. - */ - - }], [{ - key: "from", - value: function from(text, html) { - var _content; - - return new NoticeEvent({ - type: _message_types.M_NOTICE.name, - content: (_content = {}, _defineProperty(_content, _message_types.M_TEXT.name, text), _defineProperty(_content, _message_types.M_HTML.name, html), _content) - }); - } - }]); - - return NoticeEvent; -}(_MessageEvent2.MessageEvent); - -exports.NoticeEvent = NoticeEvent; -},{"../utility/events":173,"./MessageEvent":159,"./message_types":164}],161:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.PollEndEvent = void 0; - -var _poll_types = require("./poll_types"); - -var _InvalidEventError = require("../InvalidEventError"); - -var _relationship_types = require("./relationship_types"); - -var _MessageEvent = require("./MessageEvent"); - -var _message_types = require("./message_types"); - -var _events = require("../utility/events"); - -var _ExtensibleEvent2 = require("./ExtensibleEvent"); - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * Represents a poll end/closure event. - */ -var PollEndEvent = /*#__PURE__*/function (_ExtensibleEvent) { - _inherits(PollEndEvent, _ExtensibleEvent); - - var _super = _createSuper(PollEndEvent); - - /** - * The poll start event ID referenced by the response. - */ - - /** - * The closing message for the event. - */ - - /** - * Creates a new PollEndEvent from a pure format. Note that the event is *not* - * parsed here: it will be treated as a literal m.poll.response primary typed event. - * @param {IPartialEvent} wireFormat The event. - */ - function PollEndEvent(wireFormat) { - var _this; - - _classCallCheck(this, PollEndEvent); - - _this = _super.call(this, wireFormat); - - _defineProperty(_assertThisInitialized(_this), "pollEventId", void 0); - - _defineProperty(_assertThisInitialized(_this), "closingMessage", void 0); - - var rel = _this.wireContent["m.relates_to"]; - - if (!_relationship_types.REFERENCE_RELATION.matches(rel === null || rel === void 0 ? void 0 : rel.rel_type) || typeof (rel === null || rel === void 0 ? void 0 : rel.event_id) !== "string") { - throw new _InvalidEventError.InvalidEventError("Relationship must be a reference to an event"); - } - - _this.pollEventId = rel.event_id; - _this.closingMessage = new _MessageEvent.MessageEvent(_this.wireFormat); - return _this; - } - - _createClass(PollEndEvent, [{ - key: "isEquivalentTo", - value: function isEquivalentTo(primaryEventType) { - return (0, _events.isEventTypeSame)(primaryEventType, _poll_types.M_POLL_END); - } - }, { - key: "serialize", - value: function serialize() { - return { - type: _poll_types.M_POLL_END.name, - content: _objectSpread(_defineProperty({ - "m.relates_to": { - rel_type: _relationship_types.REFERENCE_RELATION.name, - event_id: this.pollEventId - } - }, _poll_types.M_POLL_END.name, {}), this.closingMessage.serialize().content) - }; - } - /** - * Creates a new PollEndEvent from a poll event ID. - * @param {string} pollEventId The poll start event ID. - * @param {string} message A closing message, typically revealing the top answer. - * @returns {PollStartEvent} The representative poll closure event. - */ - - }], [{ - key: "from", - value: function from(pollEventId, message) { - var _content; - - return new PollEndEvent({ - type: _poll_types.M_POLL_END.name, - content: (_content = { - "m.relates_to": { - rel_type: _relationship_types.REFERENCE_RELATION.name, - event_id: pollEventId - } - }, _defineProperty(_content, _poll_types.M_POLL_END.name, {}), _defineProperty(_content, _message_types.M_TEXT.name, message), _content) - }); - } - }]); - - return PollEndEvent; -}(_ExtensibleEvent2.ExtensibleEvent); - -exports.PollEndEvent = PollEndEvent; -},{"../InvalidEventError":154,"../utility/events":173,"./ExtensibleEvent":158,"./MessageEvent":159,"./message_types":164,"./poll_types":165,"./relationship_types":166}],162:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.PollResponseEvent = void 0; - -var _ExtensibleEvent2 = require("./ExtensibleEvent"); - -var _poll_types = require("./poll_types"); - -var _InvalidEventError = require("../InvalidEventError"); - -var _relationship_types = require("./relationship_types"); - -var _events = require("../utility/events"); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * Represents a poll response event. - */ -var PollResponseEvent = /*#__PURE__*/function (_ExtensibleEvent) { - _inherits(PollResponseEvent, _ExtensibleEvent); - - var _super = _createSuper(PollResponseEvent); - - /** - * Creates a new PollResponseEvent from a pure format. Note that the event is *not* - * parsed here: it will be treated as a literal m.poll.response primary typed event. - * - * To validate the response against a poll, call `validateAgainst` after creation. - * @param {IPartialEvent} wireFormat The event. - */ - function PollResponseEvent(wireFormat) { - var _this; - - _classCallCheck(this, PollResponseEvent); - - _this = _super.call(this, wireFormat); - - _defineProperty(_assertThisInitialized(_this), "internalAnswerIds", void 0); - - _defineProperty(_assertThisInitialized(_this), "internalSpoiled", void 0); - - _defineProperty(_assertThisInitialized(_this), "pollEventId", void 0); - - var rel = _this.wireContent["m.relates_to"]; - - if (!_relationship_types.REFERENCE_RELATION.matches(rel === null || rel === void 0 ? void 0 : rel.rel_type) || typeof (rel === null || rel === void 0 ? void 0 : rel.event_id) !== "string") { - throw new _InvalidEventError.InvalidEventError("Relationship must be a reference to an event"); - } - - _this.pollEventId = rel.event_id; - - _this.validateAgainst(null); - - return _this; - } - /** - * Validates the poll response using the poll start event as a frame of reference. This - * is used to determine if the vote is spoiled, whether the answers are valid, etc. - * @param {PollStartEvent} poll The poll start event. - */ - - - _createClass(PollResponseEvent, [{ - key: "answerIds", - get: - /** - * The provided answers for the poll. Note that this may be falsy/unpredictable if - * the `spoiled` property is true. - */ - function get() { - return this.internalAnswerIds; - } - /** - * The poll start event ID referenced by the response. - */ - - }, { - key: "spoiled", - get: - /** - * Whether the vote is spoiled. - */ - function get() { - return this.internalSpoiled; - } - }, { - key: "validateAgainst", - value: function validateAgainst(poll) { - var response = _poll_types.M_POLL_RESPONSE.findIn(this.wireContent); - - if (!Array.isArray(response === null || response === void 0 ? void 0 : response.answers)) { - this.internalSpoiled = true; - this.internalAnswerIds = []; - return; - } - - var answers = response.answers; - - if (answers.some(function (a) { - return typeof a !== "string"; - }) || answers.length === 0) { - this.internalSpoiled = true; - this.internalAnswerIds = []; - return; - } - - if (poll) { - if (answers.some(function (a) { - return !poll.answers.some(function (pa) { - return pa.id === a; - }); - })) { - this.internalSpoiled = true; - this.internalAnswerIds = []; - return; - } - - answers = answers.slice(0, poll.maxSelections); - } - - this.internalAnswerIds = answers; - this.internalSpoiled = false; - } - }, { - key: "isEquivalentTo", - value: function isEquivalentTo(primaryEventType) { - return (0, _events.isEventTypeSame)(primaryEventType, _poll_types.M_POLL_RESPONSE); - } - }, { - key: "serialize", - value: function serialize() { - return { - type: _poll_types.M_POLL_RESPONSE.name, - content: _defineProperty({ - "m.relates_to": { - rel_type: _relationship_types.REFERENCE_RELATION.name, - event_id: this.pollEventId - } - }, _poll_types.M_POLL_RESPONSE.name, { - answers: this.spoiled ? undefined : this.answerIds - }) - }; - } - /** - * Creates a new PollResponseEvent from a set of answers. To spoil the vote, pass an empty - * answers array. - * @param {string} answers The user's answers. Should be valid from a poll's answer IDs. - * @param {string} pollEventId The poll start event ID. - * @returns {PollStartEvent} The representative poll response event. - */ - - }], [{ - key: "from", - value: function from(answers, pollEventId) { - return new PollResponseEvent({ - type: _poll_types.M_POLL_RESPONSE.name, - content: _defineProperty({ - "m.relates_to": { - rel_type: _relationship_types.REFERENCE_RELATION.name, - event_id: pollEventId - } - }, _poll_types.M_POLL_RESPONSE.name, { - answers: answers - }) - }); - } - }]); - - return PollResponseEvent; -}(_ExtensibleEvent2.ExtensibleEvent); - -exports.PollResponseEvent = PollResponseEvent; -},{"../InvalidEventError":154,"../utility/events":173,"./ExtensibleEvent":158,"./poll_types":165,"./relationship_types":166}],163:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.PollStartEvent = exports.PollAnswerSubevent = void 0; - -var _poll_types = require("./poll_types"); - -var _MessageEvent2 = require("./MessageEvent"); - -var _message_types = require("./message_types"); - -var _InvalidEventError = require("../InvalidEventError"); - -var _NamespacedValue = require("../NamespacedValue"); - -var _events = require("../utility/events"); - -var _ExtensibleEvent2 = require("./ExtensibleEvent"); - -function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - -function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } - -function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * Represents a poll answer. Note that this is represented as a subtype and is - * not registered as a parsable event - it is implied for usage exclusively - * within the PollStartEvent parsing. - */ -var PollAnswerSubevent = /*#__PURE__*/function (_MessageEvent) { - _inherits(PollAnswerSubevent, _MessageEvent); - - var _super = _createSuper(PollAnswerSubevent); - - /** - * The answer ID. - */ - function PollAnswerSubevent(wireFormat) { - var _this; - - _classCallCheck(this, PollAnswerSubevent); - - _this = _super.call(this, wireFormat); - - _defineProperty(_assertThisInitialized(_this), "id", void 0); - - var id = wireFormat.content.id; - - if (!id || typeof id !== "string") { - throw new _InvalidEventError.InvalidEventError("Answer ID must be a non-empty string"); - } - - _this.id = id; - return _this; - } - - _createClass(PollAnswerSubevent, [{ - key: "serialize", - value: function serialize() { - return { - type: "org.matrix.sdk.poll.answer", - content: _objectSpread({ - id: this.id - }, this.serializeMMessageOnly()) - }; - } - /** - * Creates a new PollAnswerSubevent from ID and text. - * @param {string} id The answer ID (unique within the poll). - * @param {string} text The text. - * @returns {PollAnswerSubevent} The representative answer. - */ - - }], [{ - key: "from", - value: function from(id, text) { - return new PollAnswerSubevent({ - type: "org.matrix.sdk.poll.answer", - content: _defineProperty({ - id: id - }, _message_types.M_TEXT.name, text) - }); - } - }]); - - return PollAnswerSubevent; -}(_MessageEvent2.MessageEvent); -/** - * Represents a poll start event. - */ - - -exports.PollAnswerSubevent = PollAnswerSubevent; - -var PollStartEvent = /*#__PURE__*/function (_ExtensibleEvent) { - _inherits(PollStartEvent, _ExtensibleEvent); - - var _super2 = _createSuper(PollStartEvent); - - /** - * The question being asked, as a MessageEvent node. - */ - - /** - * The interpreted kind of poll. Note that this will infer a value that is known to the - * SDK rather than verbatim - this means unknown types will be represented as undisclosed - * polls. - * - * To get the raw kind, use rawKind. - */ - - /** - * The true kind as provided by the event sender. Might not be valid. - */ - - /** - * The maximum number of selections a user is allowed to make. - */ - - /** - * The possible answers for the poll. - */ - - /** - * Creates a new PollStartEvent from a pure format. Note that the event is *not* - * parsed here: it will be treated as a literal m.poll.start primary typed event. - * @param {IPartialEvent} wireFormat The event. - */ - function PollStartEvent(wireFormat) { - var _this2; - - _classCallCheck(this, PollStartEvent); - - _this2 = _super2.call(this, wireFormat); - - _defineProperty(_assertThisInitialized(_this2), "question", void 0); - - _defineProperty(_assertThisInitialized(_this2), "kind", void 0); - - _defineProperty(_assertThisInitialized(_this2), "rawKind", void 0); - - _defineProperty(_assertThisInitialized(_this2), "maxSelections", void 0); - - _defineProperty(_assertThisInitialized(_this2), "answers", void 0); - - var poll = _poll_types.M_POLL_START.findIn(_this2.wireContent); - - if (!poll.question) { - throw new _InvalidEventError.InvalidEventError("A question is required"); - } - - _this2.question = new _MessageEvent2.MessageEvent({ - type: "org.matrix.sdk.poll.question", - content: poll.question - }); - _this2.rawKind = poll.kind; - - if (_poll_types.M_POLL_KIND_DISCLOSED.matches(_this2.rawKind)) { - _this2.kind = _poll_types.M_POLL_KIND_DISCLOSED; - } else { - _this2.kind = _poll_types.M_POLL_KIND_UNDISCLOSED; // default & assumed value - } - - _this2.maxSelections = Number.isFinite(poll.max_selections) && poll.max_selections > 0 ? poll.max_selections : 1; - - if (!Array.isArray(poll.answers)) { - throw new _InvalidEventError.InvalidEventError("Poll answers must be an array"); - } - - var answers = poll.answers.slice(0, 20).map(function (a) { - return new PollAnswerSubevent({ - type: "org.matrix.sdk.poll.answer", - content: a - }); - }); - - if (answers.length <= 0) { - throw new _InvalidEventError.InvalidEventError("No answers available"); - } - - _this2.answers = answers; - return _this2; - } - - _createClass(PollStartEvent, [{ - key: "isEquivalentTo", - value: function isEquivalentTo(primaryEventType) { - return (0, _events.isEventTypeSame)(primaryEventType, _poll_types.M_POLL_START); - } - }, { - key: "serialize", - value: function serialize() { - var _content2; - - return { - type: _poll_types.M_POLL_START.name, - content: (_content2 = {}, _defineProperty(_content2, _poll_types.M_POLL_START.name, { - question: this.question.serialize().content, - kind: this.rawKind, - max_selections: this.maxSelections, - answers: this.answers.map(function (a) { - return a.serialize().content; - }) - }), _defineProperty(_content2, _message_types.M_TEXT.name, "".concat(this.question.text, "\n").concat(this.answers.map(function (a, i) { - return "".concat(i + 1, ". ").concat(a.text); - }).join("\n"))), _content2) - }; - } - /** - * Creates a new PollStartEvent from question, answers, and metadata. - * @param {string} question The question to ask. - * @param {string} answers The answers. Should be unique within each other. - * @param {KNOWN_POLL_KIND|string} kind The kind of poll. - * @param {number} maxSelections The maximum number of selections. Must be 1 or higher. - * @returns {PollStartEvent} The representative poll start event. - */ - - }], [{ - key: "from", - value: function from(question, answers, kind) { - var _content3; - - var maxSelections = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1; - return new PollStartEvent({ - type: _poll_types.M_POLL_START.name, - content: (_content3 = {}, _defineProperty(_content3, _message_types.M_TEXT.name, question), _defineProperty(_content3, _poll_types.M_POLL_START.name, { - question: _defineProperty({}, _message_types.M_TEXT.name, question), - kind: kind instanceof _NamespacedValue.NamespacedValue ? kind.name : kind, - max_selections: maxSelections, - answers: answers.map(function (a) { - return _defineProperty({ - id: makeId() - }, _message_types.M_TEXT.name, a); - }) - }), _content3) - }); - } - }]); - - return PollStartEvent; -}(_ExtensibleEvent2.ExtensibleEvent); - -exports.PollStartEvent = PollStartEvent; -var LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -function makeId() { - return _toConsumableArray(Array(16)).map(function () { - return LETTERS.charAt(Math.floor(Math.random() * LETTERS.length)); - }).join(''); -} -},{"../InvalidEventError":154,"../NamespacedValue":156,"../utility/events":173,"./ExtensibleEvent":158,"./MessageEvent":159,"./message_types":164,"./poll_types":165}],164:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.M_TEXT = exports.M_NOTICE = exports.M_MESSAGE = exports.M_HTML = exports.M_EMOTE = void 0; - -var _NamespacedValue = require("../NamespacedValue"); - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * The namespaced value for m.message - */ -var M_MESSAGE = new _NamespacedValue.UnstableValue("m.message", "org.matrix.msc1767.message"); -/** - * An m.message event rendering - */ - -exports.M_MESSAGE = M_MESSAGE; - -/** - * The namespaced value for m.text - */ -var M_TEXT = new _NamespacedValue.UnstableValue("m.text", "org.matrix.msc1767.text"); -/** - * The content for an m.text event - */ - -exports.M_TEXT = M_TEXT; - -/** - * The namespaced value for m.html - */ -var M_HTML = new _NamespacedValue.UnstableValue("m.html", "org.matrix.msc1767.html"); -/** - * The content for an m.html event - */ - -exports.M_HTML = M_HTML; - -/** - * The namespaced value for m.emote - */ -var M_EMOTE = new _NamespacedValue.UnstableValue("m.emote", "org.matrix.msc1767.emote"); -/** - * The event definition for an m.emote event (in content) - */ - -exports.M_EMOTE = M_EMOTE; - -/** - * The namespaced value for m.notice - */ -var M_NOTICE = new _NamespacedValue.UnstableValue("m.notice", "org.matrix.msc1767.notice"); -/** - * The event definition for an m.notice event (in content) - */ - -exports.M_NOTICE = M_NOTICE; -},{"../NamespacedValue":156}],165:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.M_POLL_START = exports.M_POLL_RESPONSE = exports.M_POLL_KIND_UNDISCLOSED = exports.M_POLL_KIND_DISCLOSED = exports.M_POLL_END = void 0; - -var _NamespacedValue = require("../NamespacedValue"); - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Identifier for a disclosed poll. - */ -var M_POLL_KIND_DISCLOSED = new _NamespacedValue.UnstableValue("m.poll.disclosed", "org.matrix.msc3381.poll.disclosed"); -/** - * Identifier for an undisclosed poll. - */ - -exports.M_POLL_KIND_DISCLOSED = M_POLL_KIND_DISCLOSED; -var M_POLL_KIND_UNDISCLOSED = new _NamespacedValue.UnstableValue("m.poll.undisclosed", "org.matrix.msc3381.poll.undisclosed"); -/** - * Any poll kind. - */ - -exports.M_POLL_KIND_UNDISCLOSED = M_POLL_KIND_UNDISCLOSED; - -/** - * The namespaced value for m.poll.start - */ -var M_POLL_START = new _NamespacedValue.UnstableValue("m.poll.start", "org.matrix.msc3381.poll.start"); -/** - * The m.poll.start type within event content - */ - -exports.M_POLL_START = M_POLL_START; - -/** - * The namespaced value for m.poll.response - */ -var M_POLL_RESPONSE = new _NamespacedValue.UnstableValue("m.poll.response", "org.matrix.msc3381.poll.response"); -/** - * The m.poll.response type within event content - */ - -exports.M_POLL_RESPONSE = M_POLL_RESPONSE; - -/** - * The namespaced value for m.poll.end - */ -var M_POLL_END = new _NamespacedValue.UnstableValue("m.poll.end", "org.matrix.msc3381.poll.end"); -/** - * The event definition for an m.poll.end event (in content) - */ - -exports.M_POLL_END = M_POLL_END; -},{"../NamespacedValue":156}],166:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.REFERENCE_RELATION = void 0; - -var _NamespacedValue = require("../NamespacedValue"); - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * The namespaced value for an m.reference relation - */ -var REFERENCE_RELATION = new _NamespacedValue.NamespacedValue("m.reference"); -/** - * Represents any relation type - */ - -exports.REFERENCE_RELATION = REFERENCE_RELATION; -},{"../NamespacedValue":156}],167:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _ExtensibleEvents = require("./ExtensibleEvents"); - -Object.keys(_ExtensibleEvents).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ExtensibleEvents[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ExtensibleEvents[key]; - } - }); -}); - -var _IPartialEvent = require("./IPartialEvent"); - -Object.keys(_IPartialEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IPartialEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IPartialEvent[key]; - } - }); -}); - -var _InvalidEventError = require("./InvalidEventError"); - -Object.keys(_InvalidEventError).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _InvalidEventError[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _InvalidEventError[key]; - } - }); -}); - -var _NamespacedValue = require("./NamespacedValue"); - -Object.keys(_NamespacedValue).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _NamespacedValue[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _NamespacedValue[key]; - } - }); -}); - -var _NamespacedMap = require("./NamespacedMap"); - -Object.keys(_NamespacedMap).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _NamespacedMap[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _NamespacedMap[key]; - } - }); -}); - -var _types = require("./types"); - -Object.keys(_types).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _types[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _types[key]; - } - }); -}); - -var _MessageMatchers = require("./utility/MessageMatchers"); - -Object.keys(_MessageMatchers).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _MessageMatchers[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _MessageMatchers[key]; - } - }); -}); - -var _events = require("./utility/events"); - -Object.keys(_events).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _events[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _events[key]; - } - }); -}); - -var _MRoomMessage = require("./interpreters/legacy/MRoomMessage"); - -Object.keys(_MRoomMessage).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _MRoomMessage[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _MRoomMessage[key]; - } - }); -}); - -var _MMessage = require("./interpreters/modern/MMessage"); - -Object.keys(_MMessage).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _MMessage[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _MMessage[key]; - } - }); -}); - -var _MPoll = require("./interpreters/modern/MPoll"); - -Object.keys(_MPoll).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _MPoll[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _MPoll[key]; - } - }); -}); - -var _relationship_types = require("./events/relationship_types"); - -Object.keys(_relationship_types).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _relationship_types[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _relationship_types[key]; - } - }); -}); - -var _ExtensibleEvent = require("./events/ExtensibleEvent"); - -Object.keys(_ExtensibleEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ExtensibleEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ExtensibleEvent[key]; - } - }); -}); - -var _message_types = require("./events/message_types"); - -Object.keys(_message_types).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _message_types[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _message_types[key]; - } - }); -}); - -var _MessageEvent = require("./events/MessageEvent"); - -Object.keys(_MessageEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _MessageEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _MessageEvent[key]; - } - }); -}); - -var _EmoteEvent = require("./events/EmoteEvent"); - -Object.keys(_EmoteEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _EmoteEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _EmoteEvent[key]; - } - }); -}); - -var _NoticeEvent = require("./events/NoticeEvent"); - -Object.keys(_NoticeEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _NoticeEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _NoticeEvent[key]; - } - }); -}); - -var _poll_types = require("./events/poll_types"); - -Object.keys(_poll_types).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _poll_types[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _poll_types[key]; - } - }); -}); - -var _PollStartEvent = require("./events/PollStartEvent"); - -Object.keys(_PollStartEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _PollStartEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _PollStartEvent[key]; - } - }); -}); - -var _PollResponseEvent = require("./events/PollResponseEvent"); - -Object.keys(_PollResponseEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _PollResponseEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _PollResponseEvent[key]; - } - }); -}); - -var _PollEndEvent = require("./events/PollEndEvent"); - -Object.keys(_PollEndEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _PollEndEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _PollEndEvent[key]; - } - }); -}); -},{"./ExtensibleEvents":152,"./IPartialEvent":153,"./InvalidEventError":154,"./NamespacedMap":155,"./NamespacedValue":156,"./events/EmoteEvent":157,"./events/ExtensibleEvent":158,"./events/MessageEvent":159,"./events/NoticeEvent":160,"./events/PollEndEvent":161,"./events/PollResponseEvent":162,"./events/PollStartEvent":163,"./events/message_types":164,"./events/poll_types":165,"./events/relationship_types":166,"./interpreters/legacy/MRoomMessage":168,"./interpreters/modern/MMessage":169,"./interpreters/modern/MPoll":170,"./types":171,"./utility/MessageMatchers":172,"./utility/events":173}],168:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.LEGACY_M_ROOM_MESSAGE = void 0; -exports.parseMRoomMessage = parseMRoomMessage; - -var _MessageEvent = require("../../events/MessageEvent"); - -var _NoticeEvent = require("../../events/NoticeEvent"); - -var _EmoteEvent = require("../../events/EmoteEvent"); - -var _NamespacedValue = require("../../NamespacedValue"); - -var _message_types = require("../../events/message_types"); - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -var LEGACY_M_ROOM_MESSAGE = new _NamespacedValue.NamespacedValue("m.room.message"); -exports.LEGACY_M_ROOM_MESSAGE = LEGACY_M_ROOM_MESSAGE; - -function parseMRoomMessage(wireEvent) { - var _wireEvent$content, _wireEvent$content2, _wireEvent$content3; - - if (_message_types.M_MESSAGE.findIn(wireEvent.content) || _message_types.M_TEXT.findIn(wireEvent.content)) { - // We know enough about the event to coerce it into the right type - return new _MessageEvent.MessageEvent(wireEvent); - } - - var msgtype = (_wireEvent$content = wireEvent.content) === null || _wireEvent$content === void 0 ? void 0 : _wireEvent$content.msgtype; - var text = (_wireEvent$content2 = wireEvent.content) === null || _wireEvent$content2 === void 0 ? void 0 : _wireEvent$content2.body; - var html = ((_wireEvent$content3 = wireEvent.content) === null || _wireEvent$content3 === void 0 ? void 0 : _wireEvent$content3.format) === "org.matrix.custom.html" ? wireEvent.content.formatted_body : null; - - if (msgtype === "m.text") { - var _objectSpread2; - - return new _MessageEvent.MessageEvent(_objectSpread(_objectSpread({}, wireEvent), {}, { - content: _objectSpread(_objectSpread({}, wireEvent.content), {}, (_objectSpread2 = {}, _defineProperty(_objectSpread2, _message_types.M_TEXT.name, text), _defineProperty(_objectSpread2, _message_types.M_HTML.name, html), _objectSpread2)) - })); - } else if (msgtype === "m.notice") { - var _objectSpread3; - - return new _NoticeEvent.NoticeEvent(_objectSpread(_objectSpread({}, wireEvent), {}, { - content: _objectSpread(_objectSpread({}, wireEvent.content), {}, (_objectSpread3 = {}, _defineProperty(_objectSpread3, _message_types.M_TEXT.name, text), _defineProperty(_objectSpread3, _message_types.M_HTML.name, html), _objectSpread3)) - })); - } else if (msgtype === "m.emote") { - var _objectSpread4; - - return new _EmoteEvent.EmoteEvent(_objectSpread(_objectSpread({}, wireEvent), {}, { - content: _objectSpread(_objectSpread({}, wireEvent.content), {}, (_objectSpread4 = {}, _defineProperty(_objectSpread4, _message_types.M_TEXT.name, text), _defineProperty(_objectSpread4, _message_types.M_HTML.name, html), _objectSpread4)) - })); - } else { - // TODO: Handle other types - return null; - } -} -},{"../../NamespacedValue":156,"../../events/EmoteEvent":157,"../../events/MessageEvent":159,"../../events/NoticeEvent":160,"../../events/message_types":164}],169:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.parseMMessage = parseMMessage; - -var _MessageEvent = require("../../events/MessageEvent"); - -var _message_types = require("../../events/message_types"); - -var _EmoteEvent = require("../../events/EmoteEvent"); - -var _NoticeEvent = require("../../events/NoticeEvent"); - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -function parseMMessage(wireEvent) { - if (_message_types.M_EMOTE.matches(wireEvent.type)) { - return new _EmoteEvent.EmoteEvent(wireEvent); - } else if (_message_types.M_NOTICE.matches(wireEvent.type)) { - return new _NoticeEvent.NoticeEvent(wireEvent); - } // default: return a generic message - - - return new _MessageEvent.MessageEvent(wireEvent); -} -},{"../../events/EmoteEvent":157,"../../events/MessageEvent":159,"../../events/NoticeEvent":160,"../../events/message_types":164}],170:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.parseMPoll = parseMPoll; - -var _poll_types = require("../../events/poll_types"); - -var _PollStartEvent = require("../../events/PollStartEvent"); - -var _PollResponseEvent = require("../../events/PollResponseEvent"); - -var _PollEndEvent = require("../../events/PollEndEvent"); - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -function parseMPoll(wireEvent) { - if (_poll_types.M_POLL_START.matches(wireEvent.type)) { - return new _PollStartEvent.PollStartEvent(wireEvent); - } else if (_poll_types.M_POLL_RESPONSE.matches(wireEvent.type)) { - return new _PollResponseEvent.PollResponseEvent(wireEvent); - } else if (_poll_types.M_POLL_END.matches(wireEvent.type)) { - return new _PollEndEvent.PollEndEvent(wireEvent); - } - - return null; // not a poll event -} -},{"../../events/PollEndEvent":161,"../../events/PollResponseEvent":162,"../../events/PollStartEvent":163,"../../events/poll_types":165}],171:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isOptionalAString = isOptionalAString; -exports.isProvided = isProvided; - -/* -Copyright 2021 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Represents an optional type: can either be T or a falsy value. - */ - -/** - * Determines if the given optional string is a defined string. - * @param {Optional} s The input string. - * @returns {boolean} True if the input is a defined string. - */ -function isOptionalAString(s) { - return isProvided(s) && typeof s === 'string'; -} -/** - * Determines if the given optional was provided a value. - * @param {Optional} s The optional to test. - * @returns {boolean} True if the value is defined. - */ - - -function isProvided(s) { - return s !== null && s !== undefined; -} -/** - * Represents either just T1, just T2, or T1 and T2 mixed. - */ -},{}],172:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.LegacyMsgType = void 0; -exports.isEventLike = isEventLike; - -var _message_types = require("../events/message_types"); - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Represents a legacy m.room.message msgtype - */ -var LegacyMsgType; -/** - * Determines if the given partial event looks similar enough to the given legacy msgtype - * to count as that message type. - * @param {IPartialEvent>} event The event. - * @param {LegacyMsgType} msgtype The message type to compare for. - * @returns {boolean} True if the event appears to look similar enough to the msgtype. - */ - -exports.LegacyMsgType = LegacyMsgType; - -(function (LegacyMsgType) { - LegacyMsgType["Text"] = "m.text"; - LegacyMsgType["Notice"] = "m.notice"; - LegacyMsgType["Emote"] = "m.emote"; -})(LegacyMsgType || (exports.LegacyMsgType = LegacyMsgType = {})); - -function isEventLike(event, msgtype) { - var content = event.content; - - if (msgtype === LegacyMsgType.Text) { - return _message_types.M_MESSAGE.matches(event.type) || event.type === "m.room.message" && (content === null || content === void 0 ? void 0 : content['msgtype']) === "m.text"; - } else if (msgtype === LegacyMsgType.Emote) { - return _message_types.M_EMOTE.matches(event.type) || event.type === "m.room.message" && (content === null || content === void 0 ? void 0 : content['msgtype']) === "m.emote"; - } else if (msgtype === LegacyMsgType.Notice) { - return _message_types.M_NOTICE.matches(event.type) || event.type === "m.room.message" && (content === null || content === void 0 ? void 0 : content['msgtype']) === "m.notice"; - } - - return false; -} -},{"../events/message_types":164}],173:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isEventTypeSame = isEventTypeSame; - -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * Represents a potentially namespaced event type. - */ - -/** - * Determines if two event types are the same, including namespaces. - * @param {EventType} given The given event type. This will be compared - * against the expected type. - * @param {EventType} expected The expected event type. - * @returns {boolean} True if the given type matches the expected type. - */ -function isEventTypeSame(given, expected) { - if (typeof given === "string") { - if (typeof expected === "string") { - return expected === given; - } else { - return expected.matches(given); - } - } else { - if (typeof expected === "string") { - return given.matches(expected); - } else { - var expectedNs = expected; - var givenNs = given; - return expectedNs.matches(givenNs.name) || expectedNs.matches(givenNs.altName); - } - } -} -},{}],174:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ClientWidgetApi = void 0; - -var _events = require("events"); - -var _PostmessageTransport = require("./transport/PostmessageTransport"); - -var _WidgetApiDirection = require("./interfaces/WidgetApiDirection"); - -var _WidgetApiAction = require("./interfaces/WidgetApiAction"); - -var _Capabilities = require("./interfaces/Capabilities"); - -var _ApiVersion = require("./interfaces/ApiVersion"); - -var _WidgetEventCapability = require("./models/WidgetEventCapability"); - -var _GetOpenIDAction = require("./interfaces/GetOpenIDAction"); - -var _SimpleObservable = require("./util/SimpleObservable"); - -var _Symbols = require("./Symbols"); - -function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function define(obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return generator._invoke = function (innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; }(innerFn, self, context), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == _typeof(value) && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; this._invoke = function (method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); }; } function maybeInvokeDelegate(delegate, context) { var method = delegate.iterator[context.method]; if (undefined === method) { if (context.delegate = null, "throw" === context.method) { if (delegate.iterator["return"] && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method)) return ContinueSentinel; context.method = "throw", context.arg = new TypeError("The iterator does not provide a 'throw' method"); } return ContinueSentinel; } var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) { if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; } return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, define(Gp, "constructor", GeneratorFunctionPrototype), define(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (object) { var keys = []; for (var key in object) { keys.push(key); } return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function reset(skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) { "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); } }, stop: function stop() { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function dispatchException(exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function abrupt(type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function complete(record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function finish(finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, "catch": function _catch(tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } - -function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } - -function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } - -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -function _asyncIterator(iterable) { var method, async, sync, retry = 2; for ("undefined" != typeof Symbol && (async = Symbol.asyncIterator, sync = Symbol.iterator); retry--;) { if (async && null != (method = iterable[async])) return method.call(iterable); if (sync && null != (method = iterable[sync])) return new AsyncFromSyncIterator(method.call(iterable)); async = "@@asyncIterator", sync = "@@iterator"; } throw new TypeError("Object is not async iterable"); } - -function AsyncFromSyncIterator(s) { function AsyncFromSyncIteratorContinuation(r) { if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object.")); var done = r.done; return Promise.resolve(r.value).then(function (value) { return { value: value, done: done }; }); } return AsyncFromSyncIterator = function AsyncFromSyncIterator(s) { this.s = s, this.n = s.next; }, AsyncFromSyncIterator.prototype = { s: null, n: null, next: function next() { return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments)); }, "return": function _return(value) { var ret = this.s["return"]; return void 0 === ret ? Promise.resolve({ value: value, done: !0 }) : AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments)); }, "throw": function _throw(value) { var thr = this.s["return"]; return void 0 === thr ? Promise.reject(value) : AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments)); } }, new AsyncFromSyncIterator(s); } - -/** - * API handler for the client side of widgets. This raises events - * for each action received as `action:${action}` (eg: "action:screenshot"). - * Default handling can be prevented by using preventDefault() on the - * raised event. The default handling varies for each action: ones - * which the SDK can handle safely are acknowledged appropriately and - * ones which are unhandled (custom or require the client to do something) - * are rejected with an error. - * - * Events which are preventDefault()ed must reply using the transport. - * The events raised will have a default of an IWidgetApiRequest - * interface. - * - * When the ClientWidgetApi is ready to start sending requests, it will - * raise a "ready" CustomEvent. After the ready event fires, actions can - * be sent and the transport will be ready. - * - * When the widget has indicated it has loaded, this class raises a - * "preparing" CustomEvent. The preparing event does not indicate that - * the widget is ready to receive communications - that is signified by - * the ready event exclusively. - * - * This class only handles one widget at a time. - */ -var ClientWidgetApi = /*#__PURE__*/function (_EventEmitter) { - _inherits(ClientWidgetApi, _EventEmitter); - - var _super = _createSuper(ClientWidgetApi); - - // contentLoadedActionSent is used to check that only one ContentLoaded request is send. - - /** - * Creates a new client widget API. This will instantiate the transport - * and start everything. When the iframe is loaded under the widget's - * conditions, a "ready" event will be raised. - * @param {Widget} widget The widget to communicate with. - * @param {HTMLIFrameElement} iframe The iframe the widget is in. - * @param {WidgetDriver} driver The driver for this widget/client. - */ - function ClientWidgetApi(widget, iframe, driver) { - var _this; - - _classCallCheck(this, ClientWidgetApi); - - _this = _super.call(this); - _this.widget = widget; - _this.iframe = iframe; - _this.driver = driver; - - _defineProperty(_assertThisInitialized(_this), "transport", void 0); - - _defineProperty(_assertThisInitialized(_this), "contentLoadedActionSent", false); - - _defineProperty(_assertThisInitialized(_this), "allowedCapabilities", new Set()); - - _defineProperty(_assertThisInitialized(_this), "allowedEvents", []); - - _defineProperty(_assertThisInitialized(_this), "isStopped", false); - - _defineProperty(_assertThisInitialized(_this), "turnServers", null); - - if (!(iframe !== null && iframe !== void 0 && iframe.contentWindow)) { - throw new Error("No iframe supplied"); - } - - if (!widget) { - throw new Error("Invalid widget"); - } - - if (!driver) { - throw new Error("Invalid driver"); - } - - _this.transport = new _PostmessageTransport.PostmessageTransport(_WidgetApiDirection.WidgetApiDirection.ToWidget, widget.id, iframe.contentWindow, window); - _this.transport.targetOrigin = widget.origin; - - _this.transport.on("message", _this.handleMessage.bind(_assertThisInitialized(_this))); - - iframe.addEventListener("load", _this.onIframeLoad.bind(_assertThisInitialized(_this))); - - _this.transport.start(); - - return _this; - } - - _createClass(ClientWidgetApi, [{ - key: "hasCapability", - value: function hasCapability(capability) { - return this.allowedCapabilities.has(capability); - } - }, { - key: "canUseRoomTimeline", - value: function canUseRoomTimeline(roomId) { - return this.hasCapability("org.matrix.msc2762.timeline:".concat(_Symbols.Symbols.AnyRoom)) || this.hasCapability("org.matrix.msc2762.timeline:".concat(roomId)); - } - }, { - key: "canSendRoomEvent", - value: function canSendRoomEvent(eventType) { - var msgtype = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - return this.allowedEvents.some(function (e) { - return e.matchesAsRoomEvent(_WidgetEventCapability.EventDirection.Send, eventType, msgtype); - }); - } - }, { - key: "canSendStateEvent", - value: function canSendStateEvent(eventType, stateKey) { - return this.allowedEvents.some(function (e) { - return e.matchesAsStateEvent(_WidgetEventCapability.EventDirection.Send, eventType, stateKey); - }); - } - }, { - key: "canSendToDeviceEvent", - value: function canSendToDeviceEvent(eventType) { - return this.allowedEvents.some(function (e) { - return e.matchesAsToDeviceEvent(_WidgetEventCapability.EventDirection.Send, eventType); - }); - } - }, { - key: "canReceiveRoomEvent", - value: function canReceiveRoomEvent(eventType) { - var msgtype = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - return this.allowedEvents.some(function (e) { - return e.matchesAsRoomEvent(_WidgetEventCapability.EventDirection.Receive, eventType, msgtype); - }); - } - }, { - key: "canReceiveStateEvent", - value: function canReceiveStateEvent(eventType, stateKey) { - return this.allowedEvents.some(function (e) { - return e.matchesAsStateEvent(_WidgetEventCapability.EventDirection.Receive, eventType, stateKey); - }); - } - }, { - key: "canReceiveToDeviceEvent", - value: function canReceiveToDeviceEvent(eventType) { - return this.allowedEvents.some(function (e) { - return e.matchesAsToDeviceEvent(_WidgetEventCapability.EventDirection.Receive, eventType); - }); - } - }, { - key: "stop", - value: function stop() { - this.isStopped = true; - this.transport.stop(); - } - }, { - key: "beginCapabilities", - value: function beginCapabilities() { - var _this2 = this; - - // widget has loaded - tell all the listeners that - this.emit("preparing"); - var requestedCaps; - this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.Capabilities, {}).then(function (caps) { - requestedCaps = caps.capabilities; - return _this2.driver.validateCapabilities(new Set(caps.capabilities)); - }).then(function (allowedCaps) { - console.log("Widget ".concat(_this2.widget.id, " is allowed capabilities:"), Array.from(allowedCaps)); - _this2.allowedCapabilities = allowedCaps; - _this2.allowedEvents = _WidgetEventCapability.WidgetEventCapability.findEventCapabilities(allowedCaps); - - _this2.notifyCapabilities(requestedCaps); - - _this2.emit("ready"); - }); - } - }, { - key: "notifyCapabilities", - value: function notifyCapabilities(requested) { - var _this3 = this; - - this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.NotifyCapabilities, { - requested: requested, - approved: Array.from(this.allowedCapabilities) - })["catch"](function (e) { - console.warn("non-fatal error notifying widget of approved capabilities:", e); - }).then(function () { - _this3.emit("capabilitiesNotified"); - }); - } - }, { - key: "onIframeLoad", - value: function onIframeLoad(ev) { - if (this.widget.waitForIframeLoad) { - // If the widget is set to waitForIframeLoad the capabilities immediatly get setup after load. - // The client does not wait for the ContentLoaded action. - this.beginCapabilities(); - } else { - // Reaching this means, that the Iframe got reloaded/loaded and - // the clientApi is awaiting the FIRST ContentLoaded action. - this.contentLoadedActionSent = false; - } - } - }, { - key: "handleContentLoadedAction", - value: function handleContentLoadedAction(action) { - if (this.contentLoadedActionSent) { - throw new Error("Improper sequence: ContentLoaded Action can only be send once after the widget loaded " + "and should only be used if waitForIframeLoad is false (default=true)"); - } - - if (this.widget.waitForIframeLoad) { - this.transport.reply(action, { - error: { - message: "Improper sequence: not expecting ContentLoaded event if " + "waitForIframLoad is true (default=true)" - } - }); - } else { - this.transport.reply(action, {}); - this.beginCapabilities(); - } - - this.contentLoadedActionSent = true; - } - }, { - key: "replyVersions", - value: function replyVersions(request) { - this.transport.reply(request, { - supported_versions: _ApiVersion.CurrentApiVersions - }); - } - }, { - key: "handleCapabilitiesRenegotiate", - value: function handleCapabilitiesRenegotiate(request) { - var _request$data, - _this4 = this; - - // acknowledge first - this.transport.reply(request, {}); - var requested = ((_request$data = request.data) === null || _request$data === void 0 ? void 0 : _request$data.capabilities) || []; - var newlyRequested = new Set(requested.filter(function (r) { - return !_this4.hasCapability(r); - })); - - if (newlyRequested.size === 0) { - // Nothing to do - notify capabilities - return this.notifyCapabilities([]); - } - - this.driver.validateCapabilities(newlyRequested).then(function (allowed) { - allowed.forEach(function (c) { - return _this4.allowedCapabilities.add(c); - }); - - var allowedEvents = _WidgetEventCapability.WidgetEventCapability.findEventCapabilities(allowed); - - allowedEvents.forEach(function (c) { - return _this4.allowedEvents.push(c); - }); - return _this4.notifyCapabilities(Array.from(newlyRequested)); - }); - } - }, { - key: "handleNavigate", - value: function handleNavigate(request) { - var _request$data2, - _request$data3, - _this5 = this; - - if (!this.hasCapability(_Capabilities.MatrixCapabilities.MSC2931Navigate)) { - return this.transport.reply(request, { - error: { - message: "Missing capability" - } - }); - } - - if (!((_request$data2 = request.data) !== null && _request$data2 !== void 0 && _request$data2.uri) || !((_request$data3 = request.data) !== null && _request$data3 !== void 0 && _request$data3.uri.toString().startsWith("https://matrix.to/#"))) { - return this.transport.reply(request, { - error: { - message: "Invalid matrix.to URI" - } - }); - } - - var onErr = function onErr(e) { - console.error("[ClientWidgetApi] Failed to handle navigation: ", e); - return _this5.transport.reply(request, { - error: { - message: "Error handling navigation" - } - }); - }; - - try { - this.driver.navigate(request.data.uri.toString())["catch"](function (e) { - return onErr(e); - }).then(function () { - return _this5.transport.reply(request, {}); - }); - } catch (e) { - return onErr(e); - } - } - }, { - key: "handleOIDC", - value: function handleOIDC(request) { - var _this6 = this; - - var phase = 1; // 1 = initial request, 2 = after user manual confirmation - - var replyState = function replyState(state, credential) { - credential = credential || {}; - - if (phase > 1) { - return _this6.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.OpenIDCredentials, _objectSpread({ - state: state, - original_request_id: request.requestId - }, credential)); - } else { - return _this6.transport.reply(request, _objectSpread({ - state: state - }, credential)); - } - }; - - var replyError = function replyError(msg) { - console.error("[ClientWidgetApi] Failed to handle OIDC: ", msg); - - if (phase > 1) { - // We don't have a way to indicate that a random error happened in this flow, so - // just block the attempt. - return replyState(_GetOpenIDAction.OpenIDRequestState.Blocked); - } else { - return _this6.transport.reply(request, { - error: { - message: msg - } - }); - } - }; - - var observer = new _SimpleObservable.SimpleObservable(function (update) { - if (update.state === _GetOpenIDAction.OpenIDRequestState.PendingUserConfirmation && phase > 1) { - observer.close(); - return replyError("client provided out-of-phase response to OIDC flow"); - } - - if (update.state === _GetOpenIDAction.OpenIDRequestState.PendingUserConfirmation) { - replyState(update.state); - phase++; - return; - } - - if (update.state === _GetOpenIDAction.OpenIDRequestState.Allowed && !update.token) { - return replyError("client provided invalid OIDC token for an allowed request"); - } - - if (update.state === _GetOpenIDAction.OpenIDRequestState.Blocked) { - update.token = undefined; // just in case the client did something weird - } - - observer.close(); - return replyState(update.state, update.token); - }); - this.driver.askOpenID(observer); - } - }, { - key: "handleReadEvents", - value: function handleReadEvents(request) { - var _this7 = this; - - if (!request.data.type) { - return this.transport.reply(request, { - error: { - message: "Invalid request - missing event type" - } - }); - } - - if (request.data.limit !== undefined && (!request.data.limit || request.data.limit < 0)) { - return this.transport.reply(request, { - error: { - message: "Invalid request - limit out of range" - } - }); - } - - var askRoomIds = null; // null denotes current room only - - if (request.data.room_ids) { - askRoomIds = request.data.room_ids; - - if (!Array.isArray(askRoomIds)) { - askRoomIds = [askRoomIds]; - } - - var _iterator2 = _createForOfIteratorHelper(askRoomIds), - _step2; - - try { - for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { - var roomId = _step2.value; - - if (!this.canUseRoomTimeline(roomId)) { - return this.transport.reply(request, { - error: { - message: "Unable to access room timeline: ".concat(roomId) - } - }); - } - } - } catch (err) { - _iterator2.e(err); - } finally { - _iterator2.f(); - } - } - - var limit = request.data.limit || 0; - var events = Promise.resolve([]); - - if (request.data.state_key !== undefined) { - var stateKey = request.data.state_key === true ? undefined : request.data.state_key.toString(); - - if (!this.canReceiveStateEvent(request.data.type, stateKey !== null && stateKey !== void 0 ? stateKey : null)) { - return this.transport.reply(request, { - error: { - message: "Cannot read state events of this type" - } - }); - } - - events = this.driver.readStateEvents(request.data.type, stateKey, limit, askRoomIds); - } else { - if (!this.canReceiveRoomEvent(request.data.type, request.data.msgtype)) { - return this.transport.reply(request, { - error: { - message: "Cannot read room events of this type" - } - }); - } - - events = this.driver.readRoomEvents(request.data.type, request.data.msgtype, limit, askRoomIds); - } - - return events.then(function (evs) { - return _this7.transport.reply(request, { - events: evs - }); - }); - } - }, { - key: "handleSendEvent", - value: function handleSendEvent(request) { - var _this8 = this; - - if (!request.data.type) { - return this.transport.reply(request, { - error: { - message: "Invalid request - missing event type" - } - }); - } - - if (!!request.data.room_id && !this.canUseRoomTimeline(request.data.room_id)) { - return this.transport.reply(request, { - error: { - message: "Unable to access room timeline: ".concat(request.data.room_id) - } - }); - } - - var isState = request.data.state_key !== null && request.data.state_key !== undefined; - var sendEventPromise; - - if (isState) { - if (!this.canSendStateEvent(request.data.type, request.data.state_key)) { - return this.transport.reply(request, { - error: { - message: "Cannot send state events of this type" - } - }); - } - - sendEventPromise = this.driver.sendEvent(request.data.type, request.data.content || {}, request.data.state_key, request.data.room_id); - } else { - var content = request.data.content || {}; - var msgtype = content['msgtype']; - - if (!this.canSendRoomEvent(request.data.type, msgtype)) { - return this.transport.reply(request, { - error: { - message: "Cannot send room events of this type" - } - }); - } - - sendEventPromise = this.driver.sendEvent(request.data.type, content, null, // not sending a state event - request.data.room_id); - } - - sendEventPromise.then(function (sentEvent) { - return _this8.transport.reply(request, { - room_id: sentEvent.roomId, - event_id: sentEvent.eventId - }); - })["catch"](function (e) { - console.error("error sending event: ", e); - return _this8.transport.reply(request, { - error: { - message: "Error sending event" - } - }); - }); - } - }, { - key: "handleSendToDevice", - value: function () { - var _handleSendToDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(request) { - return _regeneratorRuntime().wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - if (request.data.type) { - _context.next = 5; - break; - } - - _context.next = 3; - return this.transport.reply(request, { - error: { - message: "Invalid request - missing event type" - } - }); - - case 3: - _context.next = 32; - break; - - case 5: - if (request.data.messages) { - _context.next = 10; - break; - } - - _context.next = 8; - return this.transport.reply(request, { - error: { - message: "Invalid request - missing event contents" - } - }); - - case 8: - _context.next = 32; - break; - - case 10: - if (!(typeof request.data.encrypted !== "boolean")) { - _context.next = 15; - break; - } - - _context.next = 13; - return this.transport.reply(request, { - error: { - message: "Invalid request - missing encryption flag" - } - }); - - case 13: - _context.next = 32; - break; - - case 15: - if (this.canSendToDeviceEvent(request.data.type)) { - _context.next = 20; - break; - } - - _context.next = 18; - return this.transport.reply(request, { - error: { - message: "Cannot send to-device events of this type" - } - }); - - case 18: - _context.next = 32; - break; - - case 20: - _context.prev = 20; - _context.next = 23; - return this.driver.sendToDevice(request.data.type, request.data.encrypted, request.data.messages); - - case 23: - _context.next = 25; - return this.transport.reply(request, {}); - - case 25: - _context.next = 32; - break; - - case 27: - _context.prev = 27; - _context.t0 = _context["catch"](20); - console.error("error sending to-device event", _context.t0); - _context.next = 32; - return this.transport.reply(request, { - error: { - message: "Error sending event" - } - }); - - case 32: - case "end": - return _context.stop(); - } - } - }, _callee, this, [[20, 27]]); - })); - - function handleSendToDevice(_x) { - return _handleSendToDevice.apply(this, arguments); - } - - return handleSendToDevice; - }() - }, { - key: "pollTurnServers", - value: function () { - var _pollTurnServers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(turnServers, initialServer) { - var _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, server; - - return _regeneratorRuntime().wrap(function _callee2$(_context2) { - while (1) { - switch (_context2.prev = _context2.next) { - case 0: - _context2.prev = 0; - _context2.next = 3; - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.UpdateTurnServers, initialServer // it's compatible, but missing the index signature - ); - - case 3: - // Pick the generator up where we left off - _iteratorAbruptCompletion = false; - _didIteratorError = false; - _context2.prev = 5; - _iterator = _asyncIterator(turnServers); - - case 7: - _context2.next = 9; - return _iterator.next(); - - case 9: - if (!(_iteratorAbruptCompletion = !(_step = _context2.sent).done)) { - _context2.next = 16; - break; - } - - server = _step.value; - _context2.next = 13; - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.UpdateTurnServers, server // it's compatible, but missing the index signature - ); - - case 13: - _iteratorAbruptCompletion = false; - _context2.next = 7; - break; - - case 16: - _context2.next = 22; - break; - - case 18: - _context2.prev = 18; - _context2.t0 = _context2["catch"](5); - _didIteratorError = true; - _iteratorError = _context2.t0; - - case 22: - _context2.prev = 22; - _context2.prev = 23; - - if (!(_iteratorAbruptCompletion && _iterator["return"] != null)) { - _context2.next = 27; - break; - } - - _context2.next = 27; - return _iterator["return"](); - - case 27: - _context2.prev = 27; - - if (!_didIteratorError) { - _context2.next = 30; - break; - } - - throw _iteratorError; - - case 30: - return _context2.finish(27); - - case 31: - return _context2.finish(22); - - case 32: - _context2.next = 37; - break; - - case 34: - _context2.prev = 34; - _context2.t1 = _context2["catch"](0); - console.error("error polling for TURN servers", _context2.t1); - - case 37: - case "end": - return _context2.stop(); - } - } - }, _callee2, this, [[0, 34], [5, 18, 22, 32], [23,, 27, 31]]); - })); - - function pollTurnServers(_x2, _x3) { - return _pollTurnServers.apply(this, arguments); - } - - return pollTurnServers; - }() - }, { - key: "handleWatchTurnServers", - value: function () { - var _handleWatchTurnServers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(request) { - var turnServers, _yield$turnServers$ne, done, value; - - return _regeneratorRuntime().wrap(function _callee3$(_context3) { - while (1) { - switch (_context3.prev = _context3.next) { - case 0: - if (this.hasCapability(_Capabilities.MatrixCapabilities.MSC3846TurnServers)) { - _context3.next = 5; - break; - } - - _context3.next = 3; - return this.transport.reply(request, { - error: { - message: "Missing capability" - } - }); - - case 3: - _context3.next = 30; - break; - - case 5: - if (!this.turnServers) { - _context3.next = 10; - break; - } - - _context3.next = 8; - return this.transport.reply(request, {}); - - case 8: - _context3.next = 30; - break; - - case 10: - _context3.prev = 10; - turnServers = this.driver.getTurnServers(); // Peek at the first result, so we can at least verify that the - // client isn't banned from getting TURN servers entirely - - _context3.next = 14; - return turnServers.next(); - - case 14: - _yield$turnServers$ne = _context3.sent; - done = _yield$turnServers$ne.done; - value = _yield$turnServers$ne.value; - - if (!done) { - _context3.next = 19; - break; - } - - throw new Error("Client refuses to provide any TURN servers"); - - case 19: - _context3.next = 21; - return this.transport.reply(request, {}); - - case 21: - // Start the poll loop, sending the widget the initial result - this.pollTurnServers(turnServers, value); - this.turnServers = turnServers; - _context3.next = 30; - break; - - case 25: - _context3.prev = 25; - _context3.t0 = _context3["catch"](10); - console.error("error getting first TURN server results", _context3.t0); - _context3.next = 30; - return this.transport.reply(request, { - error: { - message: "TURN servers not available" - } - }); - - case 30: - case "end": - return _context3.stop(); - } - } - }, _callee3, this, [[10, 25]]); - })); - - function handleWatchTurnServers(_x4) { - return _handleWatchTurnServers.apply(this, arguments); - } - - return handleWatchTurnServers; - }() - }, { - key: "handleUnwatchTurnServers", - value: function () { - var _handleUnwatchTurnServers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(request) { - return _regeneratorRuntime().wrap(function _callee4$(_context4) { - while (1) { - switch (_context4.prev = _context4.next) { - case 0: - if (this.hasCapability(_Capabilities.MatrixCapabilities.MSC3846TurnServers)) { - _context4.next = 5; - break; - } - - _context4.next = 3; - return this.transport.reply(request, { - error: { - message: "Missing capability" - } - }); - - case 3: - _context4.next = 15; - break; - - case 5: - if (this.turnServers) { - _context4.next = 10; - break; - } - - _context4.next = 8; - return this.transport.reply(request, {}); - - case 8: - _context4.next = 15; - break; - - case 10: - _context4.next = 12; - return this.turnServers["return"](undefined); - - case 12: - this.turnServers = null; - _context4.next = 15; - return this.transport.reply(request, {}); - - case 15: - case "end": - return _context4.stop(); - } - } - }, _callee4, this); - })); - - function handleUnwatchTurnServers(_x5) { - return _handleUnwatchTurnServers.apply(this, arguments); - } - - return handleUnwatchTurnServers; - }() - }, { - key: "handleReadRelations", - value: function () { - var _handleReadRelations = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(request) { - var _this9 = this; - - var result, chunk; - return _regeneratorRuntime().wrap(function _callee5$(_context5) { - while (1) { - switch (_context5.prev = _context5.next) { - case 0: - if (request.data.event_id) { - _context5.next = 2; - break; - } - - return _context5.abrupt("return", this.transport.reply(request, { - error: { - message: "Invalid request - missing event ID" - } - })); - - case 2: - if (!(request.data.limit !== undefined && request.data.limit < 0)) { - _context5.next = 4; - break; - } - - return _context5.abrupt("return", this.transport.reply(request, { - error: { - message: "Invalid request - limit out of range" - } - })); - - case 4: - if (!(request.data.room_id !== undefined && !this.canUseRoomTimeline(request.data.room_id))) { - _context5.next = 6; - break; - } - - return _context5.abrupt("return", this.transport.reply(request, { - error: { - message: "Unable to access room timeline: ".concat(request.data.room_id) - } - })); - - case 6: - _context5.prev = 6; - _context5.next = 9; - return this.driver.readEventRelations(request.data.event_id, request.data.room_id, request.data.rel_type, request.data.event_type, request.data.from, request.data.to, request.data.limit, request.data.direction); - - case 9: - result = _context5.sent; - // only return events that the user has the permission to receive - chunk = result.chunk.filter(function (e) { - if (e.state_key !== undefined) { - return _this9.canReceiveStateEvent(e.type, e.state_key); - } else { - return _this9.canReceiveRoomEvent(e.type, e.content['msgtype']); - } - }); - return _context5.abrupt("return", this.transport.reply(request, { - chunk: chunk, - prev_batch: result.prevBatch, - next_batch: result.nextBatch - })); - - case 14: - _context5.prev = 14; - _context5.t0 = _context5["catch"](6); - console.error("error getting the relations", _context5.t0); - _context5.next = 19; - return this.transport.reply(request, { - error: { - message: "Unexpected error while reading relations" - } - }); - - case 19: - case "end": - return _context5.stop(); - } - } - }, _callee5, this, [[6, 14]]); - })); - - function handleReadRelations(_x6) { - return _handleReadRelations.apply(this, arguments); - } - - return handleReadRelations; - }() - }, { - key: "handleUserDirectorySearch", - value: function () { - var _handleUserDirectorySearch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(request) { - var result; - return _regeneratorRuntime().wrap(function _callee6$(_context6) { - while (1) { - switch (_context6.prev = _context6.next) { - case 0: - if (this.hasCapability(_Capabilities.MatrixCapabilities.MSC3973UserDirectorySearch)) { - _context6.next = 2; - break; - } - - return _context6.abrupt("return", this.transport.reply(request, { - error: { - message: "Missing capability" - } - })); - - case 2: - if (!(typeof request.data.search_term !== 'string')) { - _context6.next = 4; - break; - } - - return _context6.abrupt("return", this.transport.reply(request, { - error: { - message: "Invalid request - missing search term" - } - })); - - case 4: - if (!(request.data.limit !== undefined && request.data.limit < 0)) { - _context6.next = 6; - break; - } - - return _context6.abrupt("return", this.transport.reply(request, { - error: { - message: "Invalid request - limit out of range" - } - })); - - case 6: - _context6.prev = 6; - _context6.next = 9; - return this.driver.searchUserDirectory(request.data.search_term, request.data.limit); - - case 9: - result = _context6.sent; - return _context6.abrupt("return", this.transport.reply(request, { - limited: result.limited, - results: result.results.map(function (r) { - return { - user_id: r.userId, - display_name: r.displayName, - avatar_url: r.avatarUrl - }; - }) - })); - - case 13: - _context6.prev = 13; - _context6.t0 = _context6["catch"](6); - console.error("error searching in the user directory", _context6.t0); - _context6.next = 18; - return this.transport.reply(request, { - error: { - message: "Unexpected error while searching in the user directory" - } - }); - - case 18: - case "end": - return _context6.stop(); - } - } - }, _callee6, this, [[6, 13]]); - })); - - function handleUserDirectorySearch(_x7) { - return _handleUserDirectorySearch.apply(this, arguments); - } - - return handleUserDirectorySearch; - }() - }, { - key: "handleMessage", - value: function handleMessage(ev) { - if (this.isStopped) return; - var actionEv = new CustomEvent("action:".concat(ev.detail.action), { - detail: ev.detail, - cancelable: true - }); - this.emit("action:".concat(ev.detail.action), actionEv); - - if (!actionEv.defaultPrevented) { - switch (ev.detail.action) { - case _WidgetApiAction.WidgetApiFromWidgetAction.ContentLoaded: - return this.handleContentLoadedAction(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.SupportedApiVersions: - return this.replyVersions(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.SendEvent: - return this.handleSendEvent(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.SendToDevice: - return this.handleSendToDevice(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.GetOpenIDCredentials: - return this.handleOIDC(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.MSC2931Navigate: - return this.handleNavigate(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.MSC2974RenegotiateCapabilities: - return this.handleCapabilitiesRenegotiate(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.MSC2876ReadEvents: - return this.handleReadEvents(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.WatchTurnServers: - return this.handleWatchTurnServers(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.UnwatchTurnServers: - return this.handleUnwatchTurnServers(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.MSC3869ReadRelations: - return this.handleReadRelations(ev.detail); - - case _WidgetApiAction.WidgetApiFromWidgetAction.MSC3973UserDirectorySearch: - return this.handleUserDirectorySearch(ev.detail); - - default: - return this.transport.reply(ev.detail, { - error: { - message: "Unknown or unsupported action: " + ev.detail.action - } - }); - } - } - } - /** - * Takes a screenshot of the widget. - * @returns Resolves to the widget's screenshot. - * @throws Throws if there is a problem. - */ - - }, { - key: "takeScreenshot", - value: function takeScreenshot() { - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.TakeScreenshot, {}); - } - /** - * Alerts the widget to whether or not it is currently visible. - * @param {boolean} isVisible Whether the widget is visible or not. - * @returns {Promise} Resolves when the widget acknowledges the update. - */ - - }, { - key: "updateVisibility", - value: function updateVisibility(isVisible) { - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.UpdateVisibility, { - visible: isVisible - }); - } - }, { - key: "sendWidgetConfig", - value: function sendWidgetConfig(data) { - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.WidgetConfig, data).then(); - } - }, { - key: "notifyModalWidgetButtonClicked", - value: function notifyModalWidgetButtonClicked(id) { - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.ButtonClicked, { - id: id - }).then(); - } - }, { - key: "notifyModalWidgetClose", - value: function notifyModalWidgetClose(data) { - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.CloseModalWidget, data).then(); - } - /** - * Feeds an event to the widget. If the widget is not able to accept the event due to - * permissions, this will no-op and return calmly. If the widget failed to handle the - * event, this will raise an error. - * @param {IRoomEvent} rawEvent The event to (try to) send to the widget. - * @param {string} currentViewedRoomId The room ID the user is currently interacting with. - * Not the room ID of the event. - * @returns {Promise} Resolves when complete, rejects if there was an error sending. - */ - - }, { - key: "feedEvent", - value: function () { - var _feedEvent = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7(rawEvent, currentViewedRoomId) { - var _rawEvent$content; - - return _regeneratorRuntime().wrap(function _callee7$(_context7) { - while (1) { - switch (_context7.prev = _context7.next) { - case 0: - if (!(rawEvent.room_id !== currentViewedRoomId && !this.canUseRoomTimeline(rawEvent.room_id))) { - _context7.next = 2; - break; - } - - return _context7.abrupt("return"); - - case 2: - if (!(rawEvent.state_key !== undefined && rawEvent.state_key !== null)) { - _context7.next = 7; - break; - } - - if (this.canReceiveStateEvent(rawEvent.type, rawEvent.state_key)) { - _context7.next = 5; - break; - } - - return _context7.abrupt("return"); - - case 5: - _context7.next = 9; - break; - - case 7: - if (this.canReceiveRoomEvent(rawEvent.type, (_rawEvent$content = rawEvent.content) === null || _rawEvent$content === void 0 ? void 0 : _rawEvent$content["msgtype"])) { - _context7.next = 9; - break; - } - - return _context7.abrupt("return"); - - case 9: - _context7.next = 11; - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.SendEvent, rawEvent // it's compatible, but missing the index signature - ); - - case 11: - case "end": - return _context7.stop(); - } - } - }, _callee7, this); - })); - - function feedEvent(_x8, _x9) { - return _feedEvent.apply(this, arguments); - } - - return feedEvent; - }() - /** - * Feeds a to-device event to the widget. If the widget is not able to accept the - * event due to permissions, this will no-op and return calmly. If the widget failed - * to handle the event, this will raise an error. - * @param {IRoomEvent} rawEvent The event to (try to) send to the widget. - * @param {boolean} encrypted Whether the event contents were encrypted. - * @returns {Promise} Resolves when complete, rejects if there was an error sending. - */ - - }, { - key: "feedToDevice", - value: function () { - var _feedToDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8(rawEvent, encrypted) { - return _regeneratorRuntime().wrap(function _callee8$(_context8) { - while (1) { - switch (_context8.prev = _context8.next) { - case 0: - if (!this.canReceiveToDeviceEvent(rawEvent.type)) { - _context8.next = 3; - break; - } - - _context8.next = 3; - return this.transport.send(_WidgetApiAction.WidgetApiToWidgetAction.SendToDevice, // it's compatible, but missing the index signature - _objectSpread(_objectSpread({}, rawEvent), {}, { - encrypted: encrypted - })); - - case 3: - case "end": - return _context8.stop(); - } - } - }, _callee8, this); - })); - - function feedToDevice(_x10, _x11) { - return _feedToDevice.apply(this, arguments); - } - - return feedToDevice; - }() - }]); - - return ClientWidgetApi; -}(_events.EventEmitter); - -exports.ClientWidgetApi = ClientWidgetApi; -},{"./Symbols":175,"./interfaces/ApiVersion":179,"./interfaces/Capabilities":180,"./interfaces/GetOpenIDAction":183,"./interfaces/WidgetApiAction":207,"./interfaces/WidgetApiDirection":208,"./models/WidgetEventCapability":213,"./transport/PostmessageTransport":219,"./util/SimpleObservable":220,"events":105}],175:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.Symbols = void 0; - -/* - * Copyright 2021 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var Symbols; -exports.Symbols = Symbols; - -(function (Symbols) { - Symbols["AnyRoom"] = "*"; -})(Symbols || (exports.Symbols = Symbols = {})); -},{}],176:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetApi = void 0; - -var _events = require("events"); - -var _WidgetApiDirection = require("./interfaces/WidgetApiDirection"); - -var _ApiVersion = require("./interfaces/ApiVersion"); - -var _PostmessageTransport = require("./transport/PostmessageTransport"); - -var _WidgetApiAction = require("./interfaces/WidgetApiAction"); - -var _GetOpenIDAction = require("./interfaces/GetOpenIDAction"); - -var _WidgetType = require("./interfaces/WidgetType"); - -var _ModalWidgetActions = require("./interfaces/ModalWidgetActions"); - -var _WidgetEventCapability = require("./models/WidgetEventCapability"); - -var _Symbols = require("./Symbols"); - -function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function define(obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return generator._invoke = function (innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; }(innerFn, self, context), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == _typeof(value) && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; this._invoke = function (method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); }; } function maybeInvokeDelegate(delegate, context) { var method = delegate.iterator[context.method]; if (undefined === method) { if (context.delegate = null, "throw" === context.method) { if (delegate.iterator["return"] && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method)) return ContinueSentinel; context.method = "throw", context.arg = new TypeError("The iterator does not provide a 'throw' method"); } return ContinueSentinel; } var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) { if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; } return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, define(Gp, "constructor", GeneratorFunctionPrototype), define(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (object) { var keys = []; for (var key in object) { keys.push(key); } return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function reset(skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) { "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); } }, stop: function stop() { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function dispatchException(exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function abrupt(type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function complete(record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function finish(finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, "catch": function _catch(tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } - -function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } - -function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -function _awaitAsyncGenerator(value) { return new _AwaitValue(value); } - -function _wrapAsyncGenerator(fn) { return function () { return new _AsyncGenerator(fn.apply(this, arguments)); }; } - -function _AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; var wrappedAwait = value instanceof _AwaitValue; Promise.resolve(wrappedAwait ? value.wrapped : value).then(function (arg) { if (wrappedAwait) { resume(key === "return" ? "return" : "next", arg); return; } settle(result.done ? "return" : "normal", arg); }, function (err) { resume("throw", err); }); } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen["return"] !== "function") { this["return"] = undefined; } } - -_AsyncGenerator.prototype[typeof Symbol === "function" && Symbol.asyncIterator || "@@asyncIterator"] = function () { return this; }; - -_AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; - -_AsyncGenerator.prototype["throw"] = function (arg) { return this._invoke("throw", arg); }; - -_AsyncGenerator.prototype["return"] = function (arg) { return this._invoke("return", arg); }; - -function _AwaitValue(value) { this.wrapped = value; } - -/** - * API handler for widgets. This raises events for each action - * received as `action:${action}` (eg: "action:screenshot"). - * Default handling can be prevented by using preventDefault() - * on the raised event. The default handling varies for each - * action: ones which the SDK can handle safely are acknowledged - * appropriately and ones which are unhandled (custom or require - * the widget to do something) are rejected with an error. - * - * Events which are preventDefault()ed must reply using the - * transport. The events raised will have a detail of an - * IWidgetApiRequest interface. - * - * When the WidgetApi is ready to start sending requests, it will - * raise a "ready" CustomEvent. After the ready event fires, actions - * can be sent and the transport will be ready. - */ -var WidgetApi = /*#__PURE__*/function (_EventEmitter) { - _inherits(WidgetApi, _EventEmitter); - - var _super = _createSuper(WidgetApi); - - /** - * Creates a new API handler for the given widget. - * @param {string} widgetId The widget ID to listen for. If not supplied then - * the API will use the widget ID from the first valid request it receives. - * @param {string} clientOrigin The origin of the client, or null if not known. - */ - function WidgetApi() { - var _this2; - - var widgetId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var clientOrigin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - - _classCallCheck(this, WidgetApi); - - _this2 = _super.call(this); - _this2.clientOrigin = clientOrigin; - - _defineProperty(_assertThisInitialized(_this2), "transport", void 0); - - _defineProperty(_assertThisInitialized(_this2), "capabilitiesFinished", false); - - _defineProperty(_assertThisInitialized(_this2), "supportsMSC2974Renegotiate", false); - - _defineProperty(_assertThisInitialized(_this2), "requestedCapabilities", []); - - _defineProperty(_assertThisInitialized(_this2), "approvedCapabilities", void 0); - - _defineProperty(_assertThisInitialized(_this2), "cachedClientVersions", void 0); - - _defineProperty(_assertThisInitialized(_this2), "turnServerWatchers", 0); - - if (!window.parent) { - throw new Error("No parent window. This widget doesn't appear to be embedded properly."); - } - - _this2.transport = new _PostmessageTransport.PostmessageTransport(_WidgetApiDirection.WidgetApiDirection.FromWidget, widgetId, window.parent, window); - _this2.transport.targetOrigin = clientOrigin; - - _this2.transport.on("message", _this2.handleMessage.bind(_assertThisInitialized(_this2))); - - return _this2; - } - /** - * Determines if the widget was granted a particular capability. Note that on - * clients where the capabilities are not fed back to the widget this function - * will rely on requested capabilities instead. - * @param {Capability} capability The capability to check for approval of. - * @returns {boolean} True if the widget has approval for the given capability. - */ - - - _createClass(WidgetApi, [{ - key: "hasCapability", - value: function hasCapability(capability) { - if (Array.isArray(this.approvedCapabilities)) { - return this.approvedCapabilities.includes(capability); - } - - return this.requestedCapabilities.includes(capability); - } - /** - * Request a capability from the client. It is not guaranteed to be allowed, - * but will be asked for. - * @param {Capability} capability The capability to request. - * @throws Throws if the capabilities negotiation has already started and the - * widget is unable to request additional capabilities. - */ - - }, { - key: "requestCapability", - value: function requestCapability(capability) { - if (this.capabilitiesFinished && !this.supportsMSC2974Renegotiate) { - throw new Error("Capabilities have already been negotiated"); - } - - this.requestedCapabilities.push(capability); - } - /** - * Request capabilities from the client. They are not guaranteed to be allowed, - * but will be asked for if the negotiation has not already happened. - * @param {Capability[]} capabilities The capabilities to request. - * @throws Throws if the capabilities negotiation has already started. - */ - - }, { - key: "requestCapabilities", - value: function requestCapabilities(capabilities) { - var _this3 = this; - - capabilities.forEach(function (cap) { - return _this3.requestCapability(cap); - }); - } - /** - * Requests the capability to interact with rooms other than the user's currently - * viewed room. Applies to event receiving and sending. - * @param {string | Symbols.AnyRoom} roomId The room ID, or `Symbols.AnyRoom` to - * denote all known rooms. - */ - - }, { - key: "requestCapabilityForRoomTimeline", - value: function requestCapabilityForRoomTimeline(roomId) { - this.requestCapability("org.matrix.msc2762.timeline:".concat(roomId)); - } - /** - * Requests the capability to send a given state event with optional explicit - * state key. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} eventType The state event type to ask for. - * @param {string} stateKey If specified, the specific state key to request. - * Otherwise all state keys will be requested. - */ - - }, { - key: "requestCapabilityToSendState", - value: function requestCapabilityToSendState(eventType, stateKey) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forStateEvent(_WidgetEventCapability.EventDirection.Send, eventType, stateKey).raw); - } - /** - * Requests the capability to receive a given state event with optional explicit - * state key. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} eventType The state event type to ask for. - * @param {string} stateKey If specified, the specific state key to request. - * Otherwise all state keys will be requested. - */ - - }, { - key: "requestCapabilityToReceiveState", - value: function requestCapabilityToReceiveState(eventType, stateKey) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forStateEvent(_WidgetEventCapability.EventDirection.Receive, eventType, stateKey).raw); - } - /** - * Requests the capability to send a given to-device event. It is not - * guaranteed to be allowed, but will be asked for if the negotiation has - * not already happened. - * @param {string} eventType The room event type to ask for. - */ - - }, { - key: "requestCapabilityToSendToDevice", - value: function requestCapabilityToSendToDevice(eventType) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forToDeviceEvent(_WidgetEventCapability.EventDirection.Send, eventType).raw); - } - /** - * Requests the capability to receive a given to-device event. It is not - * guaranteed to be allowed, but will be asked for if the negotiation has - * not already happened. - * @param {string} eventType The room event type to ask for. - */ - - }, { - key: "requestCapabilityToReceiveToDevice", - value: function requestCapabilityToReceiveToDevice(eventType) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forToDeviceEvent(_WidgetEventCapability.EventDirection.Receive, eventType).raw); - } - /** - * Requests the capability to send a given room event. It is not guaranteed to be - * allowed, but will be asked for if the negotiation has not already happened. - * @param {string} eventType The room event type to ask for. - */ - - }, { - key: "requestCapabilityToSendEvent", - value: function requestCapabilityToSendEvent(eventType) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forRoomEvent(_WidgetEventCapability.EventDirection.Send, eventType).raw); - } - /** - * Requests the capability to receive a given room event. It is not guaranteed to be - * allowed, but will be asked for if the negotiation has not already happened. - * @param {string} eventType The room event type to ask for. - */ - - }, { - key: "requestCapabilityToReceiveEvent", - value: function requestCapabilityToReceiveEvent(eventType) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forRoomEvent(_WidgetEventCapability.EventDirection.Receive, eventType).raw); - } - /** - * Requests the capability to send a given message event with optional explicit - * `msgtype`. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} msgtype If specified, the specific msgtype to request. - * Otherwise all message types will be requested. - */ - - }, { - key: "requestCapabilityToSendMessage", - value: function requestCapabilityToSendMessage(msgtype) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forRoomMessageEvent(_WidgetEventCapability.EventDirection.Send, msgtype).raw); - } - /** - * Requests the capability to receive a given message event with optional explicit - * `msgtype`. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} msgtype If specified, the specific msgtype to request. - * Otherwise all message types will be requested. - */ - - }, { - key: "requestCapabilityToReceiveMessage", - value: function requestCapabilityToReceiveMessage(msgtype) { - this.requestCapability(_WidgetEventCapability.WidgetEventCapability.forRoomMessageEvent(_WidgetEventCapability.EventDirection.Receive, msgtype).raw); - } - /** - * Requests an OpenID Connect token from the client for the currently logged in - * user. This token can be validated server-side with the federation API. Note - * that the widget is responsible for validating the token and caching any results - * it needs. - * @returns {Promise} Resolves to a token for verification. - * @throws Throws if the user rejected the request or the request failed. - */ - - }, { - key: "requestOpenIDConnectToken", - value: function requestOpenIDConnectToken() { - var _this4 = this; - - return new Promise(function (resolve, reject) { - _this4.transport.sendComplete(_WidgetApiAction.WidgetApiFromWidgetAction.GetOpenIDCredentials, {}).then(function (response) { - var rdata = response.response; - - if (rdata.state === _GetOpenIDAction.OpenIDRequestState.Allowed) { - resolve(rdata); - } else if (rdata.state === _GetOpenIDAction.OpenIDRequestState.Blocked) { - reject(new Error("User declined to verify their identity")); - } else if (rdata.state === _GetOpenIDAction.OpenIDRequestState.PendingUserConfirmation) { - var handlerFn = function handlerFn(ev) { - ev.preventDefault(); - var request = ev.detail; - if (request.data.original_request_id !== response.requestId) return; - - if (request.data.state === _GetOpenIDAction.OpenIDRequestState.Allowed) { - resolve(request.data); - - _this4.transport.reply(request, {}); // ack - - } else if (request.data.state === _GetOpenIDAction.OpenIDRequestState.Blocked) { - reject(new Error("User declined to verify their identity")); - - _this4.transport.reply(request, {}); // ack - - } else { - reject(new Error("Invalid state on reply: " + rdata.state)); - - _this4.transport.reply(request, { - error: { - message: "Invalid state" - } - }); - } - - _this4.off("action:".concat(_WidgetApiAction.WidgetApiToWidgetAction.OpenIDCredentials), handlerFn); - }; - - _this4.on("action:".concat(_WidgetApiAction.WidgetApiToWidgetAction.OpenIDCredentials), handlerFn); - } else { - reject(new Error("Invalid state: " + rdata.state)); - } - })["catch"](reject); - }); - } - /** - * Asks the client for additional capabilities. Capabilities can be queued for this - * request with the requestCapability() functions. - * @returns {Promise} Resolves when complete. Note that the promise resolves when - * the capabilities request has gone through, not when the capabilities are approved/denied. - * Use the WidgetApiToWidgetAction.NotifyCapabilities action to detect changes. - */ - - }, { - key: "updateRequestedCapabilities", - value: function updateRequestedCapabilities() { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.MSC2974RenegotiateCapabilities, { - capabilities: this.requestedCapabilities - }).then(); - } - /** - * Tell the client that the content has been loaded. - * @returns {Promise} Resolves when the client acknowledges the request. - */ - - }, { - key: "sendContentLoaded", - value: function sendContentLoaded() { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.ContentLoaded, {}).then(); - } - /** - * Sends a sticker to the client. - * @param {IStickerActionRequestData} sticker The sticker to send. - * @returns {Promise} Resolves when the client acknowledges the request. - */ - - }, { - key: "sendSticker", - value: function sendSticker(sticker) { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.SendSticker, sticker).then(); - } - /** - * Asks the client to set the always-on-screen status for this widget. - * @param {boolean} value The new state to request. - * @returns {Promise} Resolve with true if the client was able to fulfill - * the request, resolves to false otherwise. Rejects if an error occurred. - */ - - }, { - key: "setAlwaysOnScreen", - value: function setAlwaysOnScreen(value) { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.UpdateAlwaysOnScreen, { - value: value - }).then(function (res) { - return res.success; - }); - } - /** - * Opens a modal widget. - * @param {string} url The URL to the modal widget. - * @param {string} name The name of the widget. - * @param {IModalWidgetOpenRequestDataButton[]} buttons The buttons to have on the widget. - * @param {IModalWidgetCreateData} data Data to supply to the modal widget. - * @param {WidgetType} type The type of modal widget. - * @returns {Promise} Resolves when the modal widget has been opened. - */ - - }, { - key: "openModalWidget", - value: function openModalWidget(url, name) { - var buttons = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; - var data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; - var type = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _WidgetType.MatrixWidgetType.Custom; - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.OpenModalWidget, { - type: type, - url: url, - name: name, - buttons: buttons, - data: data - }).then(); - } - /** - * Closes the modal widget. The widget's session will be terminated shortly after. - * @param {IModalWidgetReturnData} data Optional data to close the modal widget with. - * @returns {Promise} Resolves when complete. - */ - - }, { - key: "closeModalWidget", - value: function closeModalWidget() { - var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.CloseModalWidget, data).then(); - } - }, { - key: "sendRoomEvent", - value: function sendRoomEvent(eventType, content, roomId) { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.SendEvent, { - type: eventType, - content: content, - room_id: roomId - }); - } - }, { - key: "sendStateEvent", - value: function sendStateEvent(eventType, stateKey, content, roomId) { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.SendEvent, { - type: eventType, - content: content, - state_key: stateKey, - room_id: roomId - }); - } - /** - * Sends a to-device event. - * @param {string} eventType The type of events being sent. - * @param {boolean} encrypted Whether to encrypt the message contents. - * @param {Object} contentMap A map from user IDs to device IDs to message contents. - * @returns {Promise} Resolves when complete. - */ - - }, { - key: "sendToDevice", - value: function sendToDevice(eventType, encrypted, contentMap) { - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.SendToDevice, { - type: eventType, - encrypted: encrypted, - messages: contentMap - }); - } - }, { - key: "readRoomEvents", - value: function readRoomEvents(eventType, limit, msgtype, roomIds) { - var data = { - type: eventType, - msgtype: msgtype - }; - - if (limit !== undefined) { - data.limit = limit; - } - - if (roomIds) { - if (roomIds.includes(_Symbols.Symbols.AnyRoom)) { - data.room_ids = _Symbols.Symbols.AnyRoom; - } else { - data.room_ids = roomIds; - } - } - - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.MSC2876ReadEvents, data).then(function (r) { - return r.events; - }); - } - /** - * Reads all related events given a known eventId. - * @param eventId The id of the parent event to be read. - * @param roomId The room to look within. When undefined, the user's currently - * viewed room. - * @param relationType The relationship type of child events to search for. - * When undefined, all relations are returned. - * @param eventType The event type of child events to search for. When undefined, - * all related events are returned. - * @param limit The maximum number of events to retrieve per room. If not - * supplied, the server will apply a default limit. - * @param from The pagination token to start returning results from, as - * received from a previous call. If not supplied, results start at the most - * recent topological event known to the server. - * @param to The pagination token to stop returning results at. If not - * supplied, results continue up to limit or until there are no more events. - * @param direction The direction to search for according to MSC3715. - * @returns Resolves to the room relations. - */ - - }, { - key: "readEventRelations", - value: function () { - var _readEventRelations = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(eventId, roomId, relationType, eventType, limit, from, to, direction) { - var versions, data; - return _regeneratorRuntime().wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - _context.next = 2; - return this.getClientVersions(); - - case 2: - versions = _context.sent; - - if (versions.includes(_ApiVersion.UnstableApiVersion.MSC3869)) { - _context.next = 5; - break; - } - - throw new Error("The read_relations action is not supported by the client."); - - case 5: - data = { - event_id: eventId, - rel_type: relationType, - event_type: eventType, - room_id: roomId, - to: to, - from: from, - limit: limit, - direction: direction - }; - return _context.abrupt("return", this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.MSC3869ReadRelations, data)); - - case 7: - case "end": - return _context.stop(); - } - } - }, _callee, this); - })); - - function readEventRelations(_x, _x2, _x3, _x4, _x5, _x6, _x7, _x8) { - return _readEventRelations.apply(this, arguments); - } - - return readEventRelations; - }() - }, { - key: "readStateEvents", - value: function readStateEvents(eventType, limit, stateKey, roomIds) { - var data = { - type: eventType, - state_key: stateKey === undefined ? true : stateKey - }; - - if (limit !== undefined) { - data.limit = limit; - } - - if (roomIds) { - if (roomIds.includes(_Symbols.Symbols.AnyRoom)) { - data.room_ids = _Symbols.Symbols.AnyRoom; - } else { - data.room_ids = roomIds; - } - } - - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.MSC2876ReadEvents, data).then(function (r) { - return r.events; - }); - } - /** - * Sets a button as disabled or enabled on the modal widget. Buttons are enabled by default. - * @param {ModalButtonID} buttonId The button ID to enable/disable. - * @param {boolean} isEnabled Whether or not the button is enabled. - * @returns {Promise} Resolves when complete. - * @throws Throws if the button cannot be disabled, or the client refuses to disable the button. - */ - - }, { - key: "setModalButtonEnabled", - value: function setModalButtonEnabled(buttonId, isEnabled) { - if (buttonId === _ModalWidgetActions.BuiltInModalButtonID.Close) { - throw new Error("The close button cannot be disabled"); - } - - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.SetModalButtonEnabled, { - button: buttonId, - enabled: isEnabled - }).then(); - } - /** - * Attempts to navigate the client to the given URI. This can only be called with Matrix URIs - * (currently only matrix.to, but in future a Matrix URI scheme will be defined). - * @param {string} uri The URI to navigate to. - * @returns {Promise} Resolves when complete. - * @throws Throws if the URI is invalid or cannot be processed. - * @deprecated This currently relies on an unstable MSC (MSC2931). - */ - - }, { - key: "navigateTo", - value: function navigateTo(uri) { - if (!uri || !uri.startsWith("https://matrix.to/#")) { - throw new Error("Invalid matrix.to URI"); - } - - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.MSC2931Navigate, { - uri: uri - }).then(); - } - /** - * Starts watching for TURN servers, yielding an initial set of credentials as soon as possible, - * and thereafter yielding new credentials whenever the previous ones expire. - * @yields {ITurnServer} The TURN server URIs and credentials currently available to the widget. - */ - - }, { - key: "getTurnServers", - value: function getTurnServers() { - var _this = this; - - return _wrapAsyncGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() { - var setTurnServer, onUpdateTurnServers; - return _regeneratorRuntime().wrap(function _callee3$(_context3) { - while (1) { - switch (_context3.prev = _context3.next) { - case 0: - onUpdateTurnServers = /*#__PURE__*/function () { - var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(ev) { - return _regeneratorRuntime().wrap(function _callee2$(_context2) { - while (1) { - switch (_context2.prev = _context2.next) { - case 0: - ev.preventDefault(); - setTurnServer(ev.detail.data); - _context2.next = 4; - return _this.transport.reply(ev.detail, {}); - - case 4: - case "end": - return _context2.stop(); - } - } - }, _callee2); - })); - - return function onUpdateTurnServers(_x9) { - return _ref.apply(this, arguments); - }; - }(); // Start listening for updates before we even start watching, to catch - // TURN data that is sent immediately - - - _this.on("action:".concat(_WidgetApiAction.WidgetApiToWidgetAction.UpdateTurnServers), onUpdateTurnServers); // Only send the 'watch' action if we aren't already watching - - - if (!(_this.turnServerWatchers === 0)) { - _context3.next = 12; - break; - } - - _context3.prev = 3; - _context3.next = 6; - return _awaitAsyncGenerator(_this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.WatchTurnServers, {})); - - case 6: - _context3.next = 12; - break; - - case 8: - _context3.prev = 8; - _context3.t0 = _context3["catch"](3); - - _this.off("action:".concat(_WidgetApiAction.WidgetApiToWidgetAction.UpdateTurnServers), onUpdateTurnServers); - - throw _context3.t0; - - case 12: - _this.turnServerWatchers++; - _context3.prev = 13; - - case 14: - if (!true) { - _context3.next = 21; - break; - } - - _context3.next = 17; - return _awaitAsyncGenerator(new Promise(function (resolve) { - return setTurnServer = resolve; - })); - - case 17: - _context3.next = 19; - return _context3.sent; - - case 19: - _context3.next = 14; - break; - - case 21: - _context3.prev = 21; - - // The loop was broken by the caller - clean up - _this.off("action:".concat(_WidgetApiAction.WidgetApiToWidgetAction.UpdateTurnServers), onUpdateTurnServers); // Since sending the 'unwatch' action will end updates for all other - // consumers, only send it if we're the only consumer remaining - - - _this.turnServerWatchers--; - - if (!(_this.turnServerWatchers === 0)) { - _context3.next = 27; - break; - } - - _context3.next = 27; - return _awaitAsyncGenerator(_this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.UnwatchTurnServers, {})); - - case 27: - return _context3.finish(21); - - case 28: - case "end": - return _context3.stop(); - } - } - }, _callee3, null, [[3, 8], [13,, 21, 28]]); - }))(); - } - /** - * Search for users in the user directory. - * @param searchTerm The term to search for. - * @param limit The maximum number of results to return. If not supplied, the - * @returns Resolves to the search results. - */ - - }, { - key: "searchUserDirectory", - value: function () { - var _searchUserDirectory = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(searchTerm, limit) { - var versions, data; - return _regeneratorRuntime().wrap(function _callee4$(_context4) { - while (1) { - switch (_context4.prev = _context4.next) { - case 0: - _context4.next = 2; - return this.getClientVersions(); - - case 2: - versions = _context4.sent; - - if (versions.includes(_ApiVersion.UnstableApiVersion.MSC3973)) { - _context4.next = 5; - break; - } - - throw new Error("The user_directory_search action is not supported by the client."); - - case 5: - data = { - search_term: searchTerm, - limit: limit - }; - return _context4.abrupt("return", this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.MSC3973UserDirectorySearch, data)); - - case 7: - case "end": - return _context4.stop(); - } - } - }, _callee4, this); - })); - - function searchUserDirectory(_x10, _x11) { - return _searchUserDirectory.apply(this, arguments); - } - - return searchUserDirectory; - }() - /** - * Starts the communication channel. This should be done early to ensure - * that messages are not missed. Communication can only be stopped by the client. - */ - - }, { - key: "start", - value: function start() { - var _this5 = this; - - this.transport.start(); - this.getClientVersions().then(function (v) { - if (v.includes(_ApiVersion.UnstableApiVersion.MSC2974)) { - _this5.supportsMSC2974Renegotiate = true; - } - }); - } - }, { - key: "handleMessage", - value: function handleMessage(ev) { - var actionEv = new CustomEvent("action:".concat(ev.detail.action), { - detail: ev.detail, - cancelable: true - }); - this.emit("action:".concat(ev.detail.action), actionEv); - - if (!actionEv.defaultPrevented) { - switch (ev.detail.action) { - case _WidgetApiAction.WidgetApiToWidgetAction.SupportedApiVersions: - return this.replyVersions(ev.detail); - - case _WidgetApiAction.WidgetApiToWidgetAction.Capabilities: - return this.handleCapabilities(ev.detail); - - case _WidgetApiAction.WidgetApiToWidgetAction.UpdateVisibility: - return this.transport.reply(ev.detail, {}); - // ack to avoid error spam - - case _WidgetApiAction.WidgetApiToWidgetAction.NotifyCapabilities: - return this.transport.reply(ev.detail, {}); - // ack to avoid error spam - - default: - return this.transport.reply(ev.detail, { - error: { - message: "Unknown or unsupported action: " + ev.detail.action - } - }); - } - } - } - }, { - key: "replyVersions", - value: function replyVersions(request) { - this.transport.reply(request, { - supported_versions: _ApiVersion.CurrentApiVersions - }); - } - }, { - key: "getClientVersions", - value: function getClientVersions() { - var _this6 = this; - - if (Array.isArray(this.cachedClientVersions)) { - return Promise.resolve(this.cachedClientVersions); - } - - return this.transport.send(_WidgetApiAction.WidgetApiFromWidgetAction.SupportedApiVersions, {}).then(function (r) { - _this6.cachedClientVersions = r.supported_versions; - return r.supported_versions; - })["catch"](function (e) { - console.warn("non-fatal error getting supported client versions: ", e); - return []; - }); - } - }, { - key: "handleCapabilities", - value: function handleCapabilities(request) { - var _this7 = this; - - if (this.capabilitiesFinished) { - return this.transport.reply(request, { - error: { - message: "Capability negotiation already completed" - } - }); - } // See if we can expect a capabilities notification or not - - - return this.getClientVersions().then(function (v) { - if (v.includes(_ApiVersion.UnstableApiVersion.MSC2871)) { - _this7.once("action:".concat(_WidgetApiAction.WidgetApiToWidgetAction.NotifyCapabilities), function (ev) { - _this7.approvedCapabilities = ev.detail.data.approved; - - _this7.emit("ready"); - }); - } else { - // if we can't expect notification, we're as done as we can be - _this7.emit("ready"); - } // in either case, reply to that capabilities request - - - _this7.capabilitiesFinished = true; - return _this7.transport.reply(request, { - capabilities: _this7.requestedCapabilities - }); - }); - } - }]); - - return WidgetApi; -}(_events.EventEmitter); - -exports.WidgetApi = WidgetApi; -},{"./Symbols":175,"./interfaces/ApiVersion":179,"./interfaces/GetOpenIDAction":183,"./interfaces/ModalWidgetActions":193,"./interfaces/WidgetApiAction":207,"./interfaces/WidgetApiDirection":208,"./interfaces/WidgetType":211,"./models/WidgetEventCapability":213,"./transport/PostmessageTransport":219,"events":105}],177:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetDriver = void 0; - -var _ = require(".."); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -/** - * Represents the functions and behaviour the widget-api is unable to - * do, such as prompting the user for information or interacting with - * the UI. Clients are expected to implement this class and override - * any functions they need/want to support. - * - * This class assumes the client will have a context of a Widget - * instance already. - */ -var WidgetDriver = /*#__PURE__*/function () { - function WidgetDriver() { - _classCallCheck(this, WidgetDriver); - } - - _createClass(WidgetDriver, [{ - key: "validateCapabilities", - value: - /** - * Verifies the widget's requested capabilities, returning the ones - * it is approved to use. Mutating the requested capabilities will - * have no effect. - * - * This SHOULD result in the user being prompted to approve/deny - * capabilities. - * - * By default this rejects all capabilities (returns an empty set). - * @param {Set} requested The set of requested capabilities. - * @returns {Promise>} Resolves to the allowed capabilities. - */ - function validateCapabilities(requested) { - return Promise.resolve(new Set()); - } - /** - * Sends an event into a room. If `roomId` is falsy, the client should send the event - * into the room the user is currently looking at. The widget API will have already - * verified that the widget is capable of sending the event to that room. - * @param {string} eventType The event type to be sent. - * @param {*} content The content for the event. - * @param {string|null} stateKey The state key if this is a state event, otherwise null. - * May be an empty string. - * @param {string|null} roomId The room ID to send the event to. If falsy, the room the - * user is currently looking at. - * @returns {Promise} Resolves when the event has been sent with - * details of that event. - * @throws Rejected when the event could not be sent. - */ - - }, { - key: "sendEvent", - value: function sendEvent(eventType, content) { - var stateKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; - var roomId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - return Promise.reject(new Error("Failed to override function")); - } - /** - * Sends a to-device event. The widget API will have already verified that the widget - * is capable of sending the event. - * @param {string} eventType The event type to be sent. - * @param {boolean} encrypted Whether to encrypt the message contents. - * @param {Object} contentMap A map from user ID and device ID to event content. - * @returns {Promise} Resolves when the event has been sent. - * @throws Rejected when the event could not be sent. - */ - - }, { - key: "sendToDevice", - value: function sendToDevice(eventType, encrypted, contentMap) { - return Promise.reject(new Error("Failed to override function")); - } - /** - * Reads all events of the given type, and optionally `msgtype` (if applicable/defined), - * the user has access to. The widget API will have already verified that the widget is - * capable of receiving the events. Less events than the limit are allowed to be returned, - * but not more. If `roomIds` is supplied, it may contain `Symbols.AnyRoom` to denote that - * `limit` in each of the client's known rooms should be returned. When `null`, only the - * room the user is currently looking at should be considered. - * @param eventType The event type to be read. - * @param msgtype The msgtype of the events to be read, if applicable/defined. - * @param limit The maximum number of events to retrieve per room. Will be zero to denote "as many - * as possible". - * @param roomIds When null, the user's currently viewed room. Otherwise, the list of room IDs - * to look within, possibly containing Symbols.AnyRoom to denote all known rooms. - * @returns {Promise} Resolves to the room events, or an empty array. - */ - - }, { - key: "readRoomEvents", - value: function readRoomEvents(eventType, msgtype, limit) { - var roomIds = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - return Promise.resolve([]); - } - /** - * Reads all events of the given type, and optionally state key (if applicable/defined), - * the user has access to. The widget API will have already verified that the widget is - * capable of receiving the events. Less events than the limit are allowed to be returned, - * but not more. If `roomIds` is supplied, it may contain `Symbols.AnyRoom` to denote that - * `limit` in each of the client's known rooms should be returned. When `null`, only the - * room the user is currently looking at should be considered. - * @param eventType The event type to be read. - * @param stateKey The state key of the events to be read, if applicable/defined. - * @param limit The maximum number of events to retrieve. Will be zero to denote "as many - * as possible". - * @param roomIds When null, the user's currently viewed room. Otherwise, the list of room IDs - * to look within, possibly containing Symbols.AnyRoom to denote all known rooms. - * @returns {Promise} Resolves to the state events, or an empty array. - */ - - }, { - key: "readStateEvents", - value: function readStateEvents(eventType, stateKey, limit) { - var roomIds = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - return Promise.resolve([]); - } - /** - * Reads all events that are related to a given event. The widget API will - * have already verified that the widget is capable of receiving the event, - * or will make sure to reject access to events which are returned from this - * function, but are not capable of receiving. If `relationType` or `eventType` - * are set, the returned events should already be filtered. Less events than - * the limit are allowed to be returned, but not more. - * @param eventId The id of the parent event to be read. - * @param roomId The room to look within. When undefined, the user's - * currently viewed room. - * @param relationType The relationship type of child events to search for. - * When undefined, all relations are returned. - * @param eventType The event type of child events to search for. When undefined, - * all related events are returned. - * @param from The pagination token to start returning results from, as - * received from a previous call. If not supplied, results start at the most - * recent topological event known to the server. - * @param to The pagination token to stop returning results at. If not - * supplied, results continue up to limit or until there are no more events. - * @param limit The maximum number of events to retrieve per room. If not - * supplied, the server will apply a default limit. - * @param direction The direction to search for according to MSC3715 - * @returns Resolves to the room relations. - */ - - }, { - key: "readEventRelations", - value: function readEventRelations(eventId, roomId, relationType, eventType, from, to, limit, direction) { - return Promise.resolve({ - chunk: [] - }); - } - /** - * Asks the user for permission to validate their identity through OpenID Connect. The - * interface for this function is an observable which accepts the state machine of the - * OIDC exchange flow. For example, if the client/user blocks the request then it would - * feed back a `{state: Blocked}` into the observable. Similarly, if the user already - * approved the widget then a `{state: Allowed}` would be fed into the observable alongside - * the token itself. If the client is asking for permission, it should feed in a - * `{state: PendingUserConfirmation}` followed by the relevant Allowed or Blocked state. - * - * The widget API will reject the widget's request with an error if this contract is not - * met properly. By default, the widget driver will block all OIDC requests. - * @param {SimpleObservable} observer The observable to feed updates into. - */ - - }, { - key: "askOpenID", - value: function askOpenID(observer) { - observer.update({ - state: _.OpenIDRequestState.Blocked - }); - } - /** - * Navigates the client with a matrix.to URI. In future this function will also be provided - * with the Matrix URIs once matrix.to is replaced. The given URI will have already been - * lightly checked to ensure it looks like a valid URI, though the implementation is recommended - * to do further checks on the URI. - * @param {string} uri The URI to navigate to. - * @returns {Promise} Resolves when complete. - * @throws Throws if there's a problem with the navigation, such as invalid format. - */ - - }, { - key: "navigate", - value: function navigate(uri) { - throw new Error("Navigation is not implemented"); - } - /** - * Polls for TURN server data, yielding an initial set of credentials as soon as possible, and - * thereafter yielding new credentials whenever the previous ones expire. The widget API will - * have already verified that the widget has permission to access TURN servers. - * @yields {ITurnServer} The TURN server URIs and credentials currently available to the client. - */ - - }, { - key: "getTurnServers", - value: function getTurnServers() { - throw new Error("TURN server support is not implemented"); - } - /** - * Search for users in the user directory. - * @param searchTerm The term to search for. - * @param limit The maximum number of results to return. If not supplied, the - * @returns Resolves to the search results. - */ - - }, { - key: "searchUserDirectory", - value: function searchUserDirectory(searchTerm, limit) { - return Promise.resolve({ - limited: false, - results: [] - }); - } - }]); - - return WidgetDriver; -}(); - -exports.WidgetDriver = WidgetDriver; -},{"..":178}],178:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _WidgetApi = require("./WidgetApi"); - -Object.keys(_WidgetApi).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetApi[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetApi[key]; - } - }); -}); - -var _ClientWidgetApi = require("./ClientWidgetApi"); - -Object.keys(_ClientWidgetApi).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ClientWidgetApi[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ClientWidgetApi[key]; - } - }); -}); - -var _Symbols = require("./Symbols"); - -Object.keys(_Symbols).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _Symbols[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _Symbols[key]; - } - }); -}); - -var _ITransport = require("./transport/ITransport"); - -Object.keys(_ITransport).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ITransport[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ITransport[key]; - } - }); -}); - -var _PostmessageTransport = require("./transport/PostmessageTransport"); - -Object.keys(_PostmessageTransport).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _PostmessageTransport[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _PostmessageTransport[key]; - } - }); -}); - -var _ICustomWidgetData = require("./interfaces/ICustomWidgetData"); - -Object.keys(_ICustomWidgetData).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ICustomWidgetData[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ICustomWidgetData[key]; - } - }); -}); - -var _IJitsiWidgetData = require("./interfaces/IJitsiWidgetData"); - -Object.keys(_IJitsiWidgetData).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IJitsiWidgetData[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IJitsiWidgetData[key]; - } - }); -}); - -var _IStickerpickerWidgetData = require("./interfaces/IStickerpickerWidgetData"); - -Object.keys(_IStickerpickerWidgetData).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IStickerpickerWidgetData[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IStickerpickerWidgetData[key]; - } - }); -}); - -var _IWidget = require("./interfaces/IWidget"); - -Object.keys(_IWidget).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IWidget[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IWidget[key]; - } - }); -}); - -var _WidgetType = require("./interfaces/WidgetType"); - -Object.keys(_WidgetType).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetType[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetType[key]; - } - }); -}); - -var _IWidgetApiErrorResponse = require("./interfaces/IWidgetApiErrorResponse"); - -Object.keys(_IWidgetApiErrorResponse).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IWidgetApiErrorResponse[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IWidgetApiErrorResponse[key]; - } - }); -}); - -var _IWidgetApiRequest = require("./interfaces/IWidgetApiRequest"); - -Object.keys(_IWidgetApiRequest).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IWidgetApiRequest[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IWidgetApiRequest[key]; - } - }); -}); - -var _IWidgetApiResponse = require("./interfaces/IWidgetApiResponse"); - -Object.keys(_IWidgetApiResponse).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IWidgetApiResponse[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IWidgetApiResponse[key]; - } - }); -}); - -var _WidgetApiAction = require("./interfaces/WidgetApiAction"); - -Object.keys(_WidgetApiAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetApiAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetApiAction[key]; - } - }); -}); - -var _WidgetApiDirection = require("./interfaces/WidgetApiDirection"); - -Object.keys(_WidgetApiDirection).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetApiDirection[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetApiDirection[key]; - } - }); -}); - -var _ApiVersion = require("./interfaces/ApiVersion"); - -Object.keys(_ApiVersion).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ApiVersion[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ApiVersion[key]; - } - }); -}); - -var _Capabilities = require("./interfaces/Capabilities"); - -Object.keys(_Capabilities).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _Capabilities[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _Capabilities[key]; - } - }); -}); - -var _CapabilitiesAction = require("./interfaces/CapabilitiesAction"); - -Object.keys(_CapabilitiesAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _CapabilitiesAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _CapabilitiesAction[key]; - } - }); -}); - -var _ContentLoadedAction = require("./interfaces/ContentLoadedAction"); - -Object.keys(_ContentLoadedAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ContentLoadedAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ContentLoadedAction[key]; - } - }); -}); - -var _ScreenshotAction = require("./interfaces/ScreenshotAction"); - -Object.keys(_ScreenshotAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ScreenshotAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ScreenshotAction[key]; - } - }); -}); - -var _StickerAction = require("./interfaces/StickerAction"); - -Object.keys(_StickerAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _StickerAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _StickerAction[key]; - } - }); -}); - -var _StickyAction = require("./interfaces/StickyAction"); - -Object.keys(_StickyAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _StickyAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _StickyAction[key]; - } - }); -}); - -var _SupportedVersionsAction = require("./interfaces/SupportedVersionsAction"); - -Object.keys(_SupportedVersionsAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _SupportedVersionsAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _SupportedVersionsAction[key]; - } - }); -}); - -var _VisibilityAction = require("./interfaces/VisibilityAction"); - -Object.keys(_VisibilityAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _VisibilityAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _VisibilityAction[key]; - } - }); -}); - -var _GetOpenIDAction = require("./interfaces/GetOpenIDAction"); - -Object.keys(_GetOpenIDAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _GetOpenIDAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _GetOpenIDAction[key]; - } - }); -}); - -var _OpenIDCredentialsAction = require("./interfaces/OpenIDCredentialsAction"); - -Object.keys(_OpenIDCredentialsAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _OpenIDCredentialsAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _OpenIDCredentialsAction[key]; - } - }); -}); - -var _WidgetKind = require("./interfaces/WidgetKind"); - -Object.keys(_WidgetKind).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetKind[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetKind[key]; - } - }); -}); - -var _ModalButtonKind = require("./interfaces/ModalButtonKind"); - -Object.keys(_ModalButtonKind).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ModalButtonKind[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ModalButtonKind[key]; - } - }); -}); - -var _ModalWidgetActions = require("./interfaces/ModalWidgetActions"); - -Object.keys(_ModalWidgetActions).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ModalWidgetActions[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ModalWidgetActions[key]; - } - }); -}); - -var _SetModalButtonEnabledAction = require("./interfaces/SetModalButtonEnabledAction"); - -Object.keys(_SetModalButtonEnabledAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _SetModalButtonEnabledAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _SetModalButtonEnabledAction[key]; - } - }); -}); - -var _WidgetConfigAction = require("./interfaces/WidgetConfigAction"); - -Object.keys(_WidgetConfigAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetConfigAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetConfigAction[key]; - } - }); -}); - -var _SendEventAction = require("./interfaces/SendEventAction"); - -Object.keys(_SendEventAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _SendEventAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _SendEventAction[key]; - } - }); -}); - -var _SendToDeviceAction = require("./interfaces/SendToDeviceAction"); - -Object.keys(_SendToDeviceAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _SendToDeviceAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _SendToDeviceAction[key]; - } - }); -}); - -var _ReadEventAction = require("./interfaces/ReadEventAction"); - -Object.keys(_ReadEventAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ReadEventAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ReadEventAction[key]; - } - }); -}); - -var _IRoomEvent = require("./interfaces/IRoomEvent"); - -Object.keys(_IRoomEvent).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _IRoomEvent[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _IRoomEvent[key]; - } - }); -}); - -var _NavigateAction = require("./interfaces/NavigateAction"); - -Object.keys(_NavigateAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _NavigateAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _NavigateAction[key]; - } - }); -}); - -var _TurnServerActions = require("./interfaces/TurnServerActions"); - -Object.keys(_TurnServerActions).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _TurnServerActions[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _TurnServerActions[key]; - } - }); -}); - -var _ReadRelationsAction = require("./interfaces/ReadRelationsAction"); - -Object.keys(_ReadRelationsAction).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _ReadRelationsAction[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _ReadRelationsAction[key]; - } - }); -}); - -var _WidgetEventCapability = require("./models/WidgetEventCapability"); - -Object.keys(_WidgetEventCapability).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetEventCapability[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetEventCapability[key]; - } - }); -}); - -var _url = require("./models/validation/url"); - -Object.keys(_url).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _url[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _url[key]; - } - }); -}); - -var _utils = require("./models/validation/utils"); - -Object.keys(_utils).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _utils[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _utils[key]; - } - }); -}); - -var _Widget = require("./models/Widget"); - -Object.keys(_Widget).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _Widget[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _Widget[key]; - } - }); -}); - -var _WidgetParser = require("./models/WidgetParser"); - -Object.keys(_WidgetParser).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetParser[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetParser[key]; - } - }); -}); - -var _urlTemplate = require("./templating/url-template"); - -Object.keys(_urlTemplate).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _urlTemplate[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _urlTemplate[key]; - } - }); -}); - -var _SimpleObservable = require("./util/SimpleObservable"); - -Object.keys(_SimpleObservable).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _SimpleObservable[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _SimpleObservable[key]; - } - }); -}); - -var _WidgetDriver = require("./driver/WidgetDriver"); - -Object.keys(_WidgetDriver).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _WidgetDriver[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function get() { - return _WidgetDriver[key]; - } - }); -}); -},{"./ClientWidgetApi":174,"./Symbols":175,"./WidgetApi":176,"./driver/WidgetDriver":177,"./interfaces/ApiVersion":179,"./interfaces/Capabilities":180,"./interfaces/CapabilitiesAction":181,"./interfaces/ContentLoadedAction":182,"./interfaces/GetOpenIDAction":183,"./interfaces/ICustomWidgetData":184,"./interfaces/IJitsiWidgetData":185,"./interfaces/IRoomEvent":186,"./interfaces/IStickerpickerWidgetData":187,"./interfaces/IWidget":188,"./interfaces/IWidgetApiErrorResponse":189,"./interfaces/IWidgetApiRequest":190,"./interfaces/IWidgetApiResponse":191,"./interfaces/ModalButtonKind":192,"./interfaces/ModalWidgetActions":193,"./interfaces/NavigateAction":194,"./interfaces/OpenIDCredentialsAction":195,"./interfaces/ReadEventAction":196,"./interfaces/ReadRelationsAction":197,"./interfaces/ScreenshotAction":198,"./interfaces/SendEventAction":199,"./interfaces/SendToDeviceAction":200,"./interfaces/SetModalButtonEnabledAction":201,"./interfaces/StickerAction":202,"./interfaces/StickyAction":203,"./interfaces/SupportedVersionsAction":204,"./interfaces/TurnServerActions":205,"./interfaces/VisibilityAction":206,"./interfaces/WidgetApiAction":207,"./interfaces/WidgetApiDirection":208,"./interfaces/WidgetConfigAction":209,"./interfaces/WidgetKind":210,"./interfaces/WidgetType":211,"./models/Widget":212,"./models/WidgetEventCapability":213,"./models/WidgetParser":214,"./models/validation/url":215,"./models/validation/utils":216,"./templating/url-template":217,"./transport/ITransport":218,"./transport/PostmessageTransport":219,"./util/SimpleObservable":220}],179:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.UnstableApiVersion = exports.MatrixApiVersion = exports.CurrentApiVersions = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var MatrixApiVersion; -exports.MatrixApiVersion = MatrixApiVersion; - -(function (MatrixApiVersion) { - MatrixApiVersion["Prerelease1"] = "0.0.1"; - MatrixApiVersion["Prerelease2"] = "0.0.2"; -})(MatrixApiVersion || (exports.MatrixApiVersion = MatrixApiVersion = {})); - -var UnstableApiVersion; -exports.UnstableApiVersion = UnstableApiVersion; - -(function (UnstableApiVersion) { - UnstableApiVersion["MSC2762"] = "org.matrix.msc2762"; - UnstableApiVersion["MSC2871"] = "org.matrix.msc2871"; - UnstableApiVersion["MSC2931"] = "org.matrix.msc2931"; - UnstableApiVersion["MSC2974"] = "org.matrix.msc2974"; - UnstableApiVersion["MSC2876"] = "org.matrix.msc2876"; - UnstableApiVersion["MSC3819"] = "org.matrix.msc3819"; - UnstableApiVersion["MSC3846"] = "town.robin.msc3846"; - UnstableApiVersion["MSC3869"] = "org.matrix.msc3869"; - UnstableApiVersion["MSC3973"] = "org.matrix.msc3973"; -})(UnstableApiVersion || (exports.UnstableApiVersion = UnstableApiVersion = {})); - -var CurrentApiVersions = [MatrixApiVersion.Prerelease1, MatrixApiVersion.Prerelease2, //MatrixApiVersion.V010, -UnstableApiVersion.MSC2762, UnstableApiVersion.MSC2871, UnstableApiVersion.MSC2931, UnstableApiVersion.MSC2974, UnstableApiVersion.MSC2876, UnstableApiVersion.MSC3819, UnstableApiVersion.MSC3846, UnstableApiVersion.MSC3869, UnstableApiVersion.MSC3973]; -exports.CurrentApiVersions = CurrentApiVersions; -},{}],180:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.VideoConferenceCapabilities = exports.StickerpickerCapabilities = exports.MatrixCapabilities = void 0; -exports.getTimelineRoomIDFromCapability = getTimelineRoomIDFromCapability; -exports.isTimelineCapability = isTimelineCapability; -exports.isTimelineCapabilityFor = isTimelineCapabilityFor; - -/* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var MatrixCapabilities; -exports.MatrixCapabilities = MatrixCapabilities; - -(function (MatrixCapabilities) { - MatrixCapabilities["Screenshots"] = "m.capability.screenshot"; - MatrixCapabilities["StickerSending"] = "m.sticker"; - MatrixCapabilities["AlwaysOnScreen"] = "m.always_on_screen"; - MatrixCapabilities["RequiresClient"] = "io.element.requires_client"; - MatrixCapabilities["MSC2931Navigate"] = "org.matrix.msc2931.navigate"; - MatrixCapabilities["MSC3846TurnServers"] = "town.robin.msc3846.turn_servers"; - MatrixCapabilities["MSC3973UserDirectorySearch"] = "org.matrix.msc3973.user_directory_search"; -})(MatrixCapabilities || (exports.MatrixCapabilities = MatrixCapabilities = {})); - -var StickerpickerCapabilities = [MatrixCapabilities.StickerSending]; -exports.StickerpickerCapabilities = StickerpickerCapabilities; -var VideoConferenceCapabilities = [MatrixCapabilities.AlwaysOnScreen]; -/** - * Determines if a capability is a capability for a timeline. - * @param {Capability} capability The capability to test. - * @returns {boolean} True if a timeline capability, false otherwise. - */ - -exports.VideoConferenceCapabilities = VideoConferenceCapabilities; - -function isTimelineCapability(capability) { - // TODO: Change when MSC2762 becomes stable. - return capability === null || capability === void 0 ? void 0 : capability.startsWith("org.matrix.msc2762.timeline:"); -} -/** - * Determines if a capability is a timeline capability for the given room. - * @param {Capability} capability The capability to test. - * @param {string | Symbols.AnyRoom} roomId The room ID, or `Symbols.AnyRoom` for that designation. - * @returns {boolean} True if a matching capability, false otherwise. - */ - - -function isTimelineCapabilityFor(capability, roomId) { - return capability === "org.matrix.msc2762.timeline:".concat(roomId); -} -/** - * Gets the room ID described by a timeline capability. - * @param {string} capability The capability to parse. - * @returns {string} The room ID. - */ - - -function getTimelineRoomIDFromCapability(capability) { - return capability.substring(capability.indexOf(":") + 1); -} -},{}],181:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],182:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],183:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.OpenIDRequestState = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var OpenIDRequestState; -exports.OpenIDRequestState = OpenIDRequestState; - -(function (OpenIDRequestState) { - OpenIDRequestState["Allowed"] = "allowed"; - OpenIDRequestState["Blocked"] = "blocked"; - OpenIDRequestState["PendingUserConfirmation"] = "request"; -})(OpenIDRequestState || (exports.OpenIDRequestState = OpenIDRequestState = {})); -},{}],184:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],185:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],186:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],187:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],188:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],189:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isErrorResponse = isErrorResponse; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -function isErrorResponse(responseData) { - if ("error" in responseData) { - var err = responseData; - return !!err.error.message; - } - - return false; -} -},{}],190:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],191:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],192:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ModalButtonKind = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var ModalButtonKind; -exports.ModalButtonKind = ModalButtonKind; - -(function (ModalButtonKind) { - ModalButtonKind["Primary"] = "m.primary"; - ModalButtonKind["Secondary"] = "m.secondary"; - ModalButtonKind["Warning"] = "m.warning"; - ModalButtonKind["Danger"] = "m.danger"; - ModalButtonKind["Link"] = "m.link"; -})(ModalButtonKind || (exports.ModalButtonKind = ModalButtonKind = {})); -},{}],193:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.BuiltInModalButtonID = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var BuiltInModalButtonID; -exports.BuiltInModalButtonID = BuiltInModalButtonID; - -(function (BuiltInModalButtonID) { - BuiltInModalButtonID["Close"] = "m.close"; -})(BuiltInModalButtonID || (exports.BuiltInModalButtonID = BuiltInModalButtonID = {})); -},{}],194:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],195:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],196:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],197:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],198:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],199:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],200:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],201:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],202:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],203:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],204:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],205:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],206:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],207:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetApiToWidgetAction = exports.WidgetApiFromWidgetAction = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var WidgetApiToWidgetAction; -exports.WidgetApiToWidgetAction = WidgetApiToWidgetAction; - -(function (WidgetApiToWidgetAction) { - WidgetApiToWidgetAction["SupportedApiVersions"] = "supported_api_versions"; - WidgetApiToWidgetAction["Capabilities"] = "capabilities"; - WidgetApiToWidgetAction["NotifyCapabilities"] = "notify_capabilities"; - WidgetApiToWidgetAction["TakeScreenshot"] = "screenshot"; - WidgetApiToWidgetAction["UpdateVisibility"] = "visibility"; - WidgetApiToWidgetAction["OpenIDCredentials"] = "openid_credentials"; - WidgetApiToWidgetAction["WidgetConfig"] = "widget_config"; - WidgetApiToWidgetAction["CloseModalWidget"] = "close_modal"; - WidgetApiToWidgetAction["ButtonClicked"] = "button_clicked"; - WidgetApiToWidgetAction["SendEvent"] = "send_event"; - WidgetApiToWidgetAction["SendToDevice"] = "send_to_device"; - WidgetApiToWidgetAction["UpdateTurnServers"] = "update_turn_servers"; -})(WidgetApiToWidgetAction || (exports.WidgetApiToWidgetAction = WidgetApiToWidgetAction = {})); - -var WidgetApiFromWidgetAction; -exports.WidgetApiFromWidgetAction = WidgetApiFromWidgetAction; - -(function (WidgetApiFromWidgetAction) { - WidgetApiFromWidgetAction["SupportedApiVersions"] = "supported_api_versions"; - WidgetApiFromWidgetAction["ContentLoaded"] = "content_loaded"; - WidgetApiFromWidgetAction["SendSticker"] = "m.sticker"; - WidgetApiFromWidgetAction["UpdateAlwaysOnScreen"] = "set_always_on_screen"; - WidgetApiFromWidgetAction["GetOpenIDCredentials"] = "get_openid"; - WidgetApiFromWidgetAction["CloseModalWidget"] = "close_modal"; - WidgetApiFromWidgetAction["OpenModalWidget"] = "open_modal"; - WidgetApiFromWidgetAction["SetModalButtonEnabled"] = "set_button_enabled"; - WidgetApiFromWidgetAction["SendEvent"] = "send_event"; - WidgetApiFromWidgetAction["SendToDevice"] = "send_to_device"; - WidgetApiFromWidgetAction["WatchTurnServers"] = "watch_turn_servers"; - WidgetApiFromWidgetAction["UnwatchTurnServers"] = "unwatch_turn_servers"; - WidgetApiFromWidgetAction["MSC2876ReadEvents"] = "org.matrix.msc2876.read_events"; - WidgetApiFromWidgetAction["MSC2931Navigate"] = "org.matrix.msc2931.navigate"; - WidgetApiFromWidgetAction["MSC2974RenegotiateCapabilities"] = "org.matrix.msc2974.request_capabilities"; - WidgetApiFromWidgetAction["MSC3869ReadRelations"] = "org.matrix.msc3869.read_relations"; - WidgetApiFromWidgetAction["MSC3973UserDirectorySearch"] = "org.matrix.msc3973.user_directory_search"; -})(WidgetApiFromWidgetAction || (exports.WidgetApiFromWidgetAction = WidgetApiFromWidgetAction = {})); -},{}],208:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetApiDirection = void 0; -exports.invertedDirection = invertedDirection; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var WidgetApiDirection; -exports.WidgetApiDirection = WidgetApiDirection; - -(function (WidgetApiDirection) { - WidgetApiDirection["ToWidget"] = "toWidget"; - WidgetApiDirection["FromWidget"] = "fromWidget"; -})(WidgetApiDirection || (exports.WidgetApiDirection = WidgetApiDirection = {})); - -function invertedDirection(dir) { - if (dir === WidgetApiDirection.ToWidget) { - return WidgetApiDirection.FromWidget; - } else if (dir === WidgetApiDirection.FromWidget) { - return WidgetApiDirection.ToWidget; - } else { - throw new Error("Invalid direction"); - } -} -},{}],209:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],210:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetKind = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var WidgetKind; -exports.WidgetKind = WidgetKind; - -(function (WidgetKind) { - WidgetKind["Room"] = "room"; - WidgetKind["Account"] = "account"; - WidgetKind["Modal"] = "modal"; -})(WidgetKind || (exports.WidgetKind = WidgetKind = {})); -},{}],211:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.MatrixWidgetType = void 0; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var MatrixWidgetType; -exports.MatrixWidgetType = MatrixWidgetType; - -(function (MatrixWidgetType) { - MatrixWidgetType["Custom"] = "m.custom"; - MatrixWidgetType["JitsiMeet"] = "m.jitsi"; - MatrixWidgetType["Stickerpicker"] = "m.stickerpicker"; -})(MatrixWidgetType || (exports.MatrixWidgetType = MatrixWidgetType = {})); -},{}],212:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.Widget = void 0; - -var _utils = require("./validation/utils"); - -var _ = require(".."); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -/** - * Represents the barest form of widget. - */ -var Widget = /*#__PURE__*/function () { - function Widget(definition) { - _classCallCheck(this, Widget); - - this.definition = definition; - if (!this.definition) throw new Error("Definition is required"); - (0, _utils.assertPresent)(definition, "id"); - (0, _utils.assertPresent)(definition, "creatorUserId"); - (0, _utils.assertPresent)(definition, "type"); - (0, _utils.assertPresent)(definition, "url"); - } - /** - * The user ID who created the widget. - */ - - - _createClass(Widget, [{ - key: "creatorUserId", - get: function get() { - return this.definition.creatorUserId; - } - /** - * The type of widget. - */ - - }, { - key: "type", - get: function get() { - return this.definition.type; - } - /** - * The ID of the widget. - */ - - }, { - key: "id", - get: function get() { - return this.definition.id; - } - /** - * The name of the widget, or null if not set. - */ - - }, { - key: "name", - get: function get() { - return this.definition.name || null; - } - /** - * The title for the widget, or null if not set. - */ - - }, { - key: "title", - get: function get() { - return this.rawData.title || null; - } - /** - * The templated URL for the widget. - */ - - }, { - key: "templateUrl", - get: function get() { - return this.definition.url; - } - /** - * The origin for this widget. - */ - - }, { - key: "origin", - get: function get() { - return new URL(this.templateUrl).origin; - } - /** - * Whether or not the client should wait for the iframe to load. Defaults - * to true. - */ - - }, { - key: "waitForIframeLoad", - get: function get() { - if (this.definition.waitForIframeLoad === false) return false; - if (this.definition.waitForIframeLoad === true) return true; - return true; // default true - } - /** - * The raw data for the widget. This will always be defined, though - * may be empty. - */ - - }, { - key: "rawData", - get: function get() { - return this.definition.data || {}; - } - /** - * Gets a complete widget URL for the client to render. - * @param {ITemplateParams} params The template parameters. - * @returns {string} A templated URL. - */ - - }, { - key: "getCompleteUrl", - value: function getCompleteUrl(params) { - return (0, _.runTemplate)(this.templateUrl, this.definition, params); - } - }]); - - return Widget; -}(); - -exports.Widget = Widget; -},{"..":178,"./validation/utils":216}],213:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetEventCapability = exports.EventKind = exports.EventDirection = void 0; - -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var EventKind; -exports.EventKind = EventKind; - -(function (EventKind) { - EventKind["Event"] = "event"; - EventKind["State"] = "state_event"; - EventKind["ToDevice"] = "to_device"; -})(EventKind || (exports.EventKind = EventKind = {})); - -var EventDirection; -exports.EventDirection = EventDirection; - -(function (EventDirection) { - EventDirection["Send"] = "send"; - EventDirection["Receive"] = "receive"; -})(EventDirection || (exports.EventDirection = EventDirection = {})); - -var WidgetEventCapability = /*#__PURE__*/function () { - function WidgetEventCapability(direction, eventType, kind, keyStr, raw) { - _classCallCheck(this, WidgetEventCapability); - - this.direction = direction; - this.eventType = eventType; - this.kind = kind; - this.keyStr = keyStr; - this.raw = raw; - } - - _createClass(WidgetEventCapability, [{ - key: "matchesAsStateEvent", - value: function matchesAsStateEvent(direction, eventType, stateKey) { - if (this.kind !== EventKind.State) return false; // not a state event - - if (this.direction !== direction) return false; // direction mismatch - - if (this.eventType !== eventType) return false; // event type mismatch - - if (this.keyStr === null) return true; // all state keys are allowed - - if (this.keyStr === stateKey) return true; // this state key is allowed - // Default not allowed - - return false; - } - }, { - key: "matchesAsToDeviceEvent", - value: function matchesAsToDeviceEvent(direction, eventType) { - if (this.kind !== EventKind.ToDevice) return false; // not a to-device event - - if (this.direction !== direction) return false; // direction mismatch - - if (this.eventType !== eventType) return false; // event type mismatch - // Checks passed, the event is allowed - - return true; - } - }, { - key: "matchesAsRoomEvent", - value: function matchesAsRoomEvent(direction, eventType) { - var msgtype = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; - if (this.kind !== EventKind.Event) return false; // not a room event - - if (this.direction !== direction) return false; // direction mismatch - - if (this.eventType !== eventType) return false; // event type mismatch - - if (this.eventType === "m.room.message") { - if (this.keyStr === null) return true; // all message types are allowed - - if (this.keyStr === msgtype) return true; // this message type is allowed - } else { - return true; // already passed the check for if the event is allowed - } // Default not allowed - - - return false; - } - }], [{ - key: "forStateEvent", - value: function forStateEvent(direction, eventType, stateKey) { - // TODO: Enable support for m.* namespace once the MSC lands. - // https://github.com/matrix-org/matrix-widget-api/issues/22 - eventType = eventType.replace(/#/g, '\\#'); - stateKey = stateKey !== null && stateKey !== undefined ? "#".concat(stateKey) : ''; - var str = "org.matrix.msc2762.".concat(direction, ".state_event:").concat(eventType).concat(stateKey); // cheat by sending it through the processor - - return WidgetEventCapability.findEventCapabilities([str])[0]; - } - }, { - key: "forToDeviceEvent", - value: function forToDeviceEvent(direction, eventType) { - // TODO: Enable support for m.* namespace once the MSC lands. - // https://github.com/matrix-org/matrix-widget-api/issues/56 - var str = "org.matrix.msc3819.".concat(direction, ".to_device:").concat(eventType); // cheat by sending it through the processor - - return WidgetEventCapability.findEventCapabilities([str])[0]; - } - }, { - key: "forRoomEvent", - value: function forRoomEvent(direction, eventType) { - // TODO: Enable support for m.* namespace once the MSC lands. - // https://github.com/matrix-org/matrix-widget-api/issues/22 - var str = "org.matrix.msc2762.".concat(direction, ".event:").concat(eventType); // cheat by sending it through the processor - - return WidgetEventCapability.findEventCapabilities([str])[0]; - } - }, { - key: "forRoomMessageEvent", - value: function forRoomMessageEvent(direction, msgtype) { - // TODO: Enable support for m.* namespace once the MSC lands. - // https://github.com/matrix-org/matrix-widget-api/issues/22 - msgtype = msgtype === null || msgtype === undefined ? '' : msgtype; - var str = "org.matrix.msc2762.".concat(direction, ".event:m.room.message#").concat(msgtype); // cheat by sending it through the processor - - return WidgetEventCapability.findEventCapabilities([str])[0]; - } - /** - * Parses a capabilities request to find all the event capability requests. - * @param {Iterable} capabilities The capabilities requested/to parse. - * @returns {WidgetEventCapability[]} An array of event capability requests. May be empty, but never null. - */ - - }, { - key: "findEventCapabilities", - value: function findEventCapabilities(capabilities) { - var parsed = []; - - var _iterator = _createForOfIteratorHelper(capabilities), - _step; - - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var cap = _step.value; - var _direction = null; - var eventSegment = void 0; - var _kind = null; // TODO: Enable support for m.* namespace once the MSCs land. - // https://github.com/matrix-org/matrix-widget-api/issues/22 - // https://github.com/matrix-org/matrix-widget-api/issues/56 - - if (cap.startsWith("org.matrix.msc2762.send.event:")) { - _direction = EventDirection.Send; - _kind = EventKind.Event; - eventSegment = cap.substring("org.matrix.msc2762.send.event:".length); - } else if (cap.startsWith("org.matrix.msc2762.send.state_event:")) { - _direction = EventDirection.Send; - _kind = EventKind.State; - eventSegment = cap.substring("org.matrix.msc2762.send.state_event:".length); - } else if (cap.startsWith("org.matrix.msc3819.send.to_device:")) { - _direction = EventDirection.Send; - _kind = EventKind.ToDevice; - eventSegment = cap.substring("org.matrix.msc3819.send.to_device:".length); - } else if (cap.startsWith("org.matrix.msc2762.receive.event:")) { - _direction = EventDirection.Receive; - _kind = EventKind.Event; - eventSegment = cap.substring("org.matrix.msc2762.receive.event:".length); - } else if (cap.startsWith("org.matrix.msc2762.receive.state_event:")) { - _direction = EventDirection.Receive; - _kind = EventKind.State; - eventSegment = cap.substring("org.matrix.msc2762.receive.state_event:".length); - } else if (cap.startsWith("org.matrix.msc3819.receive.to_device:")) { - _direction = EventDirection.Receive; - _kind = EventKind.ToDevice; - eventSegment = cap.substring("org.matrix.msc3819.receive.to_device:".length); - } - - if (_direction === null || _kind === null || eventSegment === undefined) continue; // The capability uses `#` as a separator between event type and state key/msgtype, - // so we split on that. However, a # is also valid in either one of those so we - // join accordingly. - // Eg: `m.room.message##m.text` is "m.room.message" event with msgtype "#m.text". - - var expectingKeyStr = eventSegment.startsWith("m.room.message#") || _kind === EventKind.State; - - var _keyStr = null; - - if (eventSegment.includes('#') && expectingKeyStr) { - // Dev note: regex is difficult to write, so instead the rules are manually written - // out. This is probably just as understandable as a boring regex though, so win-win? - // Test cases: - // str eventSegment keyStr - // ------------------------------------------------------------- - // m.room.message# m.room.message - // m.room.message#test m.room.message test - // m.room.message\# m.room.message# test - // m.room.message##test m.room.message #test - // m.room.message\##test m.room.message# test - // m.room.message\\##test m.room.message\# test - // m.room.message\\###test m.room.message\# #test - // First step: explode the string - var parts = eventSegment.split('#'); // To form the eventSegment, we'll keep finding parts of the exploded string until - // there's one that doesn't end with the escape character (\). We'll then join those - // segments together with the exploding character. We have to remember to consume the - // escape character as well. - - var idx = parts.findIndex(function (p) { - return !p.endsWith("\\"); - }); - eventSegment = parts.slice(0, idx + 1).map(function (p) { - return p.endsWith('\\') ? p.substring(0, p.length - 1) : p; - }).join('#'); // The keyStr is whatever is left over. - - _keyStr = parts.slice(idx + 1).join('#'); - } - - parsed.push(new WidgetEventCapability(_direction, eventSegment, _kind, _keyStr, cap)); - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - - return parsed; - } - }]); - - return WidgetEventCapability; -}(); - -exports.WidgetEventCapability = WidgetEventCapability; -},{}],214:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.WidgetParser = void 0; - -var _Widget = require("./Widget"); - -var _url = require("./validation/url"); - -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -var WidgetParser = /*#__PURE__*/function () { - function WidgetParser() {// private constructor because this is a util class - - _classCallCheck(this, WidgetParser); - } - /** - * Parses widgets from the "m.widgets" account data event. This will always - * return an array, though may be empty if no valid widgets were found. - * @param {IAccountDataWidgets} content The content of the "m.widgets" account data. - * @returns {Widget[]} The widgets in account data, or an empty array. - */ - - - _createClass(WidgetParser, null, [{ - key: "parseAccountData", - value: function parseAccountData(content) { - if (!content) return []; - var result = []; - - for (var _i = 0, _Object$keys = Object.keys(content); _i < _Object$keys.length; _i++) { - var _widgetId = _Object$keys[_i]; - var roughWidget = content[_widgetId]; - if (!roughWidget) continue; - if (roughWidget.type !== "m.widget" && roughWidget.type !== "im.vector.modular.widgets") continue; - if (!roughWidget.sender) continue; - var probableWidgetId = roughWidget.state_key || roughWidget.id; - if (probableWidgetId !== _widgetId) continue; - var asStateEvent = { - content: roughWidget.content, - sender: roughWidget.sender, - type: "m.widget", - state_key: _widgetId, - event_id: "$example", - room_id: "!example", - origin_server_ts: 1 - }; - var widget = WidgetParser.parseRoomWidget(asStateEvent); - if (widget) result.push(widget); - } - - return result; - } - /** - * Parses all the widgets possible in the given array. This will always return - * an array, though may be empty if no widgets could be parsed. - * @param {IStateEvent[]} currentState The room state to parse. - * @returns {Widget[]} The widgets in the state, or an empty array. - */ - - }, { - key: "parseWidgetsFromRoomState", - value: function parseWidgetsFromRoomState(currentState) { - if (!currentState) return []; - var result = []; - - var _iterator = _createForOfIteratorHelper(currentState), - _step; - - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var state = _step.value; - var widget = WidgetParser.parseRoomWidget(state); - if (widget) result.push(widget); - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - - return result; - } - /** - * Parses a state event into a widget. If the state event does not represent - * a widget (wrong event type, invalid widget, etc) then null is returned. - * @param {IStateEvent} stateEvent The state event. - * @returns {Widget|null} The widget, or null if invalid - */ - - }, { - key: "parseRoomWidget", - value: function parseRoomWidget(stateEvent) { - if (!stateEvent) return null; // TODO: [Legacy] Remove legacy support - - if (stateEvent.type !== "m.widget" && stateEvent.type !== "im.vector.modular.widgets") { - return null; - } // Dev note: Throughout this function we have null safety to ensure that - // if the caller did not supply something useful that we don't error. This - // is done against the requirements of the interface because not everyone - // will have an interface to validate against. - - - var content = stateEvent.content || {}; // Form our best approximation of a widget with the information we have - - var estimatedWidget = { - id: stateEvent.state_key, - creatorUserId: content['creatorUserId'] || stateEvent.sender, - name: content['name'], - type: content['type'], - url: content['url'], - waitForIframeLoad: content['waitForIframeLoad'], - data: content['data'] - }; // Finally, process that widget - - return WidgetParser.processEstimatedWidget(estimatedWidget); - } - }, { - key: "processEstimatedWidget", - value: function processEstimatedWidget(widget) { - // Validate that the widget has the best chance of passing as a widget - if (!widget.id || !widget.creatorUserId || !widget.type) { - return null; - } - - if (!(0, _url.isValidUrl)(widget.url)) { - return null; - } // TODO: Validate data for known widget types - - - return new _Widget.Widget(widget); - } - }]); - - return WidgetParser; -}(); - -exports.WidgetParser = WidgetParser; -},{"./Widget":212,"./validation/url":215}],215:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isValidUrl = isValidUrl; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -function isValidUrl(val) { - if (!val) return false; // easy: not valid if not present - - try { - var parsed = new URL(val); - - if (parsed.protocol !== "http" && parsed.protocol !== "https") { - return false; - } - - return true; - } catch (e) { - if (e instanceof TypeError) { - return false; - } - - throw e; - } -} -},{}],216:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.assertPresent = assertPresent; - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -function assertPresent(obj, key) { - if (!obj[key]) { - throw new Error("".concat(key, " is required")); - } -} -},{}],217:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.runTemplate = runTemplate; -exports.toString = toString; - -/* - * Copyright 2020, 2021 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -function runTemplate(url, widget, params) { - // Always apply the supplied params over top of data to ensure the data can't lie about them. - var variables = Object.assign({}, widget.data, { - 'matrix_room_id': params.widgetRoomId || "", - 'matrix_user_id': params.currentUserId, - 'matrix_display_name': params.userDisplayName || params.currentUserId, - 'matrix_avatar_url': params.userHttpAvatarUrl || "", - 'matrix_widget_id': widget.id, - // TODO: Convert to stable (https://github.com/matrix-org/matrix-doc/pull/2873) - 'org.matrix.msc2873.client_id': params.clientId || "", - 'org.matrix.msc2873.client_theme': params.clientTheme || "", - 'org.matrix.msc2873.client_language': params.clientLanguage || "" - }); - var result = url; - - for (var _i = 0, _Object$keys = Object.keys(variables); _i < _Object$keys.length; _i++) { - var key = _Object$keys[_i]; - // Regex escape from https://stackoverflow.com/a/6969486/7037379 - var pattern = "$".concat(key).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string - - var rexp = new RegExp(pattern, 'g'); // This is technically not what we're supposed to do for a couple of reasons: - // 1. We are assuming that there won't later be a $key match after we replace a variable. - // 2. We are assuming that the variable is in a place where it can be escaped (eg: path or query string). - - result = result.replace(rexp, encodeURIComponent(toString(variables[key]))); - } - - return result; -} - -function toString(a) { - if (a === null || a === undefined) { - return "".concat(a); - } - - return String(a); -} -},{}],218:[function(require,module,exports){ -arguments[4][153][0].apply(exports,arguments) -},{"dup":153}],219:[function(require,module,exports){ -"use strict"; - -function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.PostmessageTransport = void 0; - -var _events = require("events"); - -var _ = require(".."); - -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } - -function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - -function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } - -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/** - * Transport for the Widget API over postMessage. - */ -var PostmessageTransport = /*#__PURE__*/function (_EventEmitter) { - _inherits(PostmessageTransport, _EventEmitter); - - var _super = _createSuper(PostmessageTransport); - - function PostmessageTransport(sendDirection, initialWidgetId, transportWindow, inboundWindow) { - var _this; - - _classCallCheck(this, PostmessageTransport); - - _this = _super.call(this); - _this.sendDirection = sendDirection; - _this.initialWidgetId = initialWidgetId; - _this.transportWindow = transportWindow; - _this.inboundWindow = inboundWindow; - - _defineProperty(_assertThisInitialized(_this), "strictOriginCheck", false); - - _defineProperty(_assertThisInitialized(_this), "targetOrigin", "*"); - - _defineProperty(_assertThisInitialized(_this), "timeoutSeconds", 10); - - _defineProperty(_assertThisInitialized(_this), "_ready", false); - - _defineProperty(_assertThisInitialized(_this), "_widgetId", null); - - _defineProperty(_assertThisInitialized(_this), "outboundRequests", new Map()); - - _defineProperty(_assertThisInitialized(_this), "stopController", new AbortController()); - - _this._widgetId = initialWidgetId; - return _this; - } - - _createClass(PostmessageTransport, [{ - key: "ready", - get: function get() { - return this._ready; - } - }, { - key: "widgetId", - get: function get() { - return this._widgetId || null; - } - }, { - key: "nextRequestId", - get: function get() { - var idBase = "widgetapi-".concat(Date.now()); - var index = 0; - var id = idBase; - - while (this.outboundRequests.has(id)) { - id = "".concat(idBase, "-").concat(index++); - } // reserve the ID - - - this.outboundRequests.set(id, null); - return id; - } - }, { - key: "sendInternal", - value: function sendInternal(message) { - console.log("[PostmessageTransport] Sending object to ".concat(this.targetOrigin, ": "), message); - this.transportWindow.postMessage(message, this.targetOrigin); - } - }, { - key: "reply", - value: function reply(request, responseData) { - return this.sendInternal(_objectSpread(_objectSpread({}, request), {}, { - response: responseData - })); - } - }, { - key: "send", - value: function send(action, data) { - return this.sendComplete(action, data).then(function (r) { - return r.response; - }); - } - }, { - key: "sendComplete", - value: function sendComplete(action, data) { - var _this2 = this; - - if (!this.ready || !this.widgetId) { - return Promise.reject(new Error("Not ready or unknown widget ID")); - } - - var request = { - api: this.sendDirection, - widgetId: this.widgetId, - requestId: this.nextRequestId, - action: action, - data: data - }; - - if (action === _.WidgetApiToWidgetAction.UpdateVisibility) { - request['visible'] = data['visible']; - } - - return new Promise(function (prResolve, prReject) { - var resolve = function resolve(response) { - cleanUp(); - prResolve(response); - }; - - var reject = function reject(err) { - cleanUp(); - prReject(err); - }; - - var timerId = setTimeout(function () { - return reject(new Error("Request timed out")); - }, (_this2.timeoutSeconds || 1) * 1000); - - var onStop = function onStop() { - return reject(new Error("Transport stopped")); - }; - - _this2.stopController.signal.addEventListener("abort", onStop); - - var cleanUp = function cleanUp() { - _this2.outboundRequests["delete"](request.requestId); - - clearTimeout(timerId); - - _this2.stopController.signal.removeEventListener("abort", onStop); - }; - - _this2.outboundRequests.set(request.requestId, { - request: request, - resolve: resolve, - reject: reject - }); - - _this2.sendInternal(request); - }); - } - }, { - key: "start", - value: function start() { - var _this3 = this; - - this.inboundWindow.addEventListener("message", function (ev) { - _this3.handleMessage(ev); - }); - this._ready = true; - } - }, { - key: "stop", - value: function stop() { - this._ready = false; - this.stopController.abort(); - } - }, { - key: "handleMessage", - value: function handleMessage(ev) { - if (this.stopController.signal.aborted) return; - if (!ev.data) return; // invalid event - - if (this.strictOriginCheck && ev.origin !== window.origin) return; // bad origin - // treat the message as a response first, then downgrade to a request - - var response = ev.data; - if (!response.action || !response.requestId || !response.widgetId) return; // invalid request/response - - if (!response.response) { - // it's a request - var request = response; - if (request.api !== (0, _.invertedDirection)(this.sendDirection)) return; // wrong direction - - this.handleRequest(request); - } else { - // it's a response - if (response.api !== this.sendDirection) return; // wrong direction - - this.handleResponse(response); - } - } - }, { - key: "handleRequest", - value: function handleRequest(request) { - if (this.widgetId) { - if (this.widgetId !== request.widgetId) return; // wrong widget - } else { - this._widgetId = request.widgetId; - } - - this.emit("message", new CustomEvent("message", { - detail: request - })); - } - }, { - key: "handleResponse", - value: function handleResponse(response) { - if (response.widgetId !== this.widgetId) return; // wrong widget - - var req = this.outboundRequests.get(response.requestId); - if (!req) return; // response to an unknown request - - if ((0, _.isErrorResponse)(response.response)) { - var _err = response.response; - req.reject(new Error(_err.error.message)); - } else { - req.resolve(response); - } - } - }]); - - return PostmessageTransport; -}(_events.EventEmitter); - -exports.PostmessageTransport = PostmessageTransport; -},{"..":178,"events":105}],220:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.SimpleObservable = void 0; - -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var SimpleObservable = /*#__PURE__*/function () { - function SimpleObservable(initialFn) { - _classCallCheck(this, SimpleObservable); - - _defineProperty(this, "listeners", []); - - if (initialFn) this.listeners.push(initialFn); - } - - _createClass(SimpleObservable, [{ - key: "onUpdate", - value: function onUpdate(fn) { - this.listeners.push(fn); - } - }, { - key: "update", - value: function update(val) { - var _iterator = _createForOfIteratorHelper(this.listeners), - _step; - - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var listener = _step.value; - listener(val); - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - } - }, { - key: "close", - value: function close() { - this.listeners = []; // reset - } - }]); - - return SimpleObservable; -}(); - -exports.SimpleObservable = SimpleObservable; -},{}],221:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var HashBase = require('hash-base') -var Buffer = require('safe-buffer').Buffer - -var ARRAY16 = new Array(16) - -function MD5 () { - HashBase.call(this, 64) - - // state - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 -} - -inherits(MD5, HashBase) - -MD5.prototype._update = function () { - var M = ARRAY16 - for (var i = 0; i < 16; ++i) M[i] = this._block.readInt32LE(i * 4) - - var a = this._a - var b = this._b - var c = this._c - var d = this._d - - a = fnF(a, b, c, d, M[0], 0xd76aa478, 7) - d = fnF(d, a, b, c, M[1], 0xe8c7b756, 12) - c = fnF(c, d, a, b, M[2], 0x242070db, 17) - b = fnF(b, c, d, a, M[3], 0xc1bdceee, 22) - a = fnF(a, b, c, d, M[4], 0xf57c0faf, 7) - d = fnF(d, a, b, c, M[5], 0x4787c62a, 12) - c = fnF(c, d, a, b, M[6], 0xa8304613, 17) - b = fnF(b, c, d, a, M[7], 0xfd469501, 22) - a = fnF(a, b, c, d, M[8], 0x698098d8, 7) - d = fnF(d, a, b, c, M[9], 0x8b44f7af, 12) - c = fnF(c, d, a, b, M[10], 0xffff5bb1, 17) - b = fnF(b, c, d, a, M[11], 0x895cd7be, 22) - a = fnF(a, b, c, d, M[12], 0x6b901122, 7) - d = fnF(d, a, b, c, M[13], 0xfd987193, 12) - c = fnF(c, d, a, b, M[14], 0xa679438e, 17) - b = fnF(b, c, d, a, M[15], 0x49b40821, 22) - - a = fnG(a, b, c, d, M[1], 0xf61e2562, 5) - d = fnG(d, a, b, c, M[6], 0xc040b340, 9) - c = fnG(c, d, a, b, M[11], 0x265e5a51, 14) - b = fnG(b, c, d, a, M[0], 0xe9b6c7aa, 20) - a = fnG(a, b, c, d, M[5], 0xd62f105d, 5) - d = fnG(d, a, b, c, M[10], 0x02441453, 9) - c = fnG(c, d, a, b, M[15], 0xd8a1e681, 14) - b = fnG(b, c, d, a, M[4], 0xe7d3fbc8, 20) - a = fnG(a, b, c, d, M[9], 0x21e1cde6, 5) - d = fnG(d, a, b, c, M[14], 0xc33707d6, 9) - c = fnG(c, d, a, b, M[3], 0xf4d50d87, 14) - b = fnG(b, c, d, a, M[8], 0x455a14ed, 20) - a = fnG(a, b, c, d, M[13], 0xa9e3e905, 5) - d = fnG(d, a, b, c, M[2], 0xfcefa3f8, 9) - c = fnG(c, d, a, b, M[7], 0x676f02d9, 14) - b = fnG(b, c, d, a, M[12], 0x8d2a4c8a, 20) - - a = fnH(a, b, c, d, M[5], 0xfffa3942, 4) - d = fnH(d, a, b, c, M[8], 0x8771f681, 11) - c = fnH(c, d, a, b, M[11], 0x6d9d6122, 16) - b = fnH(b, c, d, a, M[14], 0xfde5380c, 23) - a = fnH(a, b, c, d, M[1], 0xa4beea44, 4) - d = fnH(d, a, b, c, M[4], 0x4bdecfa9, 11) - c = fnH(c, d, a, b, M[7], 0xf6bb4b60, 16) - b = fnH(b, c, d, a, M[10], 0xbebfbc70, 23) - a = fnH(a, b, c, d, M[13], 0x289b7ec6, 4) - d = fnH(d, a, b, c, M[0], 0xeaa127fa, 11) - c = fnH(c, d, a, b, M[3], 0xd4ef3085, 16) - b = fnH(b, c, d, a, M[6], 0x04881d05, 23) - a = fnH(a, b, c, d, M[9], 0xd9d4d039, 4) - d = fnH(d, a, b, c, M[12], 0xe6db99e5, 11) - c = fnH(c, d, a, b, M[15], 0x1fa27cf8, 16) - b = fnH(b, c, d, a, M[2], 0xc4ac5665, 23) - - a = fnI(a, b, c, d, M[0], 0xf4292244, 6) - d = fnI(d, a, b, c, M[7], 0x432aff97, 10) - c = fnI(c, d, a, b, M[14], 0xab9423a7, 15) - b = fnI(b, c, d, a, M[5], 0xfc93a039, 21) - a = fnI(a, b, c, d, M[12], 0x655b59c3, 6) - d = fnI(d, a, b, c, M[3], 0x8f0ccc92, 10) - c = fnI(c, d, a, b, M[10], 0xffeff47d, 15) - b = fnI(b, c, d, a, M[1], 0x85845dd1, 21) - a = fnI(a, b, c, d, M[8], 0x6fa87e4f, 6) - d = fnI(d, a, b, c, M[15], 0xfe2ce6e0, 10) - c = fnI(c, d, a, b, M[6], 0xa3014314, 15) - b = fnI(b, c, d, a, M[13], 0x4e0811a1, 21) - a = fnI(a, b, c, d, M[4], 0xf7537e82, 6) - d = fnI(d, a, b, c, M[11], 0xbd3af235, 10) - c = fnI(c, d, a, b, M[2], 0x2ad7d2bb, 15) - b = fnI(b, c, d, a, M[9], 0xeb86d391, 21) - - this._a = (this._a + a) | 0 - this._b = (this._b + b) | 0 - this._c = (this._c + c) | 0 - this._d = (this._d + d) | 0 -} - -MD5.prototype._digest = function () { - // create padding and handle blocks - this._block[this._blockOffset++] = 0x80 - if (this._blockOffset > 56) { - this._block.fill(0, this._blockOffset, 64) - this._update() - this._blockOffset = 0 - } - - this._block.fill(0, this._blockOffset, 56) - this._block.writeUInt32LE(this._length[0], 56) - this._block.writeUInt32LE(this._length[1], 60) - this._update() - - // produce result - var buffer = Buffer.allocUnsafe(16) - buffer.writeInt32LE(this._a, 0) - buffer.writeInt32LE(this._b, 4) - buffer.writeInt32LE(this._c, 8) - buffer.writeInt32LE(this._d, 12) - return buffer -} - -function rotl (x, n) { - return (x << n) | (x >>> (32 - n)) -} - -function fnF (a, b, c, d, m, k, s) { - return (rotl((a + ((b & c) | ((~b) & d)) + m + k) | 0, s) + b) | 0 -} - -function fnG (a, b, c, d, m, k, s) { - return (rotl((a + ((b & d) | (c & (~d))) + m + k) | 0, s) + b) | 0 -} - -function fnH (a, b, c, d, m, k, s) { - return (rotl((a + (b ^ c ^ d) + m + k) | 0, s) + b) | 0 -} - -function fnI (a, b, c, d, m, k, s) { - return (rotl((a + ((c ^ (b | (~d)))) + m + k) | 0, s) + b) | 0 -} - -module.exports = MD5 - -},{"hash-base":116,"inherits":146,"safe-buffer":250}],222:[function(require,module,exports){ -var bn = require('bn.js'); -var brorand = require('brorand'); - -function MillerRabin(rand) { - this.rand = rand || new brorand.Rand(); -} -module.exports = MillerRabin; - -MillerRabin.create = function create(rand) { - return new MillerRabin(rand); -}; - -MillerRabin.prototype._randbelow = function _randbelow(n) { - var len = n.bitLength(); - var min_bytes = Math.ceil(len / 8); - - // Generage random bytes until a number less than n is found. - // This ensures that 0..n-1 have an equal probability of being selected. - do - var a = new bn(this.rand.generate(min_bytes)); - while (a.cmp(n) >= 0); - - return a; -}; - -MillerRabin.prototype._randrange = function _randrange(start, stop) { - // Generate a random number greater than or equal to start and less than stop. - var size = stop.sub(start); - return start.add(this._randbelow(size)); -}; - -MillerRabin.prototype.test = function test(n, k, cb) { - var len = n.bitLength(); - var red = bn.mont(n); - var rone = new bn(1).toRed(red); - - if (!k) - k = Math.max(1, (len / 48) | 0); - - // Find d and s, (n - 1) = (2 ^ s) * d; - var n1 = n.subn(1); - for (var s = 0; !n1.testn(s); s++) {} - var d = n.shrn(s); - - var rn1 = n1.toRed(red); - - var prime = true; - for (; k > 0; k--) { - var a = this._randrange(new bn(2), n1); - if (cb) - cb(a); - - var x = a.toRed(red).redPow(d); - if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) - continue; - - for (var i = 1; i < s; i++) { - x = x.redSqr(); - - if (x.cmp(rone) === 0) - return false; - if (x.cmp(rn1) === 0) - break; - } - - if (i === s) - return false; - } - - return prime; -}; - -MillerRabin.prototype.getDivisor = function getDivisor(n, k) { - var len = n.bitLength(); - var red = bn.mont(n); - var rone = new bn(1).toRed(red); - - if (!k) - k = Math.max(1, (len / 48) | 0); - - // Find d and s, (n - 1) = (2 ^ s) * d; - var n1 = n.subn(1); - for (var s = 0; !n1.testn(s); s++) {} - var d = n.shrn(s); - - var rn1 = n1.toRed(red); - - for (; k > 0; k--) { - var a = this._randrange(new bn(2), n1); - - var g = n.gcd(a); - if (g.cmpn(1) !== 0) - return g; - - var x = a.toRed(red).redPow(d); - if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) - continue; - - for (var i = 1; i < s; i++) { - x = x.redSqr(); - - if (x.cmp(rone) === 0) - return x.fromRed().subn(1).gcd(n); - if (x.cmp(rn1) === 0) - break; - } - - if (i === s) { - x = x.redSqr(); - return x.fromRed().subn(1).gcd(n); - } - } - - return false; -}; - -},{"bn.js":18,"brorand":19}],223:[function(require,module,exports){ -module.exports = assert; - -function assert(val, msg) { - if (!val) - throw new Error(msg || 'Assertion failed'); -} - -assert.equal = function assertEqual(l, r, msg) { - if (l != r) - throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r)); -}; - -},{}],224:[function(require,module,exports){ -'use strict'; - -var utils = exports; - -function toArray(msg, enc) { - if (Array.isArray(msg)) - return msg.slice(); - if (!msg) - return []; - var res = []; - if (typeof msg !== 'string') { - for (var i = 0; i < msg.length; i++) - res[i] = msg[i] | 0; - return res; - } - if (enc === 'hex') { - msg = msg.replace(/[^a-z0-9]+/ig, ''); - if (msg.length % 2 !== 0) - msg = '0' + msg; - for (var i = 0; i < msg.length; i += 2) - res.push(parseInt(msg[i] + msg[i + 1], 16)); - } else { - for (var i = 0; i < msg.length; i++) { - var c = msg.charCodeAt(i); - var hi = c >> 8; - var lo = c & 0xff; - if (hi) - res.push(hi, lo); - else - res.push(lo); - } - } - return res; -} -utils.toArray = toArray; - -function zero2(word) { - if (word.length === 1) - return '0' + word; - else - return word; -} -utils.zero2 = zero2; - -function toHex(msg) { - var res = ''; - for (var i = 0; i < msg.length; i++) - res += zero2(msg[i].toString(16)); - return res; -} -utils.toHex = toHex; - -utils.encode = function encode(arr, enc) { - if (enc === 'hex') - return toHex(arr); - else - return arr; -}; - -},{}],225:[function(require,module,exports){ -'use strict'; -const retry = require('retry'); - -const networkErrorMsgs = [ - 'Failed to fetch', // Chrome - 'NetworkError when attempting to fetch resource.', // Firefox - 'The Internet connection appears to be offline.', // Safari - 'Network request failed' // `cross-fetch` -]; - -class AbortError extends Error { - constructor(message) { - super(); - - if (message instanceof Error) { - this.originalError = message; - ({message} = message); - } else { - this.originalError = new Error(message); - this.originalError.stack = this.stack; - } - - this.name = 'AbortError'; - this.message = message; - } -} - -const decorateErrorWithCounts = (error, attemptNumber, options) => { - // Minus 1 from attemptNumber because the first attempt does not count as a retry - const retriesLeft = options.retries - (attemptNumber - 1); - - error.attemptNumber = attemptNumber; - error.retriesLeft = retriesLeft; - return error; -}; - -const isNetworkError = errorMessage => networkErrorMsgs.includes(errorMessage); - -const pRetry = (input, options) => new Promise((resolve, reject) => { - options = { - onFailedAttempt: () => {}, - retries: 10, - ...options - }; - - const operation = retry.operation(options); - - operation.attempt(async attemptNumber => { - try { - resolve(await input(attemptNumber)); - } catch (error) { - if (!(error instanceof Error)) { - reject(new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`)); - return; - } - - if (error instanceof AbortError) { - operation.stop(); - reject(error.originalError); - } else if (error instanceof TypeError && !isNetworkError(error.message)) { - operation.stop(); - reject(error); - } else { - decorateErrorWithCounts(error, attemptNumber, options); - - try { - await options.onFailedAttempt(error); - } catch (error) { - reject(error); - return; - } - - if (!operation.retry(error)) { - reject(operation.mainError()); - } - } - } - }); -}); - -module.exports = pRetry; -// TODO: remove this in the next major version -module.exports.default = pRetry; - -module.exports.AbortError = AbortError; - -},{"retry":246}],226:[function(require,module,exports){ -module.exports={"2.16.840.1.101.3.4.1.1": "aes-128-ecb", -"2.16.840.1.101.3.4.1.2": "aes-128-cbc", -"2.16.840.1.101.3.4.1.3": "aes-128-ofb", -"2.16.840.1.101.3.4.1.4": "aes-128-cfb", -"2.16.840.1.101.3.4.1.21": "aes-192-ecb", -"2.16.840.1.101.3.4.1.22": "aes-192-cbc", -"2.16.840.1.101.3.4.1.23": "aes-192-ofb", -"2.16.840.1.101.3.4.1.24": "aes-192-cfb", -"2.16.840.1.101.3.4.1.41": "aes-256-ecb", -"2.16.840.1.101.3.4.1.42": "aes-256-cbc", -"2.16.840.1.101.3.4.1.43": "aes-256-ofb", -"2.16.840.1.101.3.4.1.44": "aes-256-cfb" -} -},{}],227:[function(require,module,exports){ -// from https://github.com/indutny/self-signed/blob/gh-pages/lib/asn1.js -// Fedor, you are amazing. -'use strict' - -var asn1 = require('asn1.js') - -exports.certificate = require('./certificate') - -var RSAPrivateKey = asn1.define('RSAPrivateKey', function () { - this.seq().obj( - this.key('version').int(), - this.key('modulus').int(), - this.key('publicExponent').int(), - this.key('privateExponent').int(), - this.key('prime1').int(), - this.key('prime2').int(), - this.key('exponent1').int(), - this.key('exponent2').int(), - this.key('coefficient').int() - ) -}) -exports.RSAPrivateKey = RSAPrivateKey - -var RSAPublicKey = asn1.define('RSAPublicKey', function () { - this.seq().obj( - this.key('modulus').int(), - this.key('publicExponent').int() - ) -}) -exports.RSAPublicKey = RSAPublicKey - -var PublicKey = asn1.define('SubjectPublicKeyInfo', function () { - this.seq().obj( - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPublicKey').bitstr() - ) -}) -exports.PublicKey = PublicKey - -var AlgorithmIdentifier = asn1.define('AlgorithmIdentifier', function () { - this.seq().obj( - this.key('algorithm').objid(), - this.key('none').null_().optional(), - this.key('curve').objid().optional(), - this.key('params').seq().obj( - this.key('p').int(), - this.key('q').int(), - this.key('g').int() - ).optional() - ) -}) - -var PrivateKeyInfo = asn1.define('PrivateKeyInfo', function () { - this.seq().obj( - this.key('version').int(), - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPrivateKey').octstr() - ) -}) -exports.PrivateKey = PrivateKeyInfo -var EncryptedPrivateKeyInfo = asn1.define('EncryptedPrivateKeyInfo', function () { - this.seq().obj( - this.key('algorithm').seq().obj( - this.key('id').objid(), - this.key('decrypt').seq().obj( - this.key('kde').seq().obj( - this.key('id').objid(), - this.key('kdeparams').seq().obj( - this.key('salt').octstr(), - this.key('iters').int() - ) - ), - this.key('cipher').seq().obj( - this.key('algo').objid(), - this.key('iv').octstr() - ) - ) - ), - this.key('subjectPrivateKey').octstr() - ) -}) - -exports.EncryptedPrivateKey = EncryptedPrivateKeyInfo - -var DSAPrivateKey = asn1.define('DSAPrivateKey', function () { - this.seq().obj( - this.key('version').int(), - this.key('p').int(), - this.key('q').int(), - this.key('g').int(), - this.key('pub_key').int(), - this.key('priv_key').int() - ) -}) -exports.DSAPrivateKey = DSAPrivateKey - -exports.DSAparam = asn1.define('DSAparam', function () { - this.int() -}) - -var ECPrivateKey = asn1.define('ECPrivateKey', function () { - this.seq().obj( - this.key('version').int(), - this.key('privateKey').octstr(), - this.key('parameters').optional().explicit(0).use(ECParameters), - this.key('publicKey').optional().explicit(1).bitstr() - ) -}) -exports.ECPrivateKey = ECPrivateKey - -var ECParameters = asn1.define('ECParameters', function () { - this.choice({ - namedCurve: this.objid() - }) -}) - -exports.signature = asn1.define('signature', function () { - this.seq().obj( - this.key('r').int(), - this.key('s').int() - ) -}) - -},{"./certificate":228,"asn1.js":2}],228:[function(require,module,exports){ -// from https://github.com/Rantanen/node-dtls/blob/25a7dc861bda38cfeac93a723500eea4f0ac2e86/Certificate.js -// thanks to @Rantanen - -'use strict' - -var asn = require('asn1.js') - -var Time = asn.define('Time', function () { - this.choice({ - utcTime: this.utctime(), - generalTime: this.gentime() - }) -}) - -var AttributeTypeValue = asn.define('AttributeTypeValue', function () { - this.seq().obj( - this.key('type').objid(), - this.key('value').any() - ) -}) - -var AlgorithmIdentifier = asn.define('AlgorithmIdentifier', function () { - this.seq().obj( - this.key('algorithm').objid(), - this.key('parameters').optional(), - this.key('curve').objid().optional() - ) -}) - -var SubjectPublicKeyInfo = asn.define('SubjectPublicKeyInfo', function () { - this.seq().obj( - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPublicKey').bitstr() - ) -}) - -var RelativeDistinguishedName = asn.define('RelativeDistinguishedName', function () { - this.setof(AttributeTypeValue) -}) - -var RDNSequence = asn.define('RDNSequence', function () { - this.seqof(RelativeDistinguishedName) -}) - -var Name = asn.define('Name', function () { - this.choice({ - rdnSequence: this.use(RDNSequence) - }) -}) - -var Validity = asn.define('Validity', function () { - this.seq().obj( - this.key('notBefore').use(Time), - this.key('notAfter').use(Time) - ) -}) - -var Extension = asn.define('Extension', function () { - this.seq().obj( - this.key('extnID').objid(), - this.key('critical').bool().def(false), - this.key('extnValue').octstr() - ) -}) - -var TBSCertificate = asn.define('TBSCertificate', function () { - this.seq().obj( - this.key('version').explicit(0).int().optional(), - this.key('serialNumber').int(), - this.key('signature').use(AlgorithmIdentifier), - this.key('issuer').use(Name), - this.key('validity').use(Validity), - this.key('subject').use(Name), - this.key('subjectPublicKeyInfo').use(SubjectPublicKeyInfo), - this.key('issuerUniqueID').implicit(1).bitstr().optional(), - this.key('subjectUniqueID').implicit(2).bitstr().optional(), - this.key('extensions').explicit(3).seqof(Extension).optional() - ) -}) - -var X509Certificate = asn.define('X509Certificate', function () { - this.seq().obj( - this.key('tbsCertificate').use(TBSCertificate), - this.key('signatureAlgorithm').use(AlgorithmIdentifier), - this.key('signatureValue').bitstr() - ) -}) - -module.exports = X509Certificate - -},{"asn1.js":2}],229:[function(require,module,exports){ -// adapted from https://github.com/apatil/pemstrip -var findProc = /Proc-Type: 4,ENCRYPTED[\n\r]+DEK-Info: AES-((?:128)|(?:192)|(?:256))-CBC,([0-9A-H]+)[\n\r]+([0-9A-z\n\r+/=]+)[\n\r]+/m -var startRegex = /^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----/m -var fullRegex = /^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----([0-9A-z\n\r+/=]+)-----END \1-----$/m -var evp = require('evp_bytestokey') -var ciphers = require('browserify-aes') -var Buffer = require('safe-buffer').Buffer -module.exports = function (okey, password) { - var key = okey.toString() - var match = key.match(findProc) - var decrypted - if (!match) { - var match2 = key.match(fullRegex) - decrypted = Buffer.from(match2[2].replace(/[\r\n]/g, ''), 'base64') - } else { - var suite = 'aes' + match[1] - var iv = Buffer.from(match[2], 'hex') - var cipherText = Buffer.from(match[3].replace(/[\r\n]/g, ''), 'base64') - var cipherKey = evp(password, iv.slice(0, 8), parseInt(match[1], 10)).key - var out = [] - var cipher = ciphers.createDecipheriv(suite, cipherKey, iv) - out.push(cipher.update(cipherText)) - out.push(cipher.final()) - decrypted = Buffer.concat(out) - } - var tag = key.match(startRegex)[1] - return { - tag: tag, - data: decrypted - } -} - -},{"browserify-aes":23,"evp_bytestokey":106,"safe-buffer":250}],230:[function(require,module,exports){ -var asn1 = require('./asn1') -var aesid = require('./aesid.json') -var fixProc = require('./fixProc') -var ciphers = require('browserify-aes') -var compat = require('pbkdf2') -var Buffer = require('safe-buffer').Buffer -module.exports = parseKeys - -function parseKeys (buffer) { - var password - if (typeof buffer === 'object' && !Buffer.isBuffer(buffer)) { - password = buffer.passphrase - buffer = buffer.key - } - if (typeof buffer === 'string') { - buffer = Buffer.from(buffer) - } - - var stripped = fixProc(buffer, password) - - var type = stripped.tag - var data = stripped.data - var subtype, ndata - switch (type) { - case 'CERTIFICATE': - ndata = asn1.certificate.decode(data, 'der').tbsCertificate.subjectPublicKeyInfo - // falls through - case 'PUBLIC KEY': - if (!ndata) { - ndata = asn1.PublicKey.decode(data, 'der') - } - subtype = ndata.algorithm.algorithm.join('.') - switch (subtype) { - case '1.2.840.113549.1.1.1': - return asn1.RSAPublicKey.decode(ndata.subjectPublicKey.data, 'der') - case '1.2.840.10045.2.1': - ndata.subjectPrivateKey = ndata.subjectPublicKey - return { - type: 'ec', - data: ndata - } - case '1.2.840.10040.4.1': - ndata.algorithm.params.pub_key = asn1.DSAparam.decode(ndata.subjectPublicKey.data, 'der') - return { - type: 'dsa', - data: ndata.algorithm.params - } - default: throw new Error('unknown key id ' + subtype) - } - // throw new Error('unknown key type ' + type) - case 'ENCRYPTED PRIVATE KEY': - data = asn1.EncryptedPrivateKey.decode(data, 'der') - data = decrypt(data, password) - // falls through - case 'PRIVATE KEY': - ndata = asn1.PrivateKey.decode(data, 'der') - subtype = ndata.algorithm.algorithm.join('.') - switch (subtype) { - case '1.2.840.113549.1.1.1': - return asn1.RSAPrivateKey.decode(ndata.subjectPrivateKey, 'der') - case '1.2.840.10045.2.1': - return { - curve: ndata.algorithm.curve, - privateKey: asn1.ECPrivateKey.decode(ndata.subjectPrivateKey, 'der').privateKey - } - case '1.2.840.10040.4.1': - ndata.algorithm.params.priv_key = asn1.DSAparam.decode(ndata.subjectPrivateKey, 'der') - return { - type: 'dsa', - params: ndata.algorithm.params - } - default: throw new Error('unknown key id ' + subtype) - } - // throw new Error('unknown key type ' + type) - case 'RSA PUBLIC KEY': - return asn1.RSAPublicKey.decode(data, 'der') - case 'RSA PRIVATE KEY': - return asn1.RSAPrivateKey.decode(data, 'der') - case 'DSA PRIVATE KEY': - return { - type: 'dsa', - params: asn1.DSAPrivateKey.decode(data, 'der') - } - case 'EC PRIVATE KEY': - data = asn1.ECPrivateKey.decode(data, 'der') - return { - curve: data.parameters.value, - privateKey: data.privateKey - } - default: throw new Error('unknown key type ' + type) - } -} -parseKeys.signature = asn1.signature -function decrypt (data, password) { - var salt = data.algorithm.decrypt.kde.kdeparams.salt - var iters = parseInt(data.algorithm.decrypt.kde.kdeparams.iters.toString(), 10) - var algo = aesid[data.algorithm.decrypt.cipher.algo.join('.')] - var iv = data.algorithm.decrypt.cipher.iv - var cipherText = data.subjectPrivateKey - var keylen = parseInt(algo.split('-')[1], 10) / 8 - var key = compat.pbkdf2Sync(password, salt, iters, keylen, 'sha1') - var cipher = ciphers.createDecipheriv(algo, key, iv) - var out = [] - out.push(cipher.update(cipherText)) - out.push(cipher.final()) - return Buffer.concat(out) -} - -},{"./aesid.json":226,"./asn1":227,"./fixProc":229,"browserify-aes":23,"pbkdf2":231,"safe-buffer":250}],231:[function(require,module,exports){ -exports.pbkdf2 = require('./lib/async') -exports.pbkdf2Sync = require('./lib/sync') - -},{"./lib/async":232,"./lib/sync":235}],232:[function(require,module,exports){ -(function (global){(function (){ -var Buffer = require('safe-buffer').Buffer - -var checkParameters = require('./precondition') -var defaultEncoding = require('./default-encoding') -var sync = require('./sync') -var toBuffer = require('./to-buffer') - -var ZERO_BUF -var subtle = global.crypto && global.crypto.subtle -var toBrowser = { - sha: 'SHA-1', - 'sha-1': 'SHA-1', - sha1: 'SHA-1', - sha256: 'SHA-256', - 'sha-256': 'SHA-256', - sha384: 'SHA-384', - 'sha-384': 'SHA-384', - 'sha-512': 'SHA-512', - sha512: 'SHA-512' -} -var checks = [] -function checkNative (algo) { - if (global.process && !global.process.browser) { - return Promise.resolve(false) - } - if (!subtle || !subtle.importKey || !subtle.deriveBits) { - return Promise.resolve(false) - } - if (checks[algo] !== undefined) { - return checks[algo] - } - ZERO_BUF = ZERO_BUF || Buffer.alloc(8) - var prom = browserPbkdf2(ZERO_BUF, ZERO_BUF, 10, 128, algo) - .then(function () { - return true - }).catch(function () { - return false - }) - checks[algo] = prom - return prom -} -var nextTick -function getNextTick () { - if (nextTick) { - return nextTick - } - if (global.process && global.process.nextTick) { - nextTick = global.process.nextTick - } else if (global.queueMicrotask) { - nextTick = global.queueMicrotask - } else if (global.setImmediate) { - nextTick = global.setImmediate - } else { - nextTick = global.setTimeout - } - return nextTick -} -function browserPbkdf2 (password, salt, iterations, length, algo) { - return subtle.importKey( - 'raw', password, { name: 'PBKDF2' }, false, ['deriveBits'] - ).then(function (key) { - return subtle.deriveBits({ - name: 'PBKDF2', - salt: salt, - iterations: iterations, - hash: { - name: algo - } - }, key, length << 3) - }).then(function (res) { - return Buffer.from(res) - }) -} - -function resolvePromise (promise, callback) { - promise.then(function (out) { - getNextTick()(function () { - callback(null, out) - }) - }, function (e) { - getNextTick()(function () { - callback(e) - }) - }) -} -module.exports = function (password, salt, iterations, keylen, digest, callback) { - if (typeof digest === 'function') { - callback = digest - digest = undefined - } - - digest = digest || 'sha1' - var algo = toBrowser[digest.toLowerCase()] - - if (!algo || typeof global.Promise !== 'function') { - getNextTick()(function () { - var out - try { - out = sync(password, salt, iterations, keylen, digest) - } catch (e) { - return callback(e) - } - callback(null, out) - }) - return - } - - checkParameters(iterations, keylen) - password = toBuffer(password, defaultEncoding, 'Password') - salt = toBuffer(salt, defaultEncoding, 'Salt') - if (typeof callback !== 'function') throw new Error('No callback provided to pbkdf2') - - resolvePromise(checkNative(algo).then(function (resp) { - if (resp) return browserPbkdf2(password, salt, iterations, keylen, algo) - - return sync(password, salt, iterations, keylen, digest) - }), callback) -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./default-encoding":233,"./precondition":234,"./sync":235,"./to-buffer":236,"safe-buffer":250}],233:[function(require,module,exports){ -(function (process,global){(function (){ -var defaultEncoding -/* istanbul ignore next */ -if (global.process && global.process.browser) { - defaultEncoding = 'utf-8' -} else if (global.process && global.process.version) { - var pVersionMajor = parseInt(process.version.split('.')[0].slice(1), 10) - - defaultEncoding = pVersionMajor >= 6 ? 'utf-8' : 'binary' -} else { - defaultEncoding = 'utf-8' -} -module.exports = defaultEncoding - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"_process":237}],234:[function(require,module,exports){ -var MAX_ALLOC = Math.pow(2, 30) - 1 // default in iojs - -module.exports = function (iterations, keylen) { - if (typeof iterations !== 'number') { - throw new TypeError('Iterations not a number') - } - - if (iterations < 0) { - throw new TypeError('Bad iterations') - } - - if (typeof keylen !== 'number') { - throw new TypeError('Key length not a number') - } - - if (keylen < 0 || keylen > MAX_ALLOC || keylen !== keylen) { /* eslint no-self-compare: 0 */ - throw new TypeError('Bad key length') - } -} - -},{}],235:[function(require,module,exports){ -var md5 = require('create-hash/md5') -var RIPEMD160 = require('ripemd160') -var sha = require('sha.js') -var Buffer = require('safe-buffer').Buffer - -var checkParameters = require('./precondition') -var defaultEncoding = require('./default-encoding') -var toBuffer = require('./to-buffer') - -var ZEROS = Buffer.alloc(128) -var sizes = { - md5: 16, - sha1: 20, - sha224: 28, - sha256: 32, - sha384: 48, - sha512: 64, - rmd160: 20, - ripemd160: 20 -} - -function Hmac (alg, key, saltLen) { - var hash = getDigest(alg) - var blocksize = (alg === 'sha512' || alg === 'sha384') ? 128 : 64 - - if (key.length > blocksize) { - key = hash(key) - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) - } - - var ipad = Buffer.allocUnsafe(blocksize + sizes[alg]) - var opad = Buffer.allocUnsafe(blocksize + sizes[alg]) - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C - } - - var ipad1 = Buffer.allocUnsafe(blocksize + saltLen + 4) - ipad.copy(ipad1, 0, 0, blocksize) - this.ipad1 = ipad1 - this.ipad2 = ipad - this.opad = opad - this.alg = alg - this.blocksize = blocksize - this.hash = hash - this.size = sizes[alg] -} - -Hmac.prototype.run = function (data, ipad) { - data.copy(ipad, this.blocksize) - var h = this.hash(ipad) - h.copy(this.opad, this.blocksize) - return this.hash(this.opad) -} - -function getDigest (alg) { - function shaFunc (data) { - return sha(alg).update(data).digest() - } - function rmd160Func (data) { - return new RIPEMD160().update(data).digest() - } - - if (alg === 'rmd160' || alg === 'ripemd160') return rmd160Func - if (alg === 'md5') return md5 - return shaFunc -} - -function pbkdf2 (password, salt, iterations, keylen, digest) { - checkParameters(iterations, keylen) - password = toBuffer(password, defaultEncoding, 'Password') - salt = toBuffer(salt, defaultEncoding, 'Salt') - - digest = digest || 'sha1' - - var hmac = new Hmac(digest, password, salt.length) - - var DK = Buffer.allocUnsafe(keylen) - var block1 = Buffer.allocUnsafe(salt.length + 4) - salt.copy(block1, 0, 0, salt.length) - - var destPos = 0 - var hLen = sizes[digest] - var l = Math.ceil(keylen / hLen) - - for (var i = 1; i <= l; i++) { - block1.writeUInt32BE(i, salt.length) - - var T = hmac.run(block1, hmac.ipad1) - var U = T - - for (var j = 1; j < iterations; j++) { - U = hmac.run(U, hmac.ipad2) - for (var k = 0; k < hLen; k++) T[k] ^= U[k] - } - - T.copy(DK, destPos) - destPos += hLen - } - - return DK -} - -module.exports = pbkdf2 - -},{"./default-encoding":233,"./precondition":234,"./to-buffer":236,"create-hash/md5":75,"ripemd160":249,"safe-buffer":250,"sha.js":257}],236:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -module.exports = function (thing, encoding, name) { - if (Buffer.isBuffer(thing)) { - return thing - } else if (typeof thing === 'string') { - return Buffer.from(thing, encoding) - } else if (ArrayBuffer.isView(thing)) { - return Buffer.from(thing.buffer) - } else { - throw new TypeError(name + ' must be a string, a Buffer, a typed array or a DataView') - } -} - -},{"safe-buffer":250}],237:[function(require,module,exports){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],238:[function(require,module,exports){ -exports.publicEncrypt = require('./publicEncrypt') -exports.privateDecrypt = require('./privateDecrypt') - -exports.privateEncrypt = function privateEncrypt (key, buf) { - return exports.publicEncrypt(key, buf, true) -} - -exports.publicDecrypt = function publicDecrypt (key, buf) { - return exports.privateDecrypt(key, buf, true) -} - -},{"./privateDecrypt":240,"./publicEncrypt":241}],239:[function(require,module,exports){ -var createHash = require('create-hash') -var Buffer = require('safe-buffer').Buffer - -module.exports = function (seed, len) { - var t = Buffer.alloc(0) - var i = 0 - var c - while (t.length < len) { - c = i2ops(i++) - t = Buffer.concat([t, createHash('sha1').update(seed).update(c).digest()]) - } - return t.slice(0, len) -} - -function i2ops (c) { - var out = Buffer.allocUnsafe(4) - out.writeUInt32BE(c, 0) - return out -} - -},{"create-hash":74,"safe-buffer":250}],240:[function(require,module,exports){ -var parseKeys = require('parse-asn1') -var mgf = require('./mgf') -var xor = require('./xor') -var BN = require('bn.js') -var crt = require('browserify-rsa') -var createHash = require('create-hash') -var withPublic = require('./withPublic') -var Buffer = require('safe-buffer').Buffer - -module.exports = function privateDecrypt (privateKey, enc, reverse) { - var padding - if (privateKey.padding) { - padding = privateKey.padding - } else if (reverse) { - padding = 1 - } else { - padding = 4 - } - - var key = parseKeys(privateKey) - var k = key.modulus.byteLength() - if (enc.length > k || new BN(enc).cmp(key.modulus) >= 0) { - throw new Error('decryption error') - } - var msg - if (reverse) { - msg = withPublic(new BN(enc), key) - } else { - msg = crt(enc, key) - } - var zBuffer = Buffer.alloc(k - msg.length) - msg = Buffer.concat([zBuffer, msg], k) - if (padding === 4) { - return oaep(key, msg) - } else if (padding === 1) { - return pkcs1(key, msg, reverse) - } else if (padding === 3) { - return msg - } else { - throw new Error('unknown padding') - } -} - -function oaep (key, msg) { - var k = key.modulus.byteLength() - var iHash = createHash('sha1').update(Buffer.alloc(0)).digest() - var hLen = iHash.length - if (msg[0] !== 0) { - throw new Error('decryption error') - } - var maskedSeed = msg.slice(1, hLen + 1) - var maskedDb = msg.slice(hLen + 1) - var seed = xor(maskedSeed, mgf(maskedDb, hLen)) - var db = xor(maskedDb, mgf(seed, k - hLen - 1)) - if (compare(iHash, db.slice(0, hLen))) { - throw new Error('decryption error') - } - var i = hLen - while (db[i] === 0) { - i++ - } - if (db[i++] !== 1) { - throw new Error('decryption error') - } - return db.slice(i) -} - -function pkcs1 (key, msg, reverse) { - var p1 = msg.slice(0, 2) - var i = 2 - var status = 0 - while (msg[i++] !== 0) { - if (i >= msg.length) { - status++ - break - } - } - var ps = msg.slice(2, i - 1) - - if ((p1.toString('hex') !== '0002' && !reverse) || (p1.toString('hex') !== '0001' && reverse)) { - status++ - } - if (ps.length < 8) { - status++ - } - if (status) { - throw new Error('decryption error') - } - return msg.slice(i) -} -function compare (a, b) { - a = Buffer.from(a) - b = Buffer.from(b) - var dif = 0 - var len = a.length - if (a.length !== b.length) { - dif++ - len = Math.min(a.length, b.length) - } - var i = -1 - while (++i < len) { - dif += (a[i] ^ b[i]) - } - return dif -} - -},{"./mgf":239,"./withPublic":242,"./xor":243,"bn.js":18,"browserify-rsa":41,"create-hash":74,"parse-asn1":230,"safe-buffer":250}],241:[function(require,module,exports){ -var parseKeys = require('parse-asn1') -var randomBytes = require('randombytes') -var createHash = require('create-hash') -var mgf = require('./mgf') -var xor = require('./xor') -var BN = require('bn.js') -var withPublic = require('./withPublic') -var crt = require('browserify-rsa') -var Buffer = require('safe-buffer').Buffer - -module.exports = function publicEncrypt (publicKey, msg, reverse) { - var padding - if (publicKey.padding) { - padding = publicKey.padding - } else if (reverse) { - padding = 1 - } else { - padding = 4 - } - var key = parseKeys(publicKey) - var paddedMsg - if (padding === 4) { - paddedMsg = oaep(key, msg) - } else if (padding === 1) { - paddedMsg = pkcs1(key, msg, reverse) - } else if (padding === 3) { - paddedMsg = new BN(msg) - if (paddedMsg.cmp(key.modulus) >= 0) { - throw new Error('data too long for modulus') - } - } else { - throw new Error('unknown padding') - } - if (reverse) { - return crt(paddedMsg, key) - } else { - return withPublic(paddedMsg, key) - } -} - -function oaep (key, msg) { - var k = key.modulus.byteLength() - var mLen = msg.length - var iHash = createHash('sha1').update(Buffer.alloc(0)).digest() - var hLen = iHash.length - var hLen2 = 2 * hLen - if (mLen > k - hLen2 - 2) { - throw new Error('message too long') - } - var ps = Buffer.alloc(k - mLen - hLen2 - 2) - var dblen = k - hLen - 1 - var seed = randomBytes(hLen) - var maskedDb = xor(Buffer.concat([iHash, ps, Buffer.alloc(1, 1), msg], dblen), mgf(seed, dblen)) - var maskedSeed = xor(seed, mgf(maskedDb, hLen)) - return new BN(Buffer.concat([Buffer.alloc(1), maskedSeed, maskedDb], k)) -} -function pkcs1 (key, msg, reverse) { - var mLen = msg.length - var k = key.modulus.byteLength() - if (mLen > k - 11) { - throw new Error('message too long') - } - var ps - if (reverse) { - ps = Buffer.alloc(k - mLen - 3, 0xff) - } else { - ps = nonZero(k - mLen - 3) - } - return new BN(Buffer.concat([Buffer.from([0, reverse ? 1 : 2]), ps, Buffer.alloc(1), msg], k)) -} -function nonZero (len) { - var out = Buffer.allocUnsafe(len) - var i = 0 - var cache = randomBytes(len * 2) - var cur = 0 - var num - while (i < len) { - if (cur === cache.length) { - cache = randomBytes(len * 2) - cur = 0 - } - num = cache[cur++] - if (num) { - out[i++] = num - } - } - return out -} - -},{"./mgf":239,"./withPublic":242,"./xor":243,"bn.js":18,"browserify-rsa":41,"create-hash":74,"parse-asn1":230,"randombytes":244,"safe-buffer":250}],242:[function(require,module,exports){ -var BN = require('bn.js') -var Buffer = require('safe-buffer').Buffer - -function withPublic (paddedMsg, key) { - return Buffer.from(paddedMsg - .toRed(BN.mont(key.modulus)) - .redPow(new BN(key.publicExponent)) - .fromRed() - .toArray()) -} - -module.exports = withPublic - -},{"bn.js":18,"safe-buffer":250}],243:[function(require,module,exports){ -module.exports = function xor (a, b) { - var len = a.length - var i = -1 - while (++i < len) { - a[i] ^= b[i] - } - return a -} - -},{}],244:[function(require,module,exports){ -(function (process,global){(function (){ -'use strict' - -// limit of Crypto.getRandomValues() -// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues -var MAX_BYTES = 65536 - -// Node supports requesting up to this number of bytes -// https://github.com/nodejs/node/blob/master/lib/internal/crypto/random.js#L48 -var MAX_UINT32 = 4294967295 - -function oldBrowser () { - throw new Error('Secure random number generation is not supported by this browser.\nUse Chrome, Firefox or Internet Explorer 11') -} - -var Buffer = require('safe-buffer').Buffer -var crypto = global.crypto || global.msCrypto - -if (crypto && crypto.getRandomValues) { - module.exports = randomBytes -} else { - module.exports = oldBrowser -} - -function randomBytes (size, cb) { - // phantomjs needs to throw - if (size > MAX_UINT32) throw new RangeError('requested too many random bytes') - - var bytes = Buffer.allocUnsafe(size) - - if (size > 0) { // getRandomValues fails on IE if size == 0 - if (size > MAX_BYTES) { // this is the max bytes crypto.getRandomValues - // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues - for (var generated = 0; generated < size; generated += MAX_BYTES) { - // buffer.slice automatically checks if the end is past the end of - // the buffer so we don't have to here - crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES)) - } - } else { - crypto.getRandomValues(bytes) - } - } - - if (typeof cb === 'function') { - return process.nextTick(function () { - cb(null, bytes) - }) - } - - return bytes -} - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"_process":237,"safe-buffer":250}],245:[function(require,module,exports){ -(function (process,global){(function (){ -'use strict' - -function oldBrowser () { - throw new Error('secure random number generation not supported by this browser\nuse chrome, FireFox or Internet Explorer 11') -} -var safeBuffer = require('safe-buffer') -var randombytes = require('randombytes') -var Buffer = safeBuffer.Buffer -var kBufferMaxLength = safeBuffer.kMaxLength -var crypto = global.crypto || global.msCrypto -var kMaxUint32 = Math.pow(2, 32) - 1 -function assertOffset (offset, length) { - if (typeof offset !== 'number' || offset !== offset) { // eslint-disable-line no-self-compare - throw new TypeError('offset must be a number') - } - - if (offset > kMaxUint32 || offset < 0) { - throw new TypeError('offset must be a uint32') - } - - if (offset > kBufferMaxLength || offset > length) { - throw new RangeError('offset out of range') - } -} - -function assertSize (size, offset, length) { - if (typeof size !== 'number' || size !== size) { // eslint-disable-line no-self-compare - throw new TypeError('size must be a number') - } - - if (size > kMaxUint32 || size < 0) { - throw new TypeError('size must be a uint32') - } - - if (size + offset > length || size > kBufferMaxLength) { - throw new RangeError('buffer too small') - } -} -if ((crypto && crypto.getRandomValues) || !process.browser) { - exports.randomFill = randomFill - exports.randomFillSync = randomFillSync -} else { - exports.randomFill = oldBrowser - exports.randomFillSync = oldBrowser -} -function randomFill (buf, offset, size, cb) { - if (!Buffer.isBuffer(buf) && !(buf instanceof global.Uint8Array)) { - throw new TypeError('"buf" argument must be a Buffer or Uint8Array') - } - - if (typeof offset === 'function') { - cb = offset - offset = 0 - size = buf.length - } else if (typeof size === 'function') { - cb = size - size = buf.length - offset - } else if (typeof cb !== 'function') { - throw new TypeError('"cb" argument must be a function') - } - assertOffset(offset, buf.length) - assertSize(size, offset, buf.length) - return actualFill(buf, offset, size, cb) -} - -function actualFill (buf, offset, size, cb) { - if (process.browser) { - var ourBuf = buf.buffer - var uint = new Uint8Array(ourBuf, offset, size) - crypto.getRandomValues(uint) - if (cb) { - process.nextTick(function () { - cb(null, buf) - }) - return - } - return buf - } - if (cb) { - randombytes(size, function (err, bytes) { - if (err) { - return cb(err) - } - bytes.copy(buf, offset) - cb(null, buf) - }) - return - } - var bytes = randombytes(size) - bytes.copy(buf, offset) - return buf -} -function randomFillSync (buf, offset, size) { - if (typeof offset === 'undefined') { - offset = 0 - } - if (!Buffer.isBuffer(buf) && !(buf instanceof global.Uint8Array)) { - throw new TypeError('"buf" argument must be a Buffer or Uint8Array') - } - - assertOffset(offset, buf.length) - - if (size === undefined) size = buf.length - offset - - assertSize(size, offset, buf.length) - - return actualFill(buf, offset, size) -} - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"_process":237,"randombytes":244,"safe-buffer":250}],246:[function(require,module,exports){ -module.exports = require('./lib/retry'); -},{"./lib/retry":247}],247:[function(require,module,exports){ -var RetryOperation = require('./retry_operation'); - -exports.operation = function(options) { - var timeouts = exports.timeouts(options); - return new RetryOperation(timeouts, { - forever: options && (options.forever || options.retries === Infinity), - unref: options && options.unref, - maxRetryTime: options && options.maxRetryTime - }); -}; - -exports.timeouts = function(options) { - if (options instanceof Array) { - return [].concat(options); - } - - var opts = { - retries: 10, - factor: 2, - minTimeout: 1 * 1000, - maxTimeout: Infinity, - randomize: false - }; - for (var key in options) { - opts[key] = options[key]; - } - - if (opts.minTimeout > opts.maxTimeout) { - throw new Error('minTimeout is greater than maxTimeout'); - } - - var timeouts = []; - for (var i = 0; i < opts.retries; i++) { - timeouts.push(this.createTimeout(i, opts)); - } - - if (options && options.forever && !timeouts.length) { - timeouts.push(this.createTimeout(i, opts)); - } - - // sort the array numerically ascending - timeouts.sort(function(a,b) { - return a - b; - }); - - return timeouts; -}; - -exports.createTimeout = function(attempt, opts) { - var random = (opts.randomize) - ? (Math.random() + 1) - : 1; - - var timeout = Math.round(random * Math.max(opts.minTimeout, 1) * Math.pow(opts.factor, attempt)); - timeout = Math.min(timeout, opts.maxTimeout); - - return timeout; -}; - -exports.wrap = function(obj, options, methods) { - if (options instanceof Array) { - methods = options; - options = null; - } - - if (!methods) { - methods = []; - for (var key in obj) { - if (typeof obj[key] === 'function') { - methods.push(key); - } - } - } - - for (var i = 0; i < methods.length; i++) { - var method = methods[i]; - var original = obj[method]; - - obj[method] = function retryWrapper(original) { - var op = exports.operation(options); - var args = Array.prototype.slice.call(arguments, 1); - var callback = args.pop(); - - args.push(function(err) { - if (op.retry(err)) { - return; - } - if (err) { - arguments[0] = op.mainError(); - } - callback.apply(this, arguments); - }); - - op.attempt(function() { - original.apply(obj, args); - }); - }.bind(obj, original); - obj[method].options = options; - } -}; - -},{"./retry_operation":248}],248:[function(require,module,exports){ -function RetryOperation(timeouts, options) { - // Compatibility for the old (timeouts, retryForever) signature - if (typeof options === 'boolean') { - options = { forever: options }; - } - - this._originalTimeouts = JSON.parse(JSON.stringify(timeouts)); - this._timeouts = timeouts; - this._options = options || {}; - this._maxRetryTime = options && options.maxRetryTime || Infinity; - this._fn = null; - this._errors = []; - this._attempts = 1; - this._operationTimeout = null; - this._operationTimeoutCb = null; - this._timeout = null; - this._operationStart = null; - this._timer = null; - - if (this._options.forever) { - this._cachedTimeouts = this._timeouts.slice(0); - } -} -module.exports = RetryOperation; - -RetryOperation.prototype.reset = function() { - this._attempts = 1; - this._timeouts = this._originalTimeouts.slice(0); -} - -RetryOperation.prototype.stop = function() { - if (this._timeout) { - clearTimeout(this._timeout); - } - if (this._timer) { - clearTimeout(this._timer); - } - - this._timeouts = []; - this._cachedTimeouts = null; -}; - -RetryOperation.prototype.retry = function(err) { - if (this._timeout) { - clearTimeout(this._timeout); - } - - if (!err) { - return false; - } - var currentTime = new Date().getTime(); - if (err && currentTime - this._operationStart >= this._maxRetryTime) { - this._errors.push(err); - this._errors.unshift(new Error('RetryOperation timeout occurred')); - return false; - } - - this._errors.push(err); - - var timeout = this._timeouts.shift(); - if (timeout === undefined) { - if (this._cachedTimeouts) { - // retry forever, only keep last error - this._errors.splice(0, this._errors.length - 1); - timeout = this._cachedTimeouts.slice(-1); - } else { - return false; - } - } - - var self = this; - this._timer = setTimeout(function() { - self._attempts++; - - if (self._operationTimeoutCb) { - self._timeout = setTimeout(function() { - self._operationTimeoutCb(self._attempts); - }, self._operationTimeout); - - if (self._options.unref) { - self._timeout.unref(); - } - } - - self._fn(self._attempts); - }, timeout); - - if (this._options.unref) { - this._timer.unref(); - } - - return true; -}; - -RetryOperation.prototype.attempt = function(fn, timeoutOps) { - this._fn = fn; - - if (timeoutOps) { - if (timeoutOps.timeout) { - this._operationTimeout = timeoutOps.timeout; - } - if (timeoutOps.cb) { - this._operationTimeoutCb = timeoutOps.cb; - } - } - - var self = this; - if (this._operationTimeoutCb) { - this._timeout = setTimeout(function() { - self._operationTimeoutCb(); - }, self._operationTimeout); - } - - this._operationStart = new Date().getTime(); - - this._fn(this._attempts); -}; - -RetryOperation.prototype.try = function(fn) { - console.log('Using RetryOperation.try() is deprecated'); - this.attempt(fn); -}; - -RetryOperation.prototype.start = function(fn) { - console.log('Using RetryOperation.start() is deprecated'); - this.attempt(fn); -}; - -RetryOperation.prototype.start = RetryOperation.prototype.try; - -RetryOperation.prototype.errors = function() { - return this._errors; -}; - -RetryOperation.prototype.attempts = function() { - return this._attempts; -}; - -RetryOperation.prototype.mainError = function() { - if (this._errors.length === 0) { - return null; - } - - var counts = {}; - var mainError = null; - var mainErrorCount = 0; - - for (var i = 0; i < this._errors.length; i++) { - var error = this._errors[i]; - var message = error.message; - var count = (counts[message] || 0) + 1; - - counts[message] = count; - - if (count >= mainErrorCount) { - mainError = error; - mainErrorCount = count; - } - } - - return mainError; -}; - -},{}],249:[function(require,module,exports){ -'use strict' -var Buffer = require('buffer').Buffer -var inherits = require('inherits') -var HashBase = require('hash-base') - -var ARRAY16 = new Array(16) - -var zl = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 -] - -var zr = [ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 -] - -var sl = [ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 -] - -var sr = [ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 -] - -var hl = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e] -var hr = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000] - -function RIPEMD160 () { - HashBase.call(this, 64) - - // state - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 - this._e = 0xc3d2e1f0 -} - -inherits(RIPEMD160, HashBase) - -RIPEMD160.prototype._update = function () { - var words = ARRAY16 - for (var j = 0; j < 16; ++j) words[j] = this._block.readInt32LE(j * 4) - - var al = this._a | 0 - var bl = this._b | 0 - var cl = this._c | 0 - var dl = this._d | 0 - var el = this._e | 0 - - var ar = this._a | 0 - var br = this._b | 0 - var cr = this._c | 0 - var dr = this._d | 0 - var er = this._e | 0 - - // computation - for (var i = 0; i < 80; i += 1) { - var tl - var tr - if (i < 16) { - tl = fn1(al, bl, cl, dl, el, words[zl[i]], hl[0], sl[i]) - tr = fn5(ar, br, cr, dr, er, words[zr[i]], hr[0], sr[i]) - } else if (i < 32) { - tl = fn2(al, bl, cl, dl, el, words[zl[i]], hl[1], sl[i]) - tr = fn4(ar, br, cr, dr, er, words[zr[i]], hr[1], sr[i]) - } else if (i < 48) { - tl = fn3(al, bl, cl, dl, el, words[zl[i]], hl[2], sl[i]) - tr = fn3(ar, br, cr, dr, er, words[zr[i]], hr[2], sr[i]) - } else if (i < 64) { - tl = fn4(al, bl, cl, dl, el, words[zl[i]], hl[3], sl[i]) - tr = fn2(ar, br, cr, dr, er, words[zr[i]], hr[3], sr[i]) - } else { // if (i<80) { - tl = fn5(al, bl, cl, dl, el, words[zl[i]], hl[4], sl[i]) - tr = fn1(ar, br, cr, dr, er, words[zr[i]], hr[4], sr[i]) - } - - al = el - el = dl - dl = rotl(cl, 10) - cl = bl - bl = tl - - ar = er - er = dr - dr = rotl(cr, 10) - cr = br - br = tr - } - - // update state - var t = (this._b + cl + dr) | 0 - this._b = (this._c + dl + er) | 0 - this._c = (this._d + el + ar) | 0 - this._d = (this._e + al + br) | 0 - this._e = (this._a + bl + cr) | 0 - this._a = t -} - -RIPEMD160.prototype._digest = function () { - // create padding and handle blocks - this._block[this._blockOffset++] = 0x80 - if (this._blockOffset > 56) { - this._block.fill(0, this._blockOffset, 64) - this._update() - this._blockOffset = 0 - } - - this._block.fill(0, this._blockOffset, 56) - this._block.writeUInt32LE(this._length[0], 56) - this._block.writeUInt32LE(this._length[1], 60) - this._update() - - // produce result - var buffer = Buffer.alloc ? Buffer.alloc(20) : new Buffer(20) - buffer.writeInt32LE(this._a, 0) - buffer.writeInt32LE(this._b, 4) - buffer.writeInt32LE(this._c, 8) - buffer.writeInt32LE(this._d, 12) - buffer.writeInt32LE(this._e, 16) - return buffer -} - -function rotl (x, n) { - return (x << n) | (x >>> (32 - n)) -} - -function fn1 (a, b, c, d, e, m, k, s) { - return (rotl((a + (b ^ c ^ d) + m + k) | 0, s) + e) | 0 -} - -function fn2 (a, b, c, d, e, m, k, s) { - return (rotl((a + ((b & c) | ((~b) & d)) + m + k) | 0, s) + e) | 0 -} - -function fn3 (a, b, c, d, e, m, k, s) { - return (rotl((a + ((b | (~c)) ^ d) + m + k) | 0, s) + e) | 0 -} - -function fn4 (a, b, c, d, e, m, k, s) { - return (rotl((a + ((b & d) | (c & (~d))) + m + k) | 0, s) + e) | 0 -} - -function fn5 (a, b, c, d, e, m, k, s) { - return (rotl((a + (b ^ (c | (~d))) + m + k) | 0, s) + e) | 0 -} - -module.exports = RIPEMD160 - -},{"buffer":68,"hash-base":116,"inherits":146}],250:[function(require,module,exports){ -/*! safe-buffer. MIT License. Feross Aboukhadijeh */ -/* eslint-disable node/no-deprecated-api */ -var buffer = require('buffer') -var Buffer = buffer.Buffer - -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] - } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} - -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} - -SafeBuffer.prototype = Object.create(Buffer.prototype) - -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) - -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} - -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) - } - return buf -} - -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) -} - -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return buffer.SlowBuffer(size) -} - -},{"buffer":68}],251:[function(require,module,exports){ -(function (process){(function (){ -/* eslint-disable node/no-deprecated-api */ - -'use strict' - -var buffer = require('buffer') -var Buffer = buffer.Buffer - -var safer = {} - -var key - -for (key in buffer) { - if (!buffer.hasOwnProperty(key)) continue - if (key === 'SlowBuffer' || key === 'Buffer') continue - safer[key] = buffer[key] -} - -var Safer = safer.Buffer = {} -for (key in Buffer) { - if (!Buffer.hasOwnProperty(key)) continue - if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue - Safer[key] = Buffer[key] -} - -safer.Buffer.prototype = Buffer.prototype - -if (!Safer.from || Safer.from === Uint8Array.from) { - Safer.from = function (value, encodingOrOffset, length) { - if (typeof value === 'number') { - throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) - } - if (value && typeof value.length === 'undefined') { - throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) - } - return Buffer(value, encodingOrOffset, length) - } -} - -if (!Safer.alloc) { - Safer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) - } - if (size < 0 || size >= 2 * (1 << 30)) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } - var buf = Buffer(size) - if (!fill || fill.length === 0) { - buf.fill(0) - } else if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - return buf - } -} - -if (!safer.kStringMaxLength) { - try { - safer.kStringMaxLength = process.binding('buffer').kStringMaxLength - } catch (e) { - // we can't determine kStringMaxLength in environments where process.binding - // is unsupported, so let's not set it - } -} - -if (!safer.constants) { - safer.constants = { - MAX_LENGTH: safer.kMaxLength - } - if (safer.kStringMaxLength) { - safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength - } -} - -module.exports = safer - -}).call(this)}).call(this,require('_process')) - -},{"_process":237,"buffer":68}],252:[function(require,module,exports){ -var grammar = module.exports = { - v: [{ - name: 'version', - reg: /^(\d*)$/ - }], - o: [{ - // o=- 20518 0 IN IP4 203.0.113.1 - // NB: sessionId will be a String in most cases because it is huge - name: 'origin', - reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/, - names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'], - format: '%s %s %d %s IP%d %s' - }], - // default parsing of these only (though some of these feel outdated) - s: [{ name: 'name' }], - i: [{ name: 'description' }], - u: [{ name: 'uri' }], - e: [{ name: 'email' }], - p: [{ name: 'phone' }], - z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly... - r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly - // k: [{}], // outdated thing ignored - t: [{ - // t=0 0 - name: 'timing', - reg: /^(\d*) (\d*)/, - names: ['start', 'stop'], - format: '%d %d' - }], - c: [{ - // c=IN IP4 10.47.197.26 - name: 'connection', - reg: /^IN IP(\d) (\S*)/, - names: ['version', 'ip'], - format: 'IN IP%d %s' - }], - b: [{ - // b=AS:4000 - push: 'bandwidth', - reg: /^(TIAS|AS|CT|RR|RS):(\d*)/, - names: ['type', 'limit'], - format: '%s:%s' - }], - m: [{ - // m=video 51744 RTP/AVP 126 97 98 34 31 - // NB: special - pushes to session - // TODO: rtp/fmtp should be filtered by the payloads found here? - reg: /^(\w*) (\d*) ([\w/]*)(?: (.*))?/, - names: ['type', 'port', 'protocol', 'payloads'], - format: '%s %d %s %s' - }], - a: [ - { - // a=rtpmap:110 opus/48000/2 - push: 'rtp', - reg: /^rtpmap:(\d*) ([\w\-.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/, - names: ['payload', 'codec', 'rate', 'encoding'], - format: function (o) { - return (o.encoding) - ? 'rtpmap:%d %s/%s/%s' - : o.rate - ? 'rtpmap:%d %s/%s' - : 'rtpmap:%d %s'; - } - }, - { - // a=fmtp:108 profile-level-id=24;object=23;bitrate=64000 - // a=fmtp:111 minptime=10; useinbandfec=1 - push: 'fmtp', - reg: /^fmtp:(\d*) ([\S| ]*)/, - names: ['payload', 'config'], - format: 'fmtp:%d %s' - }, - { - // a=control:streamid=0 - name: 'control', - reg: /^control:(.*)/, - format: 'control:%s' - }, - { - // a=rtcp:65179 IN IP4 193.84.77.194 - name: 'rtcp', - reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/, - names: ['port', 'netType', 'ipVer', 'address'], - format: function (o) { - return (o.address != null) - ? 'rtcp:%d %s IP%d %s' - : 'rtcp:%d'; - } - }, - { - // a=rtcp-fb:98 trr-int 100 - push: 'rtcpFbTrrInt', - reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/, - names: ['payload', 'value'], - format: 'rtcp-fb:%s trr-int %d' - }, - { - // a=rtcp-fb:98 nack rpsi - push: 'rtcpFb', - reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/, - names: ['payload', 'type', 'subtype'], - format: function (o) { - return (o.subtype != null) - ? 'rtcp-fb:%s %s %s' - : 'rtcp-fb:%s %s'; - } - }, - { - // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset - // a=extmap:1/recvonly URI-gps-string - // a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24 - push: 'ext', - reg: /^extmap:(\d+)(?:\/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?/, - names: ['value', 'direction', 'encrypt-uri', 'uri', 'config'], - format: function (o) { - return ( - 'extmap:%d' + - (o.direction ? '/%s' : '%v') + - (o['encrypt-uri'] ? ' %s' : '%v') + - ' %s' + - (o.config ? ' %s' : '') - ); - } - }, - { - // a=extmap-allow-mixed - name: 'extmapAllowMixed', - reg: /^(extmap-allow-mixed)/ - }, - { - // a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32 - push: 'crypto', - reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/, - names: ['id', 'suite', 'config', 'sessionConfig'], - format: function (o) { - return (o.sessionConfig != null) - ? 'crypto:%d %s %s %s' - : 'crypto:%d %s %s'; - } - }, - { - // a=setup:actpass - name: 'setup', - reg: /^setup:(\w*)/, - format: 'setup:%s' - }, - { - // a=connection:new - name: 'connectionType', - reg: /^connection:(new|existing)/, - format: 'connection:%s' - }, - { - // a=mid:1 - name: 'mid', - reg: /^mid:([^\s]*)/, - format: 'mid:%s' - }, - { - // a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a - name: 'msid', - reg: /^msid:(.*)/, - format: 'msid:%s' - }, - { - // a=ptime:20 - name: 'ptime', - reg: /^ptime:(\d*(?:\.\d*)*)/, - format: 'ptime:%d' - }, - { - // a=maxptime:60 - name: 'maxptime', - reg: /^maxptime:(\d*(?:\.\d*)*)/, - format: 'maxptime:%d' - }, - { - // a=sendrecv - name: 'direction', - reg: /^(sendrecv|recvonly|sendonly|inactive)/ - }, - { - // a=ice-lite - name: 'icelite', - reg: /^(ice-lite)/ - }, - { - // a=ice-ufrag:F7gI - name: 'iceUfrag', - reg: /^ice-ufrag:(\S*)/, - format: 'ice-ufrag:%s' - }, - { - // a=ice-pwd:x9cml/YzichV2+XlhiMu8g - name: 'icePwd', - reg: /^ice-pwd:(\S*)/, - format: 'ice-pwd:%s' - }, - { - // a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33 - name: 'fingerprint', - reg: /^fingerprint:(\S*) (\S*)/, - names: ['type', 'hash'], - format: 'fingerprint:%s %s' - }, - { - // a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host - // a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10 - // a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10 - // a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10 - // a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10 - push:'candidates', - reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/, - names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'], - format: function (o) { - var str = 'candidate:%s %d %s %d %s %d typ %s'; - - str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v'; - - // NB: candidate has three optional chunks, so %void middles one if it's missing - str += (o.tcptype != null) ? ' tcptype %s' : '%v'; - - if (o.generation != null) { - str += ' generation %d'; - } - - str += (o['network-id'] != null) ? ' network-id %d' : '%v'; - str += (o['network-cost'] != null) ? ' network-cost %d' : '%v'; - return str; - } - }, - { - // a=end-of-candidates (keep after the candidates line for readability) - name: 'endOfCandidates', - reg: /^(end-of-candidates)/ - }, - { - // a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ... - name: 'remoteCandidates', - reg: /^remote-candidates:(.*)/, - format: 'remote-candidates:%s' - }, - { - // a=ice-options:google-ice - name: 'iceOptions', - reg: /^ice-options:(\S*)/, - format: 'ice-options:%s' - }, - { - // a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1 - push: 'ssrcs', - reg: /^ssrc:(\d*) ([\w_-]*)(?::(.*))?/, - names: ['id', 'attribute', 'value'], - format: function (o) { - var str = 'ssrc:%d'; - if (o.attribute != null) { - str += ' %s'; - if (o.value != null) { - str += ':%s'; - } - } - return str; - } - }, - { - // a=ssrc-group:FEC 1 2 - // a=ssrc-group:FEC-FR 3004364195 1080772241 - push: 'ssrcGroups', - // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E - reg: /^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/, - names: ['semantics', 'ssrcs'], - format: 'ssrc-group:%s %s' - }, - { - // a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV - name: 'msidSemantic', - reg: /^msid-semantic:\s?(\w*) (\S*)/, - names: ['semantic', 'token'], - format: 'msid-semantic: %s %s' // space after ':' is not accidental - }, - { - // a=group:BUNDLE audio video - push: 'groups', - reg: /^group:(\w*) (.*)/, - names: ['type', 'mids'], - format: 'group:%s %s' - }, - { - // a=rtcp-mux - name: 'rtcpMux', - reg: /^(rtcp-mux)/ - }, - { - // a=rtcp-rsize - name: 'rtcpRsize', - reg: /^(rtcp-rsize)/ - }, - { - // a=sctpmap:5000 webrtc-datachannel 1024 - name: 'sctpmap', - reg: /^sctpmap:([\w_/]*) (\S*)(?: (\S*))?/, - names: ['sctpmapNumber', 'app', 'maxMessageSize'], - format: function (o) { - return (o.maxMessageSize != null) - ? 'sctpmap:%s %s %s' - : 'sctpmap:%s %s'; - } - }, - { - // a=x-google-flag:conference - name: 'xGoogleFlag', - reg: /^x-google-flag:([^\s]*)/, - format: 'x-google-flag:%s' - }, - { - // a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0 - push: 'rids', - reg: /^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/, - names: ['id', 'direction', 'params'], - format: function (o) { - return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s'; - } - }, - { - // a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250] - // a=imageattr:* send [x=800,y=640] recv * - // a=imageattr:100 recv [x=320,y=240] - push: 'imageattrs', - reg: new RegExp( - // a=imageattr:97 - '^imageattr:(\\d+|\\*)' + - // send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] - '[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)' + - // recv [x=330,y=250] - '(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?' - ), - names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'], - format: function (o) { - return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : ''); - } - }, - { - // a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8 - // a=simulcast:recv 1;4,5 send 6;7 - name: 'simulcast', - reg: new RegExp( - // a=simulcast: - '^simulcast:' + - // send 1,2,3;~4,~5 - '(send|recv) ([a-zA-Z0-9\\-_~;,]+)' + - // space + recv 6;~7,~8 - '(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?' + - // end - '$' - ), - names: ['dir1', 'list1', 'dir2', 'list2'], - format: function (o) { - return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : ''); - } - }, - { - // old simulcast draft 03 (implemented by Firefox) - // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03 - // a=simulcast: recv pt=97;98 send pt=97 - // a=simulcast: send rid=5;6;7 paused=6,7 - name: 'simulcast_03', - reg: /^simulcast:[\s\t]+([\S+\s\t]+)$/, - names: ['value'], - format: 'simulcast: %s' - }, - { - // a=framerate:25 - // a=framerate:29.97 - name: 'framerate', - reg: /^framerate:(\d+(?:$|\.\d+))/, - format: 'framerate:%s' - }, - { - // RFC4570 - // a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5 - name: 'sourceFilter', - reg: /^source-filter: *(excl|incl) (\S*) (IP4|IP6|\*) (\S*) (.*)/, - names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'], - format: 'source-filter: %s %s %s %s %s' - }, - { - // a=bundle-only - name: 'bundleOnly', - reg: /^(bundle-only)/ - }, - { - // a=label:1 - name: 'label', - reg: /^label:(.+)/, - format: 'label:%s' - }, - { - // RFC version 26 for SCTP over DTLS - // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5 - name: 'sctpPort', - reg: /^sctp-port:(\d+)$/, - format: 'sctp-port:%s' - }, - { - // RFC version 26 for SCTP over DTLS - // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6 - name: 'maxMessageSize', - reg: /^max-message-size:(\d+)$/, - format: 'max-message-size:%s' - }, - { - // RFC7273 - // a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37 - push:'tsRefClocks', - reg: /^ts-refclk:([^\s=]*)(?:=(\S*))?/, - names: ['clksrc', 'clksrcExt'], - format: function (o) { - return 'ts-refclk:%s' + (o.clksrcExt != null ? '=%s' : ''); - } - }, - { - // RFC7273 - // a=mediaclk:direct=963214424 - name:'mediaClk', - reg: /^mediaclk:(?:id=(\S*))? *([^\s=]*)(?:=(\S*))?(?: *rate=(\d+)\/(\d+))?/, - names: ['id', 'mediaClockName', 'mediaClockValue', 'rateNumerator', 'rateDenominator'], - format: function (o) { - var str = 'mediaclk:'; - str += (o.id != null ? 'id=%s %s' : '%v%s'); - str += (o.mediaClockValue != null ? '=%s' : ''); - str += (o.rateNumerator != null ? ' rate=%s' : ''); - str += (o.rateDenominator != null ? '/%s' : ''); - return str; - } - }, - { - // a=keywds:keywords - name: 'keywords', - reg: /^keywds:(.+)$/, - format: 'keywds:%s' - }, - { - // a=content:main - name: 'content', - reg: /^content:(.+)/, - format: 'content:%s' - }, - // BFCP https://tools.ietf.org/html/rfc4583 - { - // a=floorctrl:c-s - name: 'bfcpFloorCtrl', - reg: /^floorctrl:(c-only|s-only|c-s)/, - format: 'floorctrl:%s' - }, - { - // a=confid:1 - name: 'bfcpConfId', - reg: /^confid:(\d+)/, - format: 'confid:%s' - }, - { - // a=userid:1 - name: 'bfcpUserId', - reg: /^userid:(\d+)/, - format: 'userid:%s' - }, - { - // a=floorid:1 - name: 'bfcpFloorId', - reg: /^floorid:(.+) (?:m-stream|mstrm):(.+)/, - names: ['id', 'mStream'], - format: 'floorid:%s mstrm:%s' - }, - { - // any a= that we don't understand is kept verbatim on media.invalid - push: 'invalid', - names: ['value'] - } - ] -}; - -// set sensible defaults to avoid polluting the grammar with boring details -Object.keys(grammar).forEach(function (key) { - var objs = grammar[key]; - objs.forEach(function (obj) { - if (!obj.reg) { - obj.reg = /(.*)/; - } - if (!obj.format) { - obj.format = '%s'; - } - }); -}); - -},{}],253:[function(require,module,exports){ -var parser = require('./parser'); -var writer = require('./writer'); - -exports.write = writer; -exports.parse = parser.parse; -exports.parseParams = parser.parseParams; -exports.parseFmtpConfig = parser.parseFmtpConfig; // Alias of parseParams(). -exports.parsePayloads = parser.parsePayloads; -exports.parseRemoteCandidates = parser.parseRemoteCandidates; -exports.parseImageAttributes = parser.parseImageAttributes; -exports.parseSimulcastStreamList = parser.parseSimulcastStreamList; - -},{"./parser":254,"./writer":255}],254:[function(require,module,exports){ -var toIntIfInt = function (v) { - return String(Number(v)) === v ? Number(v) : v; -}; - -var attachProperties = function (match, location, names, rawName) { - if (rawName && !names) { - location[rawName] = toIntIfInt(match[1]); - } - else { - for (var i = 0; i < names.length; i += 1) { - if (match[i+1] != null) { - location[names[i]] = toIntIfInt(match[i+1]); - } - } - } -}; - -var parseReg = function (obj, location, content) { - var needsBlank = obj.name && obj.names; - if (obj.push && !location[obj.push]) { - location[obj.push] = []; - } - else if (needsBlank && !location[obj.name]) { - location[obj.name] = {}; - } - var keyLocation = obj.push ? - {} : // blank object that will be pushed - needsBlank ? location[obj.name] : location; // otherwise, named location or root - - attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name); - - if (obj.push) { - location[obj.push].push(keyLocation); - } -}; - -var grammar = require('./grammar'); -var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/); - -exports.parse = function (sdp) { - var session = {} - , media = [] - , location = session; // points at where properties go under (one of the above) - - // parse lines we understand - sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) { - var type = l[0]; - var content = l.slice(2); - if (type === 'm') { - media.push({rtp: [], fmtp: []}); - location = media[media.length-1]; // point at latest media line - } - - for (var j = 0; j < (grammar[type] || []).length; j += 1) { - var obj = grammar[type][j]; - if (obj.reg.test(content)) { - return parseReg(obj, location, content); - } - } - }); - - session.media = media; // link it up - return session; -}; - -var paramReducer = function (acc, expr) { - var s = expr.split(/=(.+)/, 2); - if (s.length === 2) { - acc[s[0]] = toIntIfInt(s[1]); - } else if (s.length === 1 && expr.length > 1) { - acc[s[0]] = undefined; - } - return acc; -}; - -exports.parseParams = function (str) { - return str.split(/;\s?/).reduce(paramReducer, {}); -}; - -// For backward compatibility - alias will be removed in 3.0.0 -exports.parseFmtpConfig = exports.parseParams; - -exports.parsePayloads = function (str) { - return str.toString().split(' ').map(Number); -}; - -exports.parseRemoteCandidates = function (str) { - var candidates = []; - var parts = str.split(' ').map(toIntIfInt); - for (var i = 0; i < parts.length; i += 3) { - candidates.push({ - component: parts[i], - ip: parts[i + 1], - port: parts[i + 2] - }); - } - return candidates; -}; - -exports.parseImageAttributes = function (str) { - return str.split(' ').map(function (item) { - return item.substring(1, item.length-1).split(',').reduce(paramReducer, {}); - }); -}; - -exports.parseSimulcastStreamList = function (str) { - return str.split(';').map(function (stream) { - return stream.split(',').map(function (format) { - var scid, paused = false; - - if (format[0] !== '~') { - scid = toIntIfInt(format); - } else { - scid = toIntIfInt(format.substring(1, format.length)); - paused = true; - } - - return { - scid: scid, - paused: paused - }; - }); - }); -}; - -},{"./grammar":252}],255:[function(require,module,exports){ -var grammar = require('./grammar'); - -// customized util.format - discards excess arguments and can void middle ones -var formatRegExp = /%[sdv%]/g; -var format = function (formatStr) { - var i = 1; - var args = arguments; - var len = args.length; - return formatStr.replace(formatRegExp, function (x) { - if (i >= len) { - return x; // missing argument - } - var arg = args[i]; - i += 1; - switch (x) { - case '%%': - return '%'; - case '%s': - return String(arg); - case '%d': - return Number(arg); - case '%v': - return ''; - } - }); - // NB: we discard excess arguments - they are typically undefined from makeLine -}; - -var makeLine = function (type, obj, location) { - var str = obj.format instanceof Function ? - (obj.format(obj.push ? location : location[obj.name])) : - obj.format; - - var args = [type + '=' + str]; - if (obj.names) { - for (var i = 0; i < obj.names.length; i += 1) { - var n = obj.names[i]; - if (obj.name) { - args.push(location[obj.name][n]); - } - else { // for mLine and push attributes - args.push(location[obj.names[i]]); - } - } - } - else { - args.push(location[obj.name]); - } - return format.apply(null, args); -}; - -// RFC specified order -// TODO: extend this with all the rest -var defaultOuterOrder = [ - 'v', 'o', 's', 'i', - 'u', 'e', 'p', 'c', - 'b', 't', 'r', 'z', 'a' -]; -var defaultInnerOrder = ['i', 'c', 'b', 'a']; - - -module.exports = function (session, opts) { - opts = opts || {}; - // ensure certain properties exist - if (session.version == null) { - session.version = 0; // 'v=0' must be there (only defined version atm) - } - if (session.name == null) { - session.name = ' '; // 's= ' must be there if no meaningful name set - } - session.media.forEach(function (mLine) { - if (mLine.payloads == null) { - mLine.payloads = ''; - } - }); - - var outerOrder = opts.outerOrder || defaultOuterOrder; - var innerOrder = opts.innerOrder || defaultInnerOrder; - var sdp = []; - - // loop through outerOrder for matching properties on session - outerOrder.forEach(function (type) { - grammar[type].forEach(function (obj) { - if (obj.name in session && session[obj.name] != null) { - sdp.push(makeLine(type, obj, session)); - } - else if (obj.push in session && session[obj.push] != null) { - session[obj.push].forEach(function (el) { - sdp.push(makeLine(type, obj, el)); - }); - } - }); - }); - - // then for each media line, follow the innerOrder - session.media.forEach(function (mLine) { - sdp.push(makeLine('m', grammar.m[0], mLine)); - - innerOrder.forEach(function (type) { - grammar[type].forEach(function (obj) { - if (obj.name in mLine && mLine[obj.name] != null) { - sdp.push(makeLine(type, obj, mLine)); - } - else if (obj.push in mLine && mLine[obj.push] != null) { - mLine[obj.push].forEach(function (el) { - sdp.push(makeLine(type, obj, el)); - }); - } - }); - }); - }); - - return sdp.join('\r\n') + '\r\n'; -}; - -},{"./grammar":252}],256:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -// prototype class for hash functions -function Hash (blockSize, finalSize) { - this._block = Buffer.alloc(blockSize) - this._finalSize = finalSize - this._blockSize = blockSize - this._len = 0 -} - -Hash.prototype.update = function (data, enc) { - if (typeof data === 'string') { - enc = enc || 'utf8' - data = Buffer.from(data, enc) - } - - var block = this._block - var blockSize = this._blockSize - var length = data.length - var accum = this._len - - for (var offset = 0; offset < length;) { - var assigned = accum % blockSize - var remainder = Math.min(length - offset, blockSize - assigned) - - for (var i = 0; i < remainder; i++) { - block[assigned + i] = data[offset + i] - } - - accum += remainder - offset += remainder - - if ((accum % blockSize) === 0) { - this._update(block) - } - } - - this._len += length - return this -} - -Hash.prototype.digest = function (enc) { - var rem = this._len % this._blockSize - - this._block[rem] = 0x80 - - // zero (rem + 1) trailing bits, where (rem + 1) is the smallest - // non-negative solution to the equation (length + 1 + (rem + 1)) === finalSize mod blockSize - this._block.fill(0, rem + 1) - - if (rem >= this._finalSize) { - this._update(this._block) - this._block.fill(0) - } - - var bits = this._len * 8 - - // uint32 - if (bits <= 0xffffffff) { - this._block.writeUInt32BE(bits, this._blockSize - 4) - - // uint64 - } else { - var lowBits = (bits & 0xffffffff) >>> 0 - var highBits = (bits - lowBits) / 0x100000000 - - this._block.writeUInt32BE(highBits, this._blockSize - 8) - this._block.writeUInt32BE(lowBits, this._blockSize - 4) - } - - this._update(this._block) - var hash = this._hash() - - return enc ? hash.toString(enc) : hash -} - -Hash.prototype._update = function () { - throw new Error('_update must be implemented by subclass') -} - -module.exports = Hash - -},{"safe-buffer":250}],257:[function(require,module,exports){ -var exports = module.exports = function SHA (algorithm) { - algorithm = algorithm.toLowerCase() - - var Algorithm = exports[algorithm] - if (!Algorithm) throw new Error(algorithm + ' is not supported (we accept pull requests)') - - return new Algorithm() -} - -exports.sha = require('./sha') -exports.sha1 = require('./sha1') -exports.sha224 = require('./sha224') -exports.sha256 = require('./sha256') -exports.sha384 = require('./sha384') -exports.sha512 = require('./sha512') - -},{"./sha":258,"./sha1":259,"./sha224":260,"./sha256":261,"./sha384":262,"./sha512":263}],258:[function(require,module,exports){ -/* - * A JavaScript implementation of the Secure Hash Algorithm, SHA-0, as defined - * in FIPS PUB 180-1 - * This source code is derived from sha1.js of the same repository. - * The difference between SHA-0 and SHA-1 is just a bitwise rotate left - * operation was added. - */ - -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc | 0, 0xca62c1d6 | 0 -] - -var W = new Array(80) - -function Sha () { - this.init() - this._w = W - - Hash.call(this, 64, 56) -} - -inherits(Sha, Hash) - -Sha.prototype.init = function () { - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 - this._e = 0xc3d2e1f0 - - return this -} - -function rotl5 (num) { - return (num << 5) | (num >>> 27) -} - -function rotl30 (num) { - return (num << 30) | (num >>> 2) -} - -function ft (s, b, c, d) { - if (s === 0) return (b & c) | ((~b) & d) - if (s === 2) return (b & c) | (b & d) | (c & d) - return b ^ c ^ d -} - -Sha.prototype._update = function (M) { - var W = this._w - - var a = this._a | 0 - var b = this._b | 0 - var c = this._c | 0 - var d = this._d | 0 - var e = this._e | 0 - - for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) - for (; i < 80; ++i) W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16] - - for (var j = 0; j < 80; ++j) { - var s = ~~(j / 20) - var t = (rotl5(a) + ft(s, b, c, d) + e + W[j] + K[s]) | 0 - - e = d - d = c - c = rotl30(b) - b = a - a = t - } - - this._a = (a + this._a) | 0 - this._b = (b + this._b) | 0 - this._c = (c + this._c) | 0 - this._d = (d + this._d) | 0 - this._e = (e + this._e) | 0 -} - -Sha.prototype._hash = function () { - var H = Buffer.allocUnsafe(20) - - H.writeInt32BE(this._a | 0, 0) - H.writeInt32BE(this._b | 0, 4) - H.writeInt32BE(this._c | 0, 8) - H.writeInt32BE(this._d | 0, 12) - H.writeInt32BE(this._e | 0, 16) - - return H -} - -module.exports = Sha - -},{"./hash":256,"inherits":146,"safe-buffer":250}],259:[function(require,module,exports){ -/* - * 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. - */ - -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc | 0, 0xca62c1d6 | 0 -] - -var W = new Array(80) - -function Sha1 () { - this.init() - this._w = W - - Hash.call(this, 64, 56) -} - -inherits(Sha1, Hash) - -Sha1.prototype.init = function () { - this._a = 0x67452301 - this._b = 0xefcdab89 - this._c = 0x98badcfe - this._d = 0x10325476 - this._e = 0xc3d2e1f0 - - return this -} - -function rotl1 (num) { - return (num << 1) | (num >>> 31) -} - -function rotl5 (num) { - return (num << 5) | (num >>> 27) -} - -function rotl30 (num) { - return (num << 30) | (num >>> 2) -} - -function ft (s, b, c, d) { - if (s === 0) return (b & c) | ((~b) & d) - if (s === 2) return (b & c) | (b & d) | (c & d) - return b ^ c ^ d -} - -Sha1.prototype._update = function (M) { - var W = this._w - - var a = this._a | 0 - var b = this._b | 0 - var c = this._c | 0 - var d = this._d | 0 - var e = this._e | 0 - - for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) - for (; i < 80; ++i) W[i] = rotl1(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]) - - for (var j = 0; j < 80; ++j) { - var s = ~~(j / 20) - var t = (rotl5(a) + ft(s, b, c, d) + e + W[j] + K[s]) | 0 - - e = d - d = c - c = rotl30(b) - b = a - a = t - } - - this._a = (a + this._a) | 0 - this._b = (b + this._b) | 0 - this._c = (c + this._c) | 0 - this._d = (d + this._d) | 0 - this._e = (e + this._e) | 0 -} - -Sha1.prototype._hash = function () { - var H = Buffer.allocUnsafe(20) - - H.writeInt32BE(this._a | 0, 0) - H.writeInt32BE(this._b | 0, 4) - H.writeInt32BE(this._c | 0, 8) - H.writeInt32BE(this._d | 0, 12) - H.writeInt32BE(this._e | 0, 16) - - return H -} - -module.exports = Sha1 - -},{"./hash":256,"inherits":146,"safe-buffer":250}],260:[function(require,module,exports){ -/** - * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined - * in FIPS 180-2 - * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * - */ - -var inherits = require('inherits') -var Sha256 = require('./sha256') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var W = new Array(64) - -function Sha224 () { - this.init() - - this._w = W // new Array(64) - - Hash.call(this, 64, 56) -} - -inherits(Sha224, Sha256) - -Sha224.prototype.init = function () { - this._a = 0xc1059ed8 - this._b = 0x367cd507 - this._c = 0x3070dd17 - this._d = 0xf70e5939 - this._e = 0xffc00b31 - this._f = 0x68581511 - this._g = 0x64f98fa7 - this._h = 0xbefa4fa4 - - return this -} - -Sha224.prototype._hash = function () { - var H = Buffer.allocUnsafe(28) - - H.writeInt32BE(this._a, 0) - H.writeInt32BE(this._b, 4) - H.writeInt32BE(this._c, 8) - H.writeInt32BE(this._d, 12) - H.writeInt32BE(this._e, 16) - H.writeInt32BE(this._f, 20) - H.writeInt32BE(this._g, 24) - - return H -} - -module.exports = Sha224 - -},{"./hash":256,"./sha256":261,"inherits":146,"safe-buffer":250}],261:[function(require,module,exports){ -/** - * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined - * in FIPS 180-2 - * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * - */ - -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, - 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, - 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, - 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, - 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, - 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, - 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, - 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, - 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, - 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, - 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, - 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, - 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, - 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, - 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, - 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 -] - -var W = new Array(64) - -function Sha256 () { - this.init() - - this._w = W // new Array(64) - - Hash.call(this, 64, 56) -} - -inherits(Sha256, Hash) - -Sha256.prototype.init = function () { - this._a = 0x6a09e667 - this._b = 0xbb67ae85 - this._c = 0x3c6ef372 - this._d = 0xa54ff53a - this._e = 0x510e527f - this._f = 0x9b05688c - this._g = 0x1f83d9ab - this._h = 0x5be0cd19 - - return this -} - -function ch (x, y, z) { - return z ^ (x & (y ^ z)) -} - -function maj (x, y, z) { - return (x & y) | (z & (x | y)) -} - -function sigma0 (x) { - return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10) -} - -function sigma1 (x) { - return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7) -} - -function gamma0 (x) { - return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3) -} - -function gamma1 (x) { - return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10) -} - -Sha256.prototype._update = function (M) { - var W = this._w - - var a = this._a | 0 - var b = this._b | 0 - var c = this._c | 0 - var d = this._d | 0 - var e = this._e | 0 - var f = this._f | 0 - var g = this._g | 0 - var h = this._h | 0 - - for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4) - for (; i < 64; ++i) W[i] = (gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]) | 0 - - for (var j = 0; j < 64; ++j) { - var T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0 - var T2 = (sigma0(a) + maj(a, b, c)) | 0 - - h = g - g = f - f = e - e = (d + T1) | 0 - d = c - c = b - b = a - a = (T1 + T2) | 0 - } - - this._a = (a + this._a) | 0 - this._b = (b + this._b) | 0 - this._c = (c + this._c) | 0 - this._d = (d + this._d) | 0 - this._e = (e + this._e) | 0 - this._f = (f + this._f) | 0 - this._g = (g + this._g) | 0 - this._h = (h + this._h) | 0 -} - -Sha256.prototype._hash = function () { - var H = Buffer.allocUnsafe(32) - - H.writeInt32BE(this._a, 0) - H.writeInt32BE(this._b, 4) - H.writeInt32BE(this._c, 8) - H.writeInt32BE(this._d, 12) - H.writeInt32BE(this._e, 16) - H.writeInt32BE(this._f, 20) - H.writeInt32BE(this._g, 24) - H.writeInt32BE(this._h, 28) - - return H -} - -module.exports = Sha256 - -},{"./hash":256,"inherits":146,"safe-buffer":250}],262:[function(require,module,exports){ -var inherits = require('inherits') -var SHA512 = require('./sha512') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var W = new Array(160) - -function Sha384 () { - this.init() - this._w = W - - Hash.call(this, 128, 112) -} - -inherits(Sha384, SHA512) - -Sha384.prototype.init = function () { - this._ah = 0xcbbb9d5d - this._bh = 0x629a292a - this._ch = 0x9159015a - this._dh = 0x152fecd8 - this._eh = 0x67332667 - this._fh = 0x8eb44a87 - this._gh = 0xdb0c2e0d - this._hh = 0x47b5481d - - this._al = 0xc1059ed8 - this._bl = 0x367cd507 - this._cl = 0x3070dd17 - this._dl = 0xf70e5939 - this._el = 0xffc00b31 - this._fl = 0x68581511 - this._gl = 0x64f98fa7 - this._hl = 0xbefa4fa4 - - return this -} - -Sha384.prototype._hash = function () { - var H = Buffer.allocUnsafe(48) - - function writeInt64BE (h, l, offset) { - H.writeInt32BE(h, offset) - H.writeInt32BE(l, offset + 4) - } - - writeInt64BE(this._ah, this._al, 0) - writeInt64BE(this._bh, this._bl, 8) - writeInt64BE(this._ch, this._cl, 16) - writeInt64BE(this._dh, this._dl, 24) - writeInt64BE(this._eh, this._el, 32) - writeInt64BE(this._fh, this._fl, 40) - - return H -} - -module.exports = Sha384 - -},{"./hash":256,"./sha512":263,"inherits":146,"safe-buffer":250}],263:[function(require,module,exports){ -var inherits = require('inherits') -var Hash = require('./hash') -var Buffer = require('safe-buffer').Buffer - -var K = [ - 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, - 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, - 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, - 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, - 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, - 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, - 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, - 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, - 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, - 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, - 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, - 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, - 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, - 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, - 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, - 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, - 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, - 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, - 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, - 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, - 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, - 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, - 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, - 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, - 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, - 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, - 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, - 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, - 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, - 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, - 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, - 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, - 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, - 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, - 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, - 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, - 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, - 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, - 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, - 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 -] - -var W = new Array(160) - -function Sha512 () { - this.init() - this._w = W - - Hash.call(this, 128, 112) -} - -inherits(Sha512, Hash) - -Sha512.prototype.init = function () { - this._ah = 0x6a09e667 - this._bh = 0xbb67ae85 - this._ch = 0x3c6ef372 - this._dh = 0xa54ff53a - this._eh = 0x510e527f - this._fh = 0x9b05688c - this._gh = 0x1f83d9ab - this._hh = 0x5be0cd19 - - this._al = 0xf3bcc908 - this._bl = 0x84caa73b - this._cl = 0xfe94f82b - this._dl = 0x5f1d36f1 - this._el = 0xade682d1 - this._fl = 0x2b3e6c1f - this._gl = 0xfb41bd6b - this._hl = 0x137e2179 - - return this -} - -function Ch (x, y, z) { - return z ^ (x & (y ^ z)) -} - -function maj (x, y, z) { - return (x & y) | (z & (x | y)) -} - -function sigma0 (x, xl) { - return (x >>> 28 | xl << 4) ^ (xl >>> 2 | x << 30) ^ (xl >>> 7 | x << 25) -} - -function sigma1 (x, xl) { - return (x >>> 14 | xl << 18) ^ (x >>> 18 | xl << 14) ^ (xl >>> 9 | x << 23) -} - -function Gamma0 (x, xl) { - return (x >>> 1 | xl << 31) ^ (x >>> 8 | xl << 24) ^ (x >>> 7) -} - -function Gamma0l (x, xl) { - return (x >>> 1 | xl << 31) ^ (x >>> 8 | xl << 24) ^ (x >>> 7 | xl << 25) -} - -function Gamma1 (x, xl) { - return (x >>> 19 | xl << 13) ^ (xl >>> 29 | x << 3) ^ (x >>> 6) -} - -function Gamma1l (x, xl) { - return (x >>> 19 | xl << 13) ^ (xl >>> 29 | x << 3) ^ (x >>> 6 | xl << 26) -} - -function getCarry (a, b) { - return (a >>> 0) < (b >>> 0) ? 1 : 0 -} - -Sha512.prototype._update = function (M) { - var W = this._w - - var ah = this._ah | 0 - var bh = this._bh | 0 - var ch = this._ch | 0 - var dh = this._dh | 0 - var eh = this._eh | 0 - var fh = this._fh | 0 - var gh = this._gh | 0 - var hh = this._hh | 0 - - var al = this._al | 0 - var bl = this._bl | 0 - var cl = this._cl | 0 - var dl = this._dl | 0 - var el = this._el | 0 - var fl = this._fl | 0 - var gl = this._gl | 0 - var hl = this._hl | 0 - - for (var i = 0; i < 32; i += 2) { - W[i] = M.readInt32BE(i * 4) - W[i + 1] = M.readInt32BE(i * 4 + 4) - } - for (; i < 160; i += 2) { - var xh = W[i - 15 * 2] - var xl = W[i - 15 * 2 + 1] - var gamma0 = Gamma0(xh, xl) - var gamma0l = Gamma0l(xl, xh) - - xh = W[i - 2 * 2] - xl = W[i - 2 * 2 + 1] - var gamma1 = Gamma1(xh, xl) - var gamma1l = Gamma1l(xl, xh) - - // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] - var Wi7h = W[i - 7 * 2] - var Wi7l = W[i - 7 * 2 + 1] - - var Wi16h = W[i - 16 * 2] - var Wi16l = W[i - 16 * 2 + 1] - - var Wil = (gamma0l + Wi7l) | 0 - var Wih = (gamma0 + Wi7h + getCarry(Wil, gamma0l)) | 0 - Wil = (Wil + gamma1l) | 0 - Wih = (Wih + gamma1 + getCarry(Wil, gamma1l)) | 0 - Wil = (Wil + Wi16l) | 0 - Wih = (Wih + Wi16h + getCarry(Wil, Wi16l)) | 0 - - W[i] = Wih - W[i + 1] = Wil - } - - for (var j = 0; j < 160; j += 2) { - Wih = W[j] - Wil = W[j + 1] - - var majh = maj(ah, bh, ch) - var majl = maj(al, bl, cl) - - var sigma0h = sigma0(ah, al) - var sigma0l = sigma0(al, ah) - var sigma1h = sigma1(eh, el) - var sigma1l = sigma1(el, eh) - - // t1 = h + sigma1 + ch + K[j] + W[j] - var Kih = K[j] - var Kil = K[j + 1] - - var chh = Ch(eh, fh, gh) - var chl = Ch(el, fl, gl) - - var t1l = (hl + sigma1l) | 0 - var t1h = (hh + sigma1h + getCarry(t1l, hl)) | 0 - t1l = (t1l + chl) | 0 - t1h = (t1h + chh + getCarry(t1l, chl)) | 0 - t1l = (t1l + Kil) | 0 - t1h = (t1h + Kih + getCarry(t1l, Kil)) | 0 - t1l = (t1l + Wil) | 0 - t1h = (t1h + Wih + getCarry(t1l, Wil)) | 0 - - // t2 = sigma0 + maj - var t2l = (sigma0l + majl) | 0 - var t2h = (sigma0h + majh + getCarry(t2l, sigma0l)) | 0 - - hh = gh - hl = gl - gh = fh - gl = fl - fh = eh - fl = el - el = (dl + t1l) | 0 - eh = (dh + t1h + getCarry(el, dl)) | 0 - dh = ch - dl = cl - ch = bh - cl = bl - bh = ah - bl = al - al = (t1l + t2l) | 0 - ah = (t1h + t2h + getCarry(al, t1l)) | 0 - } - - this._al = (this._al + al) | 0 - this._bl = (this._bl + bl) | 0 - this._cl = (this._cl + cl) | 0 - this._dl = (this._dl + dl) | 0 - this._el = (this._el + el) | 0 - this._fl = (this._fl + fl) | 0 - this._gl = (this._gl + gl) | 0 - this._hl = (this._hl + hl) | 0 - - this._ah = (this._ah + ah + getCarry(this._al, al)) | 0 - this._bh = (this._bh + bh + getCarry(this._bl, bl)) | 0 - this._ch = (this._ch + ch + getCarry(this._cl, cl)) | 0 - this._dh = (this._dh + dh + getCarry(this._dl, dl)) | 0 - this._eh = (this._eh + eh + getCarry(this._el, el)) | 0 - this._fh = (this._fh + fh + getCarry(this._fl, fl)) | 0 - this._gh = (this._gh + gh + getCarry(this._gl, gl)) | 0 - this._hh = (this._hh + hh + getCarry(this._hl, hl)) | 0 -} - -Sha512.prototype._hash = function () { - var H = Buffer.allocUnsafe(64) - - function writeInt64BE (h, l, offset) { - H.writeInt32BE(h, offset) - H.writeInt32BE(l, offset + 4) - } - - writeInt64BE(this._ah, this._al, 0) - writeInt64BE(this._bh, this._bl, 8) - writeInt64BE(this._ch, this._cl, 16) - writeInt64BE(this._dh, this._dl, 24) - writeInt64BE(this._eh, this._el, 32) - writeInt64BE(this._fh, this._fl, 40) - writeInt64BE(this._gh, this._gl, 48) - writeInt64BE(this._hh, this._hl, 56) - - return H -} - -module.exports = Sha512 - -},{"./hash":256,"inherits":146,"safe-buffer":250}],264:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -module.exports = Stream; - -var EE = require('events').EventEmitter; -var inherits = require('inherits'); - -inherits(Stream, EE); -Stream.Readable = require('readable-stream/lib/_stream_readable.js'); -Stream.Writable = require('readable-stream/lib/_stream_writable.js'); -Stream.Duplex = require('readable-stream/lib/_stream_duplex.js'); -Stream.Transform = require('readable-stream/lib/_stream_transform.js'); -Stream.PassThrough = require('readable-stream/lib/_stream_passthrough.js'); -Stream.finished = require('readable-stream/lib/internal/streams/end-of-stream.js') -Stream.pipeline = require('readable-stream/lib/internal/streams/pipeline.js') - -// Backwards-compat with node 0.4.x -Stream.Stream = Stream; - - - -// old-style streams. Note that the pipe method (the only relevant -// part of this class) is overridden in the Readable class. - -function Stream() { - EE.call(this); -} - -Stream.prototype.pipe = function(dest, options) { - var source = this; - - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); - } - } - } - - source.on('data', ondata); - - function ondrain() { - if (source.readable && source.resume) { - source.resume(); - } - } - - dest.on('drain', ondrain); - - // If the 'end' option is not supplied, dest.end() will be called when - // source gets the 'end' or 'close' events. Only dest.end() once. - if (!dest._isStdio && (!options || options.end !== false)) { - source.on('end', onend); - source.on('close', onclose); - } - - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - - dest.end(); - } - - - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - - if (typeof dest.destroy === 'function') dest.destroy(); - } - - // don't leave dangling pipes when there are errors. - function onerror(er) { - cleanup(); - if (EE.listenerCount(this, 'error') === 0) { - throw er; // Unhandled stream error in pipe. - } - } - - source.on('error', onerror); - dest.on('error', onerror); - - // remove all the event listeners that were added. - function cleanup() { - source.removeListener('data', ondata); - dest.removeListener('drain', ondrain); - - source.removeListener('end', onend); - source.removeListener('close', onclose); - - source.removeListener('error', onerror); - dest.removeListener('error', onerror); - - source.removeListener('end', cleanup); - source.removeListener('close', cleanup); - - dest.removeListener('close', cleanup); - } - - source.on('end', cleanup); - source.on('close', cleanup); - - dest.on('close', cleanup); - - dest.emit('pipe', source); - - // Allow for unix-like usage: A.pipe(B).pipe(C) - return dest; -}; - -},{"events":105,"inherits":146,"readable-stream/lib/_stream_duplex.js":266,"readable-stream/lib/_stream_passthrough.js":267,"readable-stream/lib/_stream_readable.js":268,"readable-stream/lib/_stream_transform.js":269,"readable-stream/lib/_stream_writable.js":270,"readable-stream/lib/internal/streams/end-of-stream.js":274,"readable-stream/lib/internal/streams/pipeline.js":276}],265:[function(require,module,exports){ -arguments[4][50][0].apply(exports,arguments) -},{"dup":50}],266:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. -'use strict'; -/**/ - -var objectKeys = Object.keys || function (obj) { - var keys = []; - - for (var key in obj) { - keys.push(key); - } - - return keys; -}; -/**/ - - -module.exports = Duplex; - -var Readable = require('./_stream_readable'); - -var Writable = require('./_stream_writable'); - -require('inherits')(Duplex, Readable); - -{ - // Allow the keys array to be GC'ed. - var keys = objectKeys(Writable.prototype); - - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } -} - -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - Readable.call(this, options); - Writable.call(this, options); - this.allowHalfOpen = true; - - if (options) { - if (options.readable === false) this.readable = false; - if (options.writable === false) this.writable = false; - - if (options.allowHalfOpen === false) { - this.allowHalfOpen = false; - this.once('end', onend); - } - } -} - -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); -Object.defineProperty(Duplex.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); -Object.defineProperty(Duplex.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); // the no-half-open enforcer - -function onend() { - // If the writable side ended, then we're ok. - if (this._writableState.ended) return; // no more data can be written. - // But allow more writes to happen in this tick. - - process.nextTick(onEndNT, this); -} - -function onEndNT(self) { - self.end(); -} - -Object.defineProperty(Duplex.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined || this._writableState === undefined) { - return false; - } - - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); -}).call(this)}).call(this,require('_process')) - -},{"./_stream_readable":268,"./_stream_writable":270,"_process":237,"inherits":146}],267:[function(require,module,exports){ -arguments[4][52][0].apply(exports,arguments) -},{"./_stream_transform":269,"dup":52,"inherits":146}],268:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -'use strict'; - -module.exports = Readable; -/**/ - -var Duplex; -/**/ - -Readable.ReadableState = ReadableState; -/**/ - -var EE = require('events').EventEmitter; - -var EElistenerCount = function EElistenerCount(emitter, type) { - return emitter.listeners(type).length; -}; -/**/ - -/**/ - - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} -/**/ - - -var debugUtil = require('util'); - -var debug; - -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function debug() {}; -} -/**/ - - -var BufferList = require('./internal/streams/buffer_list'); - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance. - - -var StringDecoder; -var createReadableStreamAsyncIterator; -var from; - -require('inherits')(Readable, Stream); - -var errorOrDestroy = destroyImpl.errorOrDestroy; -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} - -function ReadableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - - this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - - this.sync = true; // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - this.paused = true; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish') - - this.autoDestroy = !!options.autoDestroy; // has it been destroyed - - this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s - - this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled - - this.readingMore = false; - this.decoder = null; - this.encoding = null; - - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} - -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside - // the ReadableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - this._readableState = new ReadableState(options, this, isDuplex); // legacy - - this.readable = true; - - if (options) { - if (typeof options.read === 'function') this._read = options.read; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - - Stream.call(this); -} - -Object.defineProperty(Readable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined) { - return false; - } - - return this._readableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - } -}); -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; - -Readable.prototype._destroy = function (err, cb) { - cb(err); -}; // Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. - - -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - - skipChunkCheck = true; - } - } else { - skipChunkCheck = true; - } - - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; // Unshift should *always* be something directly out of read() - - -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); -}; - -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - debug('readableAddChunk', chunk); - var state = stream._readableState; - - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - - if (er) { - errorOrDestroy(stream, er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (addToFront) { - if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed) { - return false; - } else { - state.reading = false; - - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); - } - } // We can push more data if we are below the highWaterMark. - // Also, if we have no data yet, we can stand some more bytes. - // This is to work around cases where hwm=0, such as the repl. - - - return !state.ended && (state.length < state.highWaterMark || state.length === 0); -} - -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - state.awaitDrain = 0; - stream.emit('data', chunk); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - if (state.needReadable) emitReadable(stream); - } - - maybeReadMore(stream, state); -} - -function chunkInvalid(state, chunk) { - var er; - - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); - } - - return er; -} - -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; // backwards compatibility. - - -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - var decoder = new StringDecoder(enc); - this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8 - - this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers: - - var p = this._readableState.buffer.head; - var content = ''; - - while (p !== null) { - content += decoder.write(p.data); - p = p.next; - } - - this._readableState.buffer.clear(); - - if (content !== '') this._readableState.buffer.push(content); - this._readableState.length = content.length; - return this; -}; // Don't raise the hwm > 1GB - - -var MAX_HWM = 0x40000000; - -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - - return n; -} // This function is designed to be inlinable, so please take care when making -// changes to the function body. - - -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } // If we're asking for more than the current hwm, then raise the hwm. - - - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; // Don't have enough - - if (!state.ended) { - state.needReadable = true; - return 0; - } - - return state.length; -} // you can override either this method, or the async _read(n) below. - - -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - - if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - - n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. - - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - // if we need a readable event, then we need to do some reading. - - - var doRead = state.needReadable; - debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some - - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - - - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; // if the length is currently zero, then we *need* a readable event. - - if (state.length === 0) state.needReadable = true; // call internal read method - - this._read(state.highWaterMark); - - state.sync = false; // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - - if (!state.reading) n = howMuchToRead(nOrig, state); - } - - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; - - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n; - state.awaitDrain = 0; - } - - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. - - if (nOrig !== n && state.ended) endReadable(this); - } - - if (ret !== null) this.emit('data', ret); - return ret; -}; - -function onEofChunk(stream, state) { - debug('onEofChunk'); - if (state.ended) return; - - if (state.decoder) { - var chunk = state.decoder.end(); - - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - - state.ended = true; - - if (state.sync) { - // if we are sync, wait until next tick to emit the data. - // Otherwise we risk emitting data in the flow() - // the readable code triggers during a read() call - emitReadable(stream); - } else { - // emit 'readable' now to make sure it gets picked up. - state.needReadable = false; - - if (!state.emittedReadable) { - state.emittedReadable = true; - emitReadable_(stream); - } - } -} // Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. - - -function emitReadable(stream) { - var state = stream._readableState; - debug('emitReadable', state.needReadable, state.emittedReadable); - state.needReadable = false; - - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - process.nextTick(emitReadable_, stream); - } -} - -function emitReadable_(stream) { - var state = stream._readableState; - debug('emitReadable_', state.destroyed, state.length, state.ended); - - if (!state.destroyed && (state.length || state.ended)) { - stream.emit('readable'); - state.emittedReadable = false; - } // The stream needs another readable event if - // 1. It is not flowing, as the flow mechanism will take - // care of it. - // 2. It is not ended. - // 3. It is below the highWaterMark, so we can schedule - // another readable later. - - - state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; - flow(stream); -} // at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. - - -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - process.nextTick(maybeReadMore_, stream, state); - } -} - -function maybeReadMore_(stream, state) { - // Attempt to read more data if we should. - // - // The conditions for reading more data are (one of): - // - Not enough data buffered (state.length < state.highWaterMark). The loop - // is responsible for filling the buffer with enough data if such data - // is available. If highWaterMark is 0 and we are not in the flowing mode - // we should _not_ attempt to buffer any extra data. We'll get more data - // when the stream consumer calls read() instead. - // - No data in the buffer, and the stream is in flowing mode. In this mode - // the loop below is responsible for ensuring read() is called. Failing to - // call read here would abort the flow and there's no other mechanism for - // continuing the flow if the stream consumer has just subscribed to the - // 'data' event. - // - // In addition to the above conditions to keep reading data, the following - // conditions prevent the data from being read: - // - The stream has ended (state.ended). - // - There is already a pending 'read' operation (state.reading). This is a - // case where the the stream has called the implementation defined _read() - // method, but they are processing the call asynchronously and have _not_ - // called push() with new data. In this case we skip performing more - // read()s. The execution ends in this method again after the _read() ends - // up calling push() with more data. - while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { - var len = state.length; - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) // didn't get any data, stop spinning. - break; - } - - state.readingMore = false; -} // abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. - - -Readable.prototype._read = function (n) { - errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); -}; - -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - - case 1: - state.pipes = [state.pipes, dest]; - break; - - default: - state.pipes.push(dest); - break; - } - - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); - dest.on('unpipe', onunpipe); - - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - debug('onend'); - dest.end(); - } // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - - - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - var cleanedUp = false; - - function cleanup() { - debug('cleanup'); // cleanup event handlers once the pipe is broken - - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - cleanedUp = true; // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - - src.on('data', ondata); - - function ondata(chunk) { - debug('ondata'); - var ret = dest.write(chunk); - debug('dest.write', ret); - - if (ret === false) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', state.awaitDrain); - state.awaitDrain++; - } - - src.pause(); - } - } // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - - - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); - } // Make sure our error handler is attached before userland ones. - - - prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. - - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - - dest.once('close', onclose); - - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - - dest.once('finish', onfinish); - - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } // tell the dest that it's being piped to - - - dest.emit('pipe', src); // start the flow if it hasn't been started already. - - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } - - return dest; -}; - -function pipeOnDrain(src) { - return function pipeOnDrainFunctionResult() { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); - } - }; -} - -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { - hasUnpiped: false - }; // if we're not piping anywhere, then do nothing. - - if (state.pipesCount === 0) return this; // just one destination. most common case. - - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - if (!dest) dest = state.pipes; // got a match. - - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } // slow case. multiple pipe destinations. - - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, { - hasUnpiped: false - }); - } - - return this; - } // try to find the right one. - - - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - dest.emit('unpipe', this, unpipeInfo); - return this; -}; // set up data events if they are asked for -// Ensure readable listeners eventually get something - - -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - var state = this._readableState; - - if (ev === 'data') { - // update readableListening so that resume() may be a no-op - // a few lines down. This is needed to support once('readable'). - state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused - - if (state.flowing !== false) this.resume(); - } else if (ev === 'readable') { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - debug('on readable', state.length, state.reading); - - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - process.nextTick(nReadingNextTick, this); - } - } - } - - return res; -}; - -Readable.prototype.addListener = Readable.prototype.on; - -Readable.prototype.removeListener = function (ev, fn) { - var res = Stream.prototype.removeListener.call(this, ev, fn); - - if (ev === 'readable') { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; -}; - -Readable.prototype.removeAllListeners = function (ev) { - var res = Stream.prototype.removeAllListeners.apply(this, arguments); - - if (ev === 'readable' || ev === undefined) { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; -}; - -function updateReadableListening(self) { - var state = self._readableState; - state.readableListening = self.listenerCount('readable') > 0; - - if (state.resumeScheduled && !state.paused) { - // flowing needs to be set to true now, otherwise - // the upcoming resume will not flow. - state.flowing = true; // crude way to check if we should resume - } else if (self.listenerCount('data') > 0) { - self.resume(); - } -} - -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} // pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. - - -Readable.prototype.resume = function () { - var state = this._readableState; - - if (!state.flowing) { - debug('resume'); // we flow only if there is no one listening - // for readable, but we still have to call - // resume() - - state.flowing = !state.readableListening; - resume(this, state); - } - - state.paused = false; - return this; -}; - -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - process.nextTick(resume_, stream, state); - } -} - -function resume_(stream, state) { - debug('resume', state.reading); - - if (!state.reading) { - stream.read(0); - } - - state.resumeScheduled = false; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); -} - -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - - if (this._readableState.flowing !== false) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - - this._readableState.paused = true; - return this; -}; - -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - - while (state.flowing && stream.read() !== null) { - ; - } -} // wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. - - -Readable.prototype.wrap = function (stream) { - var _this = this; - - var state = this._readableState; - var paused = false; - stream.on('end', function () { - debug('wrapped end'); - - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); - } - - _this.push(null); - }); - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode - - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - - var ret = _this.push(chunk); - - if (!ret) { - paused = true; - stream.pause(); - } - }); // proxy all the other methods. - // important when wrapping filters and duplexes. - - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function methodWrap(method) { - return function methodWrapReturnFunction() { - return stream[method].apply(stream, arguments); - }; - }(i); - } - } // proxy certain important events. - - - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } // when we try to consume some more bytes, simply unpause the - // underlying stream. - - - this._read = function (n) { - debug('wrapped _read', n); - - if (paused) { - paused = false; - stream.resume(); - } - }; - - return this; -}; - -if (typeof Symbol === 'function') { - Readable.prototype[Symbol.asyncIterator] = function () { - if (createReadableStreamAsyncIterator === undefined) { - createReadableStreamAsyncIterator = require('./internal/streams/async_iterator'); - } - - return createReadableStreamAsyncIterator(this); - }; -} - -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.highWaterMark; - } -}); -Object.defineProperty(Readable.prototype, 'readableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState && this._readableState.buffer; - } -}); -Object.defineProperty(Readable.prototype, 'readableFlowing', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.flowing; - }, - set: function set(state) { - if (this._readableState) { - this._readableState.flowing = state; - } - } -}); // exposed for testing purposes only. - -Readable._fromList = fromList; -Object.defineProperty(Readable.prototype, 'readableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.length; - } -}); // Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. - -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = state.buffer.consume(n, state.decoder); - } - return ret; -} - -function endReadable(stream) { - var state = stream._readableState; - debug('endReadable', state.endEmitted); - - if (!state.endEmitted) { - state.ended = true; - process.nextTick(endReadableNT, state, stream); - } -} - -function endReadableNT(state, stream) { - debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift. - - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the writable side is ready for autoDestroy as well - var wState = stream._writableState; - - if (!wState || wState.autoDestroy && wState.finished) { - stream.destroy(); - } - } - } -} - -if (typeof Symbol === 'function') { - Readable.from = function (iterable, opts) { - if (from === undefined) { - from = require('./internal/streams/from'); - } - - return from(Readable, iterable, opts); - }; -} - -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - - return -1; -} -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../errors":265,"./_stream_duplex":266,"./internal/streams/async_iterator":271,"./internal/streams/buffer_list":272,"./internal/streams/destroy":273,"./internal/streams/from":275,"./internal/streams/state":277,"./internal/streams/stream":278,"_process":237,"buffer":68,"events":105,"inherits":146,"string_decoder/":279,"util":20}],269:[function(require,module,exports){ -arguments[4][54][0].apply(exports,arguments) -},{"../errors":265,"./_stream_duplex":266,"dup":54,"inherits":146}],270:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. -'use strict'; - -module.exports = Writable; -/* */ - -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} // It seems a linked list but it is not -// there will be only 2 of these for each stream - - -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; - - this.finish = function () { - onCorkedFinish(_this, state); - }; -} -/* */ - -/**/ - - -var Duplex; -/**/ - -Writable.WritableState = WritableState; -/**/ - -var internalUtil = { - deprecate: require('util-deprecate') -}; -/**/ - -/**/ - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, - ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, - ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, - ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; - -var errorOrDestroy = destroyImpl.errorOrDestroy; - -require('inherits')(Writable, Stream); - -function nop() {} - -function WritableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream, - // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream - // contains buffers or objects. - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - - this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called - - this.finalCalled = false; // drain event flag. - - this.needDrain = false; // at the start of calling end() - - this.ending = false; // when end() has been called, and returned - - this.ended = false; // when 'finish' is emitted - - this.finished = false; // has it been destroyed - - this.destroyed = false; // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. - - this.length = 0; // a flag to see when we're in the middle of a write. - - this.writing = false; // when true all writes will be buffered until .uncork() call - - this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. - - this.sync = true; // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. - - this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) - - this.onwrite = function (er) { - onwrite(stream, er); - }; // the callback that the user supplies to write(chunk,encoding,cb) - - - this.writecb = null; // the amount that is being written when _write is called. - - this.writelen = 0; - this.bufferedRequest = null; - this.lastBufferedRequest = null; // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted - - this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams - - this.prefinished = false; // True if the error was already emitted and should not be thrown again - - this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end') - - this.autoDestroy = !!options.autoDestroy; // count buffered requests - - this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two - - this.corkedRequestsFree = new CorkedRequest(this); -} - -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; - - while (current) { - out.push(current); - current = current.next; - } - - return out; -}; - -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function writableStateBufferGetter() { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); // Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. - - -var realHasInstance; - -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function value(object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function realHasInstance(object) { - return object instanceof this; - }; -} - -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - // Checking for a Stream.Duplex instance is faster here instead of inside - // the WritableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); - this._writableState = new WritableState(options, this, isDuplex); // legacy. - - this.writable = true; - - if (options) { - if (typeof options.write === 'function') this._write = options.write; - if (typeof options.writev === 'function') this._writev = options.writev; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - if (typeof options.final === 'function') this._final = options.final; - } - - Stream.call(this); -} // Otherwise people can pipe Writable streams, which is just wrong. - - -Writable.prototype.pipe = function () { - errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); -}; - -function writeAfterEnd(stream, cb) { - var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb - - errorOrDestroy(stream, er); - process.nextTick(cb, er); -} // Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. - - -function validChunk(stream, state, chunk, cb) { - var er; - - if (chunk === null) { - er = new ERR_STREAM_NULL_VALUES(); - } else if (typeof chunk !== 'string' && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); - } - - if (er) { - errorOrDestroy(stream, er); - process.nextTick(cb, er); - return false; - } - - return true; -} - -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; - - var isBuf = !state.objectMode && _isUint8Array(chunk); - - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - if (typeof cb !== 'function') cb = nop; - if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - return ret; -}; - -Writable.prototype.cork = function () { - this._writableState.corked++; -}; - -Writable.prototype.uncork = function () { - var state = this._writableState; - - if (state.corked) { - state.corked--; - if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); - } -}; - -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; - -Object.defineProperty(Writable.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); - -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } - - return chunk; -} - -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); // if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. - -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); - - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } - } - - var len = state.objectMode ? 1 : chunk.length; - state.length += len; - var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. - - if (!ret) state.needDrain = true; - - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; - - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; - } - - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); - } - - return ret; -} - -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; -} - -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; - - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - process.nextTick(cb, er); // this can emit finish, and it will always happen - // after error - - process.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); // this can emit finish, but finish must - // always follow error - - finishMaybe(stream, state); - } -} - -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; -} - -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); - onwriteStateUpdate(state); - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state) || stream.destroyed; - - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } - - if (sync) { - process.nextTick(afterWrite, stream, state, finished, cb); - } else { - afterWrite(stream, state, finished, cb); - } - } -} - -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} // Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. - - -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); - } -} // if there's something in the buffer waiting, then process it - - -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; - - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - var count = 0; - var allBuffers = true; - - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } - - buffer.allBuffers = allBuffers; - doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite - - state.pendingcb++; - state.lastBufferedRequest = null; - - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } - - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. - - if (state.writing) { - break; - } - } - - if (entry === null) state.lastBufferedRequest = null; - } - - state.bufferedRequest = entry; - state.bufferProcessing = false; -} - -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); -}; - -Writable.prototype._writev = null; - -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; - - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } - - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks - - if (state.corked) { - state.corked = 1; - this.uncork(); - } // ignore unnecessary end() calls. - - - if (!state.ending) endWritable(this, state, cb); - return this; -}; - -Object.defineProperty(Writable.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); - -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} - -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; - - if (err) { - errorOrDestroy(stream, err); - } - - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} - -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function' && !state.destroyed) { - state.pendingcb++; - state.finalCalled = true; - process.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); - } - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - - if (need) { - prefinish(stream, state); - - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the readable side is ready for autoDestroy as well - var rState = stream._readableState; - - if (!rState || rState.autoDestroy && rState.endEmitted) { - stream.destroy(); - } - } - } - } - - return need; -} - -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); - - if (cb) { - if (state.finished) process.nextTick(cb);else stream.once('finish', cb); - } - - state.ended = true; - stream.writable = false; -} - -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; - - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } // reuse the free corkReq. - - - state.corkedRequestsFree.next = corkReq; -} - -Object.defineProperty(Writable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._writableState === undefined) { - return false; - } - - return this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._writableState.destroyed = value; - } -}); -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; - -Writable.prototype._destroy = function (err, cb) { - cb(err); -}; -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../errors":265,"./_stream_duplex":266,"./internal/streams/destroy":273,"./internal/streams/state":277,"./internal/streams/stream":278,"_process":237,"buffer":68,"inherits":146,"util-deprecate":283}],271:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; - -var _Object$setPrototypeO; - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -var finished = require('./end-of-stream'); - -var kLastResolve = Symbol('lastResolve'); -var kLastReject = Symbol('lastReject'); -var kError = Symbol('error'); -var kEnded = Symbol('ended'); -var kLastPromise = Symbol('lastPromise'); -var kHandlePromise = Symbol('handlePromise'); -var kStream = Symbol('stream'); - -function createIterResult(value, done) { - return { - value: value, - done: done - }; -} - -function readAndResolve(iter) { - var resolve = iter[kLastResolve]; - - if (resolve !== null) { - var data = iter[kStream].read(); // we defer if data is null - // we can be expecting either 'end' or - // 'error' - - if (data !== null) { - iter[kLastPromise] = null; - iter[kLastResolve] = null; - iter[kLastReject] = null; - resolve(createIterResult(data, false)); - } - } -} - -function onReadable(iter) { - // we wait for the next tick, because it might - // emit an error with process.nextTick - process.nextTick(readAndResolve, iter); -} - -function wrapForNext(lastPromise, iter) { - return function (resolve, reject) { - lastPromise.then(function () { - if (iter[kEnded]) { - resolve(createIterResult(undefined, true)); - return; - } - - iter[kHandlePromise](resolve, reject); - }, reject); - }; -} - -var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); -var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { - get stream() { - return this[kStream]; - }, - - next: function next() { - var _this = this; - - // if we have detected an error in the meanwhile - // reject straight away - var error = this[kError]; - - if (error !== null) { - return Promise.reject(error); - } - - if (this[kEnded]) { - return Promise.resolve(createIterResult(undefined, true)); - } - - if (this[kStream].destroyed) { - // We need to defer via nextTick because if .destroy(err) is - // called, the error will be emitted via nextTick, and - // we cannot guarantee that there is no error lingering around - // waiting to be emitted. - return new Promise(function (resolve, reject) { - process.nextTick(function () { - if (_this[kError]) { - reject(_this[kError]); - } else { - resolve(createIterResult(undefined, true)); - } - }); - }); - } // if we have multiple next() calls - // we will wait for the previous Promise to finish - // this logic is optimized to support for await loops, - // where next() is only called once at a time - - - var lastPromise = this[kLastPromise]; - var promise; - - if (lastPromise) { - promise = new Promise(wrapForNext(lastPromise, this)); - } else { - // fast path needed to support multiple this.push() - // without triggering the next() queue - var data = this[kStream].read(); - - if (data !== null) { - return Promise.resolve(createIterResult(data, false)); - } - - promise = new Promise(this[kHandlePromise]); - } - - this[kLastPromise] = promise; - return promise; - } -}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { - return this; -}), _defineProperty(_Object$setPrototypeO, "return", function _return() { - var _this2 = this; - - // destroy(err, cb) is a private API - // we can guarantee we have that here, because we control the - // Readable class this is attached to - return new Promise(function (resolve, reject) { - _this2[kStream].destroy(null, function (err) { - if (err) { - reject(err); - return; - } - - resolve(createIterResult(undefined, true)); - }); - }); -}), _Object$setPrototypeO), AsyncIteratorPrototype); - -var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { - var _Object$create; - - var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { - value: stream, - writable: true - }), _defineProperty(_Object$create, kLastResolve, { - value: null, - writable: true - }), _defineProperty(_Object$create, kLastReject, { - value: null, - writable: true - }), _defineProperty(_Object$create, kError, { - value: null, - writable: true - }), _defineProperty(_Object$create, kEnded, { - value: stream._readableState.endEmitted, - writable: true - }), _defineProperty(_Object$create, kHandlePromise, { - value: function value(resolve, reject) { - var data = iterator[kStream].read(); - - if (data) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(data, false)); - } else { - iterator[kLastResolve] = resolve; - iterator[kLastReject] = reject; - } - }, - writable: true - }), _Object$create)); - iterator[kLastPromise] = null; - finished(stream, function (err) { - if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { - var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise - // returned by next() and store the error - - if (reject !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - reject(err); - } - - iterator[kError] = err; - return; - } - - var resolve = iterator[kLastResolve]; - - if (resolve !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(undefined, true)); - } - - iterator[kEnded] = true; - }); - stream.on('readable', onReadable.bind(null, iterator)); - return iterator; -}; - -module.exports = createReadableStreamAsyncIterator; -}).call(this)}).call(this,require('_process')) - -},{"./end-of-stream":274,"_process":237}],272:[function(require,module,exports){ -arguments[4][57][0].apply(exports,arguments) -},{"buffer":68,"dup":57,"util":20}],273:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; // undocumented cb() API, needed for core, not for public API - -function destroy(err, cb) { - var _this = this; - - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; - - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err) { - if (!this._writableState) { - process.nextTick(emitErrorNT, this, err); - } else if (!this._writableState.errorEmitted) { - this._writableState.errorEmitted = true; - process.nextTick(emitErrorNT, this, err); - } - } - - return this; - } // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks - - - if (this._readableState) { - this._readableState.destroyed = true; - } // if this is a duplex stream mark the writable part as destroyed as well - - - if (this._writableState) { - this._writableState.destroyed = true; - } - - this._destroy(err || null, function (err) { - if (!cb && err) { - if (!_this._writableState) { - process.nextTick(emitErrorAndCloseNT, _this, err); - } else if (!_this._writableState.errorEmitted) { - _this._writableState.errorEmitted = true; - process.nextTick(emitErrorAndCloseNT, _this, err); - } else { - process.nextTick(emitCloseNT, _this); - } - } else if (cb) { - process.nextTick(emitCloseNT, _this); - cb(err); - } else { - process.nextTick(emitCloseNT, _this); - } - }); - - return this; -} - -function emitErrorAndCloseNT(self, err) { - emitErrorNT(self, err); - emitCloseNT(self); -} - -function emitCloseNT(self) { - if (self._writableState && !self._writableState.emitClose) return; - if (self._readableState && !self._readableState.emitClose) return; - self.emit('close'); -} - -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; - } - - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finalCalled = false; - this._writableState.prefinished = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; - } -} - -function emitErrorNT(self, err) { - self.emit('error', err); -} - -function errorOrDestroy(stream, err) { - // We have tests that rely on errors being emitted - // in the same tick, so changing this is semver major. - // For now when you opt-in to autoDestroy we allow - // the error to be emitted nextTick. In a future - // semver major update we should change the default to this. - var rState = stream._readableState; - var wState = stream._writableState; - if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); -} - -module.exports = { - destroy: destroy, - undestroy: undestroy, - errorOrDestroy: errorOrDestroy -}; -}).call(this)}).call(this,require('_process')) - -},{"_process":237}],274:[function(require,module,exports){ -arguments[4][59][0].apply(exports,arguments) -},{"../../../errors":265,"dup":59}],275:[function(require,module,exports){ -arguments[4][60][0].apply(exports,arguments) -},{"dup":60}],276:[function(require,module,exports){ -arguments[4][61][0].apply(exports,arguments) -},{"../../../errors":265,"./end-of-stream":274,"dup":61}],277:[function(require,module,exports){ -arguments[4][62][0].apply(exports,arguments) -},{"../../../errors":265,"dup":62}],278:[function(require,module,exports){ -arguments[4][63][0].apply(exports,arguments) -},{"dup":63,"events":105}],279:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -/**/ - -var Buffer = require('safe-buffer').Buffer; -/**/ - -var isEncoding = Buffer.isEncoding || function (encoding) { - encoding = '' + encoding; - switch (encoding && encoding.toLowerCase()) { - case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': - return true; - default: - return false; - } -}; - -function _normalizeEncoding(enc) { - if (!enc) return 'utf8'; - var retried; - while (true) { - switch (enc) { - case 'utf8': - case 'utf-8': - return 'utf8'; - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return 'utf16le'; - case 'latin1': - case 'binary': - return 'latin1'; - case 'base64': - case 'ascii': - case 'hex': - return enc; - default: - if (retried) return; // undefined - enc = ('' + enc).toLowerCase(); - retried = true; - } - } -}; - -// Do not cache `Buffer.isEncoding` when checking encoding names as some -// modules monkey-patch it to support additional encodings -function normalizeEncoding(enc) { - var nenc = _normalizeEncoding(enc); - if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); - return nenc || enc; -} - -// StringDecoder provides an interface for efficiently splitting a series of -// buffers into a series of JS strings without breaking apart multi-byte -// characters. -exports.StringDecoder = StringDecoder; -function StringDecoder(encoding) { - this.encoding = normalizeEncoding(encoding); - var nb; - switch (this.encoding) { - case 'utf16le': - this.text = utf16Text; - this.end = utf16End; - nb = 4; - break; - case 'utf8': - this.fillLast = utf8FillLast; - nb = 4; - break; - case 'base64': - this.text = base64Text; - this.end = base64End; - nb = 3; - break; - default: - this.write = simpleWrite; - this.end = simpleEnd; - return; - } - this.lastNeed = 0; - this.lastTotal = 0; - this.lastChar = Buffer.allocUnsafe(nb); -} - -StringDecoder.prototype.write = function (buf) { - if (buf.length === 0) return ''; - var r; - var i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ''; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ''; -}; - -StringDecoder.prototype.end = utf8End; - -// Returns only complete characters in a Buffer -StringDecoder.prototype.text = utf8Text; - -// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer -StringDecoder.prototype.fillLast = function (buf) { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -}; - -// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a -// continuation byte. If an invalid byte is detected, -2 is returned. -function utf8CheckByte(byte) { - if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; - return byte >> 6 === 0x02 ? -1 : -2; -} - -// Checks at most 3 bytes at the end of a Buffer in order to detect an -// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) -// needed to complete the UTF-8 character (if applicable) are returned. -function utf8CheckIncomplete(self, buf, i) { - var j = buf.length - 1; - if (j < i) return 0; - var nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0;else self.lastNeed = nb - 3; - } - return nb; - } - return 0; -} - -// Validates as many continuation bytes for a multi-byte UTF-8 character as -// needed or are available. If we see a non-continuation byte where we expect -// one, we "replace" the validated continuation bytes we've seen so far with -// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding -// behavior. The continuation byte check is included three times in the case -// where all of the continuation bytes for a character exist in the same buffer. -// It is also done this way as a slight performance increase instead of using a -// loop. -function utf8CheckExtraBytes(self, buf, p) { - if ((buf[0] & 0xC0) !== 0x80) { - self.lastNeed = 0; - return '\ufffd'; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xC0) !== 0x80) { - self.lastNeed = 1; - return '\ufffd'; - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xC0) !== 0x80) { - self.lastNeed = 2; - return '\ufffd'; - } - } - } -} - -// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. -function utf8FillLast(buf) { - var p = this.lastTotal - this.lastNeed; - var r = utf8CheckExtraBytes(this, buf, p); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a -// partial character, the character's bytes are buffered until the required -// number of bytes are available. -function utf8Text(buf, i) { - var total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString('utf8', i); - this.lastTotal = total; - var end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString('utf8', i, end); -} - -// For UTF-8, a replacement character is added when ending on a partial -// character. -function utf8End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + '\ufffd'; - return r; -} - -// UTF-16LE typically needs two bytes per character, but even if we have an even -// number of bytes available, we need to check if we end on a leading/high -// surrogate. In that case, we need to wait for the next two bytes in order to -// decode the last character properly. -function utf16Text(buf, i) { - if ((buf.length - i) % 2 === 0) { - var r = buf.toString('utf16le', i); - if (r) { - var c = r.charCodeAt(r.length - 1); - if (c >= 0xD800 && c <= 0xDBFF) { - this.lastNeed = 2; - this.lastTotal = 4; - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - return r.slice(0, -1); - } - } - return r; - } - this.lastNeed = 1; - this.lastTotal = 2; - this.lastChar[0] = buf[buf.length - 1]; - return buf.toString('utf16le', i, buf.length - 1); -} - -// For UTF-16LE we do not explicitly append special replacement characters if we -// end on a partial character, we simply let v8 handle that. -function utf16End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) { - var end = this.lastTotal - this.lastNeed; - return r + this.lastChar.toString('utf16le', 0, end); - } - return r; -} - -function base64Text(buf, i) { - var n = (buf.length - i) % 3; - if (n === 0) return buf.toString('base64', i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString('base64', i, buf.length - n); -} - -function base64End(buf) { - var r = buf && buf.length ? this.write(buf) : ''; - if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); - return r; -} - -// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) -function simpleWrite(buf) { - return buf.toString(this.encoding); -} - -function simpleEnd(buf) { - return buf && buf.length ? this.write(buf) : ''; -} -},{"safe-buffer":250}],280:[function(require,module,exports){ -(function (setImmediate,clearImmediate){(function (){ -var nextTick = require('process/browser.js').nextTick; -var apply = Function.prototype.apply; -var slice = Array.prototype.slice; -var immediateIds = {}; -var nextImmediateId = 0; - -// DOM APIs, for completeness - -exports.setTimeout = function() { - return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); -}; -exports.setInterval = function() { - return new Timeout(apply.call(setInterval, window, arguments), clearInterval); -}; -exports.clearTimeout = -exports.clearInterval = function(timeout) { timeout.close(); }; - -function Timeout(id, clearFn) { - this._id = id; - this._clearFn = clearFn; -} -Timeout.prototype.unref = Timeout.prototype.ref = function() {}; -Timeout.prototype.close = function() { - this._clearFn.call(window, this._id); -}; - -// Does not start the time, just sets up the members needed. -exports.enroll = function(item, msecs) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = msecs; -}; - -exports.unenroll = function(item) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = -1; -}; - -exports._unrefActive = exports.active = function(item) { - clearTimeout(item._idleTimeoutId); - - var msecs = item._idleTimeout; - if (msecs >= 0) { - item._idleTimeoutId = setTimeout(function onTimeout() { - if (item._onTimeout) - item._onTimeout(); - }, msecs); - } -}; - -// That's not how node.js implements it but the exposed api is the same. -exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { - var id = nextImmediateId++; - var args = arguments.length < 2 ? false : slice.call(arguments, 1); - - immediateIds[id] = true; - - nextTick(function onNextTick() { - if (immediateIds[id]) { - // fn.call() is faster so we optimize for the common use-case - // @see http://jsperf.com/call-apply-segu - if (args) { - fn.apply(null, args); - } else { - fn.call(null); - } - // Prevent ids from leaking - exports.clearImmediate(id); - } - }); - - return id; -}; - -exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { - delete immediateIds[id]; -}; -}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate) - -},{"process/browser.js":237,"timers":280}],281:[function(require,module,exports){ -module.exports={ - "0": "O", - "1": "l", - "֭": "֖", - "֮": "֘", - "֨": "֙", - "֤": "֚", - "᪴": "ۛ", - "⃛": "ۛ", - "ؙ": "̓", - "ࣳ": "̓", - "̓": "̓", - "̕": "̓", - "ُ": "̓", - "ٝ": "̔", - "֜": "́", - "֝": "́", - "ؘ": "́", - "݇": "́", - "́": "́", - "॔": "́", - "َ": "́", - "̀": "̀", - "॓": "̀", - "̌": "̆", - "꙼": "̆", - "٘": "̆", - "ٚ": "̆", - "ͮ": "̆", - "ۨ": "̆̇", - "̐": "̆̇", - "ँ": "̆̇", - "ঁ": "̆̇", - "ઁ": "̆̇", - "ଁ": "̆̇", - "ఀ": "̆̇", - "ಁ": "̆̇", - "ഁ": "̆̇", - "𑒿": "̆̇", - "᳐": "̂", - "̑": "̂", - "ٛ": "̂", - "߮": "̂", - "꛰": "̂", - "֯": "̊", - "۟": "̊", - "៓": "̊", - "゚": "̊", - "ْ": "̊", - "ஂ": "̊", - "ံ": "̊", - "ំ": "̊", - "𑌀": "̊", - "ํ": "̊", - "ໍ": "̊", - "ͦ": "̊", - "ⷪ": "̊", - "࣫": "̈", - "߳": "̈", - "ً": "̋", - "ࣰ": "̋", - "͂": "̃", - "ٓ": "̃", - "ׄ": "̇", - "۬": "̇", - "݀": "̇", - "࣪": "̇", - "݁": "̇", - "͘": "̇", - "ֹ": "̇", - "ֺ": "̇", - "ׂ": "̇", - "ׁ": "̇", - "߭": "̇", - "ं": "̇", - "ਂ": "̇", - "ં": "̇", - "்": "̇", - "̷": "̸", - "᪷": "̨", - "̢": "̨", - "ͅ": "̨", - "᳒": "̄", - "̅": "̄", - "ٙ": "̄", - "߫": "̄", - "꛱": "̄", - "᳚": "̎", - "ٗ": "̒", - "͗": "͐", - "ࣿ": "͐", - "ࣸ": "͐", - "ऀ": "͒", - "᳭": "̖", - "᳜": "̩", - "ٖ": "̩", - "᳕": "̫", - "͇": "̳", - "ࣹ": "͔", - "ࣺ": "͕", - "゛": "゙", - "゜": "゚", - "̶": "̵", - "〬": "̉", - "ׅ": "̣", - "࣭": "̣", - "᳝": "̣", - "ִ": "̣", - "ٜ": "̣", - "़": "̣", - "়": "̣", - "਼": "̣", - "઼": "̣", - "଼": "̣", - "𑇊": "̣", - "𑓃": "̣", - "𐨺": "̣", - "࣮": "̤", - "᳞": "̤", - "༷": "̥", - "〭": "̥", - "̧": "̦", - "̡": "̦", - "̹": "̦", - "᳙": "̭", - "᳘": "̮", - "॒": "̱", - "̠": "̱", - "ࣱ": "ٌ", - "ࣨ": "ٌ", - "ࣥ": "ٌ", - "ﱞ": "ﹲّ", - "ࣲ": "ٍ", - "ﱟ": "ﹴّ", - "ﳲ": "ﹷّ", - "ﱠ": "ﹶّ", - "ﳳ": "ﹹّ", - "ﱡ": "ﹸّ", - "ؚ": "ِ", - "̗": "ِ", - "ﳴ": "ﹻّ", - "ﱢ": "ﹺّ", - "ﱣ": "ﹼٰ", - "ٟ": "ٕ", - "̍": "ٰ", - "݂": "ܼ", - "ਃ": "ঃ", - "ః": "ঃ", - "ಃ": "ঃ", - "ഃ": "ঃ", - "ඃ": "ঃ", - "း": "ঃ", - "𑓁": "ঃ", - "់": "่", - "່": "่", - "້": "้", - "໊": "๊", - "໋": "๋", - "꙯": "⃩", - "\u2028": " ", - "\u2029": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - " ": " ", - "ߺ": "_", - "﹍": "_", - "﹎": "_", - "﹏": "_", - "‐": "-", - "‑": "-", - "‒": "-", - "–": "-", - "﹘": "-", - "۔": "-", - "⁃": "-", - "˗": "-", - "−": "-", - "➖": "-", - "Ⲻ": "-", - "⨩": "-̓", - "⸚": "-̈", - "﬩": "-̇", - "∸": "-̇", - "⨪": "-̣", - "꓾": "-.", - "~": "〜", - "؍": ",", - "٫": ",", - "‚": ",", - "¸": ",", - "ꓹ": ",", - "⸲": "،", - "٬": "،", - ";": ";", - "⸵": "؛", - "ः": ":", - "ઃ": ":", - ":": ":", - "։": ":", - "܃": ":", - "܄": ":", - "᛬": ":", - "︰": ":", - "᠃": ":", - "᠉": ":", - "⁚": ":", - "׃": ":", - "˸": ":", - "꞉": ":", - "∶": ":", - "ː": ":", - "ꓽ": ":", - "⩴": "::=", - "⧴": ":→", - "!": "!", - "ǃ": "!", - "ⵑ": "!", - "‼": "!!", - "⁉": "!?", - "ʔ": "?", - "Ɂ": "?", - "ॽ": "?", - "Ꭾ": "?", - "ꛫ": "?", - "⁈": "?!", - "⁇": "??", - "⸮": "؟", - "𝅭": ".", - "․": ".", - "܁": ".", - "܂": ".", - "꘎": ".", - "𐩐": ".", - "٠": ".", - "۰": ".", - "ꓸ": ".", - "ꓻ": ".,", - "‥": "..", - "ꓺ": "..", - "…": "...", - "꛴": "꛳꛳", - "・": "·", - "・": "·", - "᛫": "·", - "·": "·", - "⸱": "·", - "𐄁": "·", - "•": "·", - "‧": "·", - "∙": "·", - "⋅": "·", - "ꞏ": "·", - "ᐧ": "·", - "⋯": "···", - "ⵈ": "···", - "ᑄ": "·<", - "⋗": "·>", - "ᐷ": "·>", - "ᑀ": "·>", - "ᔯ": "·4", - "ᑾ": "·b", - "ᒀ": "·ḃ", - "ᑺ": "·d", - "ᒘ": "·J", - "ᒶ": "·L", - "ᑶ": "·P", - "ᑗ": "·U", - "ᐺ": "·V", - "ᐼ": "·Ʌ", - "ᒮ": "·Γ", - "ᐎ": "·Δ", - "ᑙ": "·Ո", - "ᐌ": "·ᐁ", - "ᐐ": "·ᐄ", - "ᐒ": "·ᐅ", - "ᐔ": "·ᐆ", - "ᐗ": "·ᐊ", - "ᐙ": "·ᐋ", - "ᐾ": "·ᐲ", - "ᑂ": "·ᐴ", - "ᑆ": "·ᐹ", - "ᑛ": "·ᑏ", - "ᑔ": "·ᑐ", - "ᑝ": "·ᑐ", - "ᑟ": "·ᑑ", - "ᑡ": "·ᑕ", - "ᑣ": "·ᑖ", - "ᑴ": "·ᑫ", - "ᑸ": "·ᑮ", - "ᑼ": "·ᑰ", - "ᒒ": "·ᒉ", - "ᒔ": "·ᒋ", - "ᒖ": "·ᒌ", - "ᒚ": "·ᒎ", - "ᒜ": "·ᒐ", - "ᒞ": "·ᒑ", - "ᒬ": "·ᒣ", - "ᒰ": "·ᒦ", - "ᒲ": "·ᒧ", - "ᒴ": "·ᒨ", - "ᒸ": "·ᒫ", - "ᓉ": "·ᓀ", - "ᣆ": "·ᓂ", - "ᣈ": "·ᓃ", - "ᣊ": "·ᓄ", - "ᣌ": "·ᓅ", - "ᓋ": "·ᓇ", - "ᓍ": "·ᓈ", - "ᓜ": "·ᓓ", - "ᓞ": "·ᓕ", - "ᓠ": "·ᓖ", - "ᓢ": "·ᓗ", - "ᓤ": "·ᓘ", - "ᓦ": "·ᓚ", - "ᓨ": "·ᓛ", - "ᓶ": "·ᓭ", - "ᓸ": "·ᓯ", - "ᓺ": "·ᓰ", - "ᓼ": "·ᓱ", - "ᓾ": "·ᓲ", - "ᔀ": "·ᓴ", - "ᔂ": "·ᓵ", - "ᔗ": "·ᔐ", - "ᔙ": "·ᔑ", - "ᔛ": "·ᔒ", - "ᔝ": "·ᔓ", - "ᔟ": "·ᔔ", - "ᔡ": "·ᔕ", - "ᔣ": "·ᔖ", - "ᔱ": "·ᔨ", - "ᔳ": "·ᔩ", - "ᔵ": "·ᔪ", - "ᔷ": "·ᔫ", - "ᔹ": "·ᔭ", - "ᔻ": "·ᔮ", - "ᣎ": "·ᕃ", - "ᣏ": "·ᕆ", - "ᣐ": "·ᕇ", - "ᣑ": "·ᕈ", - "ᣒ": "·ᕉ", - "ᣓ": "·ᕋ", - "ᕎ": "·ᕌ", - "ᕛ": "·ᕚ", - "ᕨ": "·ᕧ", - "ᢳ": "·ᢱ", - "ᢶ": "·ᢴ", - "ᢹ": "·ᢸ", - "ᣂ": "·ᣀ", - "꠰": "।", - "॥": "।।", - "᰼": "᰻᰻", - "။": "၊၊", - "᪩": "᪨᪨", - "᪫": "᪪᪨", - "᭟": "᭞᭞", - "𐩗": "𐩖𐩖", - "𑑌": "𑑋𑑋", - "𑙂": "𑙁𑙁", - "𑱂": "𑱁𑱁", - "᱿": "᱾᱾", - "՝": "'", - "'": "'", - "‘": "'", - "’": "'", - "‛": "'", - "′": "'", - "‵": "'", - "՚": "'", - "׳": "'", - "`": "'", - "`": "'", - "`": "'", - "´": "'", - "΄": "'", - "´": "'", - "᾽": "'", - "᾿": "'", - "῾": "'", - "ʹ": "'", - "ʹ": "'", - "ˈ": "'", - "ˊ": "'", - "ˋ": "'", - "˴": "'", - "ʻ": "'", - "ʽ": "'", - "ʼ": "'", - "ʾ": "'", - "ꞌ": "'", - "י": "'", - "ߴ": "'", - "ߵ": "'", - "ᑊ": "'", - "ᛌ": "'", - "𖽑": "'", - "𖽒": "'", - "᳓": "''", - "\"": "''", - """: "''", - "“": "''", - "”": "''", - "‟": "''", - "″": "''", - "‶": "''", - "〃": "''", - "״": "''", - "˝": "''", - "ʺ": "''", - "˶": "''", - "ˮ": "''", - "ײ": "''", - "‴": "'''", - "‷": "'''", - "⁗": "''''", - "Ɓ": "'B", - "Ɗ": "'D", - "ʼn": "'n", - "Ƥ": "'P", - "Ƭ": "'T", - "Ƴ": "'Y", - "[": "(", - "❨": "(", - "❲": "(", - "〔": "(", - "﴾": "(", - "⸨": "((", - "㈠": "(ー)", - "⑵": "(2)", - "⒇": "(2O)", - "⑶": "(3)", - "⑷": "(4)", - "⑸": "(5)", - "⑹": "(6)", - "⑺": "(7)", - "⑻": "(8)", - "⑼": "(9)", - "⒜": "(a)", - "🄐": "(A)", - "⒝": "(b)", - "🄑": "(B)", - "⒞": "(c)", - "🄒": "(C)", - "⒟": "(d)", - "🄓": "(D)", - "⒠": "(e)", - "🄔": "(E)", - "⒡": "(f)", - "🄕": "(F)", - "⒢": "(g)", - "🄖": "(G)", - "⒣": "(h)", - "🄗": "(H)", - "⒤": "(i)", - "⒥": "(j)", - "🄙": "(J)", - "⒦": "(k)", - "🄚": "(K)", - "⑴": "(l)", - "🄘": "(l)", - "⒧": "(l)", - "🄛": "(L)", - "⑿": "(l2)", - "⒀": "(l3)", - "⒁": "(l4)", - "⒂": "(l5)", - "⒃": "(l6)", - "⒄": "(l7)", - "⒅": "(l8)", - "⒆": "(l9)", - "⑾": "(ll)", - "⑽": "(lO)", - "🄜": "(M)", - "⒩": "(n)", - "🄝": "(N)", - "⒪": "(o)", - "🄞": "(O)", - "⒫": "(p)", - "🄟": "(P)", - "⒬": "(q)", - "🄠": "(Q)", - "⒭": "(r)", - "🄡": "(R)", - "⒨": "(rn)", - "⒮": "(s)", - "🄢": "(S)", - "🄪": "(S)", - "⒯": "(t)", - "🄣": "(T)", - "⒰": "(u)", - "🄤": "(U)", - "⒱": "(v)", - "🄥": "(V)", - "⒲": "(w)", - "🄦": "(W)", - "⒳": "(x)", - "🄧": "(X)", - "⒴": "(y)", - "🄨": "(Y)", - "⒵": "(z)", - "🄩": "(Z)", - "㈀": "(ᄀ)", - "㈎": "(가)", - "㈁": "(ᄂ)", - "㈏": "(나)", - "㈂": "(ᄃ)", - "㈐": "(다)", - "㈃": "(ᄅ)", - "㈑": "(라)", - "㈄": "(ᄆ)", - "㈒": "(마)", - "㈅": "(ᄇ)", - "㈓": "(바)", - "㈆": "(ᄉ)", - "㈔": "(사)", - "㈇": "(ᄋ)", - "㈕": "(아)", - "㈝": "(오전)", - "㈞": "(오후)", - "㈈": "(ᄌ)", - "㈖": "(자)", - "㈜": "(주)", - "㈉": "(ᄎ)", - "㈗": "(차)", - "㈊": "(ᄏ)", - "㈘": "(카)", - "㈋": "(ᄐ)", - "㈙": "(타)", - "㈌": "(ᄑ)", - "㈚": "(파)", - "㈍": "(ᄒ)", - "㈛": "(하)", - "㈦": "(七)", - "㈢": "(三)", - "🉁": "(三)", - "㈨": "(九)", - "㈡": "(二)", - "🉂": "(二)", - "㈤": "(五)", - "㈹": "(代)", - "㈽": "(企)", - "㉁": "(休)", - "㈧": "(八)", - "㈥": "(六)", - "㈸": "(労)", - "🉇": "(勝)", - "㈩": "(十)", - "㈿": "(協)", - "㈴": "(名)", - "㈺": "(呼)", - "㈣": "(四)", - "㈯": "(土)", - "㈻": "(学)", - "🉃": "(安)", - "🉅": "(打)", - "🉈": "(敗)", - "㈰": "(日)", - "㈪": "(月)", - "㈲": "(有)", - "㈭": "(木)", - "🉀": "(本)", - "㈱": "(株)", - "㈬": "(水)", - "㈫": "(火)", - "🉄": "(点)", - "㈵": "(特)", - "🉆": "(盗)", - "㈼": "(監)", - "㈳": "(社)", - "㈷": "(祝)", - "㉀": "(祭)", - "㉂": "(自)", - "㉃": "(至)", - "㈶": "(財)", - "㈾": "(資)", - "㈮": "(金)", - "]": ")", - "❩": ")", - "❳": ")", - "〕": ")", - "﴿": ")", - "⸩": "))", - "❴": "{", - "𝄔": "{", - "❵": "}", - "〚": "⟦", - "〛": "⟧", - "⟨": "❬", - "〈": "❬", - "〈": "❬", - "㇛": "❬", - "く": "❬", - "𡿨": "❬", - "⟩": "❭", - "〉": "❭", - "〉": "❭", - "^": "︿", - "⸿": "¶", - "⁎": "*", - "٭": "*", - "∗": "*", - "𐌟": "*", - "᜵": "/", - "⁁": "/", - "∕": "/", - "⁄": "/", - "╱": "/", - "⟋": "/", - "⧸": "/", - "𝈺": "/", - "㇓": "/", - "〳": "/", - "Ⳇ": "/", - "ノ": "/", - "丿": "/", - "⼃": "/", - "⧶": "/̄", - "⫽": "//", - "⫻": "///", - "\": "\\", - "﹨": "\\", - "∖": "\\", - "⟍": "\\", - "⧵": "\\", - "⧹": "\\", - "𝈏": "\\", - "𝈻": "\\", - "㇔": "\\", - "丶": "\\", - "⼂": "\\", - "⳹": "\\\\", - "⑊": "\\\\", - "⟈": "\\ᑕ", - "ꝸ": "&", - "૰": "॰", - "𑂻": "॰", - "𑇇": "॰", - "⚬": "॰", - "𑇛": "꣼", - "៙": "๏", - "៕": "๚", - "៚": "๛", - "༌": "་", - "༎": "།།", - "˄": "^", - "ˆ": "^", - "꙾": "ˇ", - "˘": "ˇ", - "‾": "ˉ", - "﹉": "ˉ", - "﹊": "ˉ", - "﹋": "ˉ", - "﹌": "ˉ", - "¯": "ˉ", - " ̄": "ˉ", - "▔": "ˉ", - "ъ": "ˉb", - "ꙑ": "ˉbi", - "͵": "ˏ", - "˻": "˪", - "꜖": "˪", - "꜔": "˫", - "。": "˳", - "⸰": "°", - "˚": "°", - "∘": "°", - "○": "°", - "◦": "°", - "⍜": "°̲", - "⍤": "°̈", - "℃": "°C", - "℉": "°F", - "௵": "௳", - "༛": "༚༚", - "༟": "༚༝", - "࿎": "༝༚", - "༞": "༝༝", - "Ⓒ": "©", - "Ⓡ": "®", - "Ⓟ": "℗", - "𝈛": "⅄", - "⯬": "↞", - "⯭": "↟", - "⯮": "↠", - "⯯": "↡", - "↵": "↲", - "⥥": "⇃⇂", - "⥯": "⇃ᛚ", - "𝛛": "∂", - "𝜕": "∂", - "𝝏": "∂", - "𝞉": "∂", - "𝟃": "∂", - "𞣌": "∂", - "𞣍": "∂̵", - "ð": "∂̵", - "⌀": "∅", - "𝛁": "∇", - "𝛻": "∇", - "𝜵": "∇", - "𝝯": "∇", - "𝞩": "∇", - "𑢨": "∇", - "⍢": "∇̈", - "⍫": "∇̴", - "█": "∎", - "■": "∎", - "⨿": "∐", - "᛭": "+", - "➕": "+", - "𐊛": "+", - "⨣": "+̂", - "⨢": "+̊", - "⨤": "+̃", - "∔": "+̇", - "⨥": "+̣", - "⨦": "+̰", - "⨧": "+₂", - "➗": "÷", - "‹": "<", - "❮": "<", - "˂": "<", - "𝈶": "<", - "ᐸ": "<", - "ᚲ": "<", - "⋖": "<·", - "Ⲵ": "<·", - "ᑅ": "<·", - "≪": "<<", - "⋘": "<<<", - "᐀": "=", - "⹀": "=", - "゠": "=", - "꓿": "=", - "≚": "=̆", - "≙": "=̂", - "≗": "=̊", - "≐": "=̇", - "≑": "=̣̇", - "⩮": "=⃰", - "⩵": "==", - "⩶": "===", - "≞": "=ͫ", - "›": ">", - "❯": ">", - "˃": ">", - "𝈷": ">", - "ᐳ": ">", - "𖼿": ">", - "ᑁ": ">·", - "⪥": "><", - "≫": ">>", - "⨠": ">>", - "⋙": ">>>", - "⁓": "~", - "˜": "~", - "῀": "~", - "∼": "~", - "⍨": "~̈", - "⸞": "~̇", - "⩪": "~̇", - "⸟": "~̣", - "𞣈": "∠", - "⋀": "∧", - "∯": "∮∮", - "∰": "∮∮∮", - "⸫": "∴", - "⸪": "∵", - "⸬": "∷", - "𑇞": "≈", - "♎": "≏", - "🝞": "≏", - "≣": "≡", - "⨃": "⊍", - "⨄": "⊎", - "𝈸": "⊏", - "𝈹": "⊐", - "⨅": "⊓", - "⨆": "⊔", - "⨂": "⊗", - "⍟": "⊛", - "🝱": "⊠", - "🝕": "⊡", - "◁": "⊲", - "▷": "⊳", - "⍣": "⋆̈", - "︴": "⌇", - "◠": "⌒", - "⨽": "⌙", - "⌥": "⌤", - "⧇": "⌻", - "◎": "⌾", - "⦾": "⌾", - "⧅": "⍂", - "⦰": "⍉", - "⏃": "⍋", - "⏂": "⍎", - "⏁": "⍕", - "⏆": "⍭", - "☸": "⎈", - "︵": "⏜", - "︶": "⏝", - "︷": "⏞", - "︸": "⏟", - "︹": "⏠", - "︺": "⏡", - "▱": "⏥", - "⏼": "⏻", - "︱": "│", - "|": "│", - "┃": "│", - "┏": "┌", - "┣": "├", - "▐": "▌", - "▗": "▖", - "▝": "▘", - "☐": "□", - "■": "▪", - "▸": "▶", - "►": "▶", - "⳩": "☧", - "🜊": "☩", - "🌒": "☽", - "🌙": "☽", - "⏾": "☾", - "🌘": "☾", - "⧙": "⦚", - "🜺": "⧟", - "⨾": "⨟", - "𐆠": "⳨", - "♩": "𝅘𝅥", - "♪": "𝅘𝅥𝅮", - "⓪": "🄍", - "↺": "🄎", - "˙": "ॱ", - "ൎ": "ॱ", - "-": "ー", - "—": "ー", - "―": "ー", - "─": "ー", - "━": "ー", - "㇐": "ー", - "ꟷ": "ー", - "ᅳ": "ー", - "ㅡ": "ー", - "一": "ー", - "⼀": "ー", - "ᆖ": "ーー", - "ힹ": "ーᅡ", - "ힺ": "ーᅥ", - "ힻ": "ーᅥ丨", - "ힼ": "ーᅩ", - "ᆕ": "ーᅮ", - "ᅴ": "ー丨", - "ㅢ": "ー丨", - "ᆗ": "ー丨ᅮ", - "🄏": "$⃠", - "₤": "£", - "〒": "₸", - "〶": "₸", - "᭜": "᭐", - "꧆": "꧐", - "𑓑": "১", - "೧": "౧", - "ၥ": "၁", - "①": "➀", - "⑩": "➉", - "⏨": "₁₀", - "𝟐": "2", - "𝟚": "2", - "𝟤": "2", - "𝟮": "2", - "𝟸": "2", - "🯲": "2", - "Ꝛ": "2", - "Ƨ": "2", - "Ϩ": "2", - "Ꙅ": "2", - "ᒿ": "2", - "ꛯ": "2", - "ꧏ": "٢", - "۲": "٢", - "૨": "२", - "𑓒": "২", - "೨": "౨", - "②": "➁", - "ƻ": "2̵", - "🄃": "2,", - "⒉": "2.", - "㏵": "22日", - "㍮": "22点", - "㏶": "23日", - "㍯": "23点", - "㏷": "24日", - "㍰": "24点", - "㏸": "25日", - "㏹": "26日", - "㏺": "27日", - "㏻": "28日", - "㏼": "29日", - "㏴": "2l日", - "㍭": "2l点", - "⒛": "2O.", - "㏳": "2O日", - "㍬": "2O点", - "෩": "෨ා", - "෯": "෨ී", - "㏡": "2日", - "㋁": "2月", - "㍚": "2点", - "𝈆": "3", - "𝟑": "3", - "𝟛": "3", - "𝟥": "3", - "𝟯": "3", - "𝟹": "3", - "🯳": "3", - "Ɜ": "3", - "Ȝ": "3", - "Ʒ": "3", - "Ꝫ": "3", - "Ⳍ": "3", - "З": "3", - "Ӡ": "3", - "𖼻": "3", - "𑣊": "3", - "۳": "٣", - "𞣉": "٣", - "૩": "३", - "③": "➂", - "Ҙ": "3̦", - "🄄": "3,", - "⒊": "3.", - "㏾": "3l日", - "㏽": "3O日", - "㏢": "3日", - "㋂": "3月", - "㍛": "3点", - "𝟒": "4", - "𝟜": "4", - "𝟦": "4", - "𝟰": "4", - "𝟺": "4", - "🯴": "4", - "Ꮞ": "4", - "𑢯": "4", - "۴": "٤", - "૪": "४", - "④": "➃", - "🄅": "4,", - "⒋": "4.", - "ᔰ": "4·", - "㏣": "4日", - "㋃": "4月", - "㍜": "4点", - "𝟓": "5", - "𝟝": "5", - "𝟧": "5", - "𝟱": "5", - "𝟻": "5", - "🯵": "5", - "Ƽ": "5", - "𑢻": "5", - "⑤": "➄", - "🄆": "5,", - "⒌": "5.", - "㏤": "5日", - "㋄": "5月", - "㍝": "5点", - "𝟔": "6", - "𝟞": "6", - "𝟨": "6", - "𝟲": "6", - "𝟼": "6", - "🯶": "6", - "Ⳓ": "6", - "б": "6", - "Ꮾ": "6", - "𑣕": "6", - "۶": "٦", - "𑓖": "৬", - "⑥": "➅", - "🄇": "6,", - "⒍": "6.", - "㏥": "6日", - "㋅": "6月", - "㍞": "6点", - "𝈒": "7", - "𝟕": "7", - "𝟟": "7", - "𝟩": "7", - "𝟳": "7", - "𝟽": "7", - "🯷": "7", - "𐓒": "7", - "𑣆": "7", - "⑦": "➆", - "🄈": "7,", - "⒎": "7.", - "㏦": "7日", - "㋆": "7月", - "㍟": "7点", - "ଃ": "8", - "৪": "8", - "੪": "8", - "𞣋": "8", - "𝟖": "8", - "𝟠": "8", - "𝟪": "8", - "𝟴": "8", - "𝟾": "8", - "🯸": "8", - "ȣ": "8", - "Ȣ": "8", - "𐌚": "8", - "૮": "८", - "⑧": "➇", - "🄉": "8,", - "⒏": "8.", - "㏧": "8日", - "㋇": "8月", - "㍠": "8点", - "੧": "9", - "୨": "9", - "৭": "9", - "൭": "9", - "𝟗": "9", - "𝟡": "9", - "𝟫": "9", - "𝟵": "9", - "𝟿": "9", - "🯹": "9", - "Ꝯ": "9", - "Ⳋ": "9", - "𑣌": "9", - "𑢬": "9", - "𑣖": "9", - "१": "٩", - "𑣤": "٩", - "۹": "٩", - "೯": "౯", - "⑨": "➈", - "🄊": "9,", - "⒐": "9.", - "㏨": "9日", - "㋈": "9月", - "㍡": "9点", - "⍺": "a", - "a": "a", - "𝐚": "a", - "𝑎": "a", - "𝒂": "a", - "𝒶": "a", - "𝓪": "a", - "𝔞": "a", - "𝕒": "a", - "𝖆": "a", - "𝖺": "a", - "𝗮": "a", - "𝘢": "a", - "𝙖": "a", - "𝚊": "a", - "ɑ": "a", - "α": "a", - "𝛂": "a", - "𝛼": "a", - "𝜶": "a", - "𝝰": "a", - "𝞪": "a", - "а": "a", - "ⷶ": "ͣ", - "A": "A", - "𝐀": "A", - "𝐴": "A", - "𝑨": "A", - "𝒜": "A", - "𝓐": "A", - "𝔄": "A", - "𝔸": "A", - "𝕬": "A", - "𝖠": "A", - "𝗔": "A", - "𝘈": "A", - "𝘼": "A", - "𝙰": "A", - "Α": "A", - "𝚨": "A", - "𝛢": "A", - "𝜜": "A", - "𝝖": "A", - "𝞐": "A", - "А": "A", - "Ꭺ": "A", - "ᗅ": "A", - "ꓮ": "A", - "𖽀": "A", - "𐊠": "A", - "⍶": "a̲", - "ǎ": "ă", - "Ǎ": "Ă", - "ȧ": "å", - "Ȧ": "Å", - "ẚ": "ả", - "℀": "a/c", - "℁": "a/s", - "ꜳ": "aa", - "Ꜳ": "AA", - "æ": "ae", - "ӕ": "ae", - "Æ": "AE", - "Ӕ": "AE", - "ꜵ": "ao", - "Ꜵ": "AO", - "🜇": "AR", - "ꜷ": "au", - "Ꜷ": "AU", - "ꜹ": "av", - "ꜻ": "av", - "Ꜹ": "AV", - "Ꜻ": "AV", - "ꜽ": "ay", - "Ꜽ": "AY", - "ꭺ": "ᴀ", - "∀": "Ɐ", - "𝈗": "Ɐ", - "ᗄ": "Ɐ", - "ꓯ": "Ɐ", - "𐐟": "Ɒ", - "𝐛": "b", - "𝑏": "b", - "𝒃": "b", - "𝒷": "b", - "𝓫": "b", - "𝔟": "b", - "𝕓": "b", - "𝖇": "b", - "𝖻": "b", - "𝗯": "b", - "𝘣": "b", - "𝙗": "b", - "𝚋": "b", - "Ƅ": "b", - "Ь": "b", - "Ꮟ": "b", - "ᑲ": "b", - "ᖯ": "b", - "B": "B", - "ℬ": "B", - "𝐁": "B", - "𝐵": "B", - "𝑩": "B", - "𝓑": "B", - "𝔅": "B", - "𝔹": "B", - "𝕭": "B", - "𝖡": "B", - "𝗕": "B", - "𝘉": "B", - "𝘽": "B", - "𝙱": "B", - "Ꞵ": "B", - "Β": "B", - "𝚩": "B", - "𝛣": "B", - "𝜝": "B", - "𝝗": "B", - "𝞑": "B", - "В": "B", - "Ᏼ": "B", - "ᗷ": "B", - "ꓐ": "B", - "𐊂": "B", - "𐊡": "B", - "𐌁": "B", - "ɓ": "b̔", - "ᑳ": "ḃ", - "ƃ": "b̄", - "Ƃ": "b̄", - "Б": "b̄", - "ƀ": "b̵", - "ҍ": "b̵", - "Ҍ": "b̵", - "ѣ": "b̵", - "Ѣ": "b̵", - "ᑿ": "b·", - "ᒁ": "ḃ·", - "ᒈ": "b'", - "Ы": "bl", - "в": "ʙ", - "ᏼ": "ʙ", - "c": "c", - "ⅽ": "c", - "𝐜": "c", - "𝑐": "c", - "𝒄": "c", - "𝒸": "c", - "𝓬": "c", - "𝔠": "c", - "𝕔": "c", - "𝖈": "c", - "𝖼": "c", - "𝗰": "c", - "𝘤": "c", - "𝙘": "c", - "𝚌": "c", - "ᴄ": "c", - "ϲ": "c", - "ⲥ": "c", - "с": "c", - "ꮯ": "c", - "𐐽": "c", - "ⷭ": "ͨ", - "🝌": "C", - "𑣲": "C", - "𑣩": "C", - "C": "C", - "Ⅽ": "C", - "ℂ": "C", - "ℭ": "C", - "𝐂": "C", - "𝐶": "C", - "𝑪": "C", - "𝒞": "C", - "𝓒": "C", - "𝕮": "C", - "𝖢": "C", - "𝗖": "C", - "𝘊": "C", - "𝘾": "C", - "𝙲": "C", - "Ϲ": "C", - "Ⲥ": "C", - "С": "C", - "Ꮯ": "C", - "ꓚ": "C", - "𐊢": "C", - "𐌂": "C", - "𐐕": "C", - "𐔜": "C", - "¢": "c̸", - "ȼ": "c̸", - "₡": "C⃫", - "🅮": "C⃠", - "ç": "c̦", - "ҫ": "c̦", - "Ç": "C̦", - "Ҫ": "C̦", - "Ƈ": "C'", - "℅": "c/o", - "℆": "c/u", - "🅭": "㏄\t⃝", - "⋴": "ꞓ", - "ɛ": "ꞓ", - "ε": "ꞓ", - "ϵ": "ꞓ", - "𝛆": "ꞓ", - "𝛜": "ꞓ", - "𝜀": "ꞓ", - "𝜖": "ꞓ", - "𝜺": "ꞓ", - "𝝐": "ꞓ", - "𝝴": "ꞓ", - "𝞊": "ꞓ", - "𝞮": "ꞓ", - "𝟄": "ꞓ", - "ⲉ": "ꞓ", - "є": "ꞓ", - "ԑ": "ꞓ", - "ꮛ": "ꞓ", - "𑣎": "ꞓ", - "𐐩": "ꞓ", - "€": "Ꞓ", - "Ⲉ": "Ꞓ", - "Є": "Ꞓ", - "⍷": "ꞓ̲", - "ͽ": "ꜿ", - "Ͽ": "Ꜿ", - "ⅾ": "d", - "ⅆ": "d", - "𝐝": "d", - "𝑑": "d", - "𝒅": "d", - "𝒹": "d", - "𝓭": "d", - "𝔡": "d", - "𝕕": "d", - "𝖉": "d", - "𝖽": "d", - "𝗱": "d", - "𝘥": "d", - "𝙙": "d", - "𝚍": "d", - "ԁ": "d", - "Ꮷ": "d", - "ᑯ": "d", - "ꓒ": "d", - "Ⅾ": "D", - "ⅅ": "D", - "𝐃": "D", - "𝐷": "D", - "𝑫": "D", - "𝒟": "D", - "𝓓": "D", - "𝔇": "D", - "𝔻": "D", - "𝕯": "D", - "𝖣": "D", - "𝗗": "D", - "𝘋": "D", - "𝘿": "D", - "𝙳": "D", - "Ꭰ": "D", - "ᗞ": "D", - "ᗪ": "D", - "ꓓ": "D", - "ɗ": "d̔", - "ɖ": "d̨", - "ƌ": "d̄", - "đ": "d̵", - "Đ": "D̵", - "Ð": "D̵", - "Ɖ": "D̵", - "₫": "ḏ̵", - "ꝺ": "Ꝺ", - "ᑻ": "d·", - "ᒇ": "d'", - "ʤ": "dȝ", - "dz": "dz", - "ʣ": "dz", - "Dz": "Dz", - "DZ": "DZ", - "dž": "dž", - "Dž": "Dž", - "DŽ": "DŽ", - "ʥ": "dʑ", - "ꭰ": "ᴅ", - "⸹": "ẟ", - "δ": "ẟ", - "𝛅": "ẟ", - "𝛿": "ẟ", - "𝜹": "ẟ", - "𝝳": "ẟ", - "𝞭": "ẟ", - "ծ": "ẟ", - "ᕷ": "ẟ", - "℮": "e", - "e": "e", - "ℯ": "e", - "ⅇ": "e", - "𝐞": "e", - "𝑒": "e", - "𝒆": "e", - "𝓮": "e", - "𝔢": "e", - "𝕖": "e", - "𝖊": "e", - "𝖾": "e", - "𝗲": "e", - "𝘦": "e", - "𝙚": "e", - "𝚎": "e", - "ꬲ": "e", - "е": "e", - "ҽ": "e", - "ⷷ": "ͤ", - "⋿": "E", - "E": "E", - "ℰ": "E", - "𝐄": "E", - "𝐸": "E", - "𝑬": "E", - "𝓔": "E", - "𝔈": "E", - "𝔼": "E", - "𝕰": "E", - "𝖤": "E", - "𝗘": "E", - "𝘌": "E", - "𝙀": "E", - "𝙴": "E", - "Ε": "E", - "𝚬": "E", - "𝛦": "E", - "𝜠": "E", - "𝝚": "E", - "𝞔": "E", - "Е": "E", - "ⴹ": "E", - "Ꭼ": "E", - "ꓰ": "E", - "𑢦": "E", - "𑢮": "E", - "𐊆": "E", - "ě": "ĕ", - "Ě": "Ĕ", - "ɇ": "e̸", - "Ɇ": "E̸", - "ҿ": "ę", - "ꭼ": "ᴇ", - "ə": "ǝ", - "ә": "ǝ", - "∃": "Ǝ", - "ⴺ": "Ǝ", - "ꓱ": "Ǝ", - "ɚ": "ǝ˞", - "ᴔ": "ǝo", - "ꭁ": "ǝo̸", - "ꭂ": "ǝo̵", - "Ә": "Ə", - "𝈡": "Ɛ", - "ℇ": "Ɛ", - "Ԑ": "Ɛ", - "Ꮛ": "Ɛ", - "𖼭": "Ɛ", - "𐐁": "Ɛ", - "ᶟ": "ᵋ", - "ᴈ": "ɜ", - "з": "ɜ", - "ҙ": "ɜ̦", - "𐑂": "ɞ", - "ꞝ": "ʚ", - "𐐪": "ʚ", - "𝐟": "f", - "𝑓": "f", - "𝒇": "f", - "𝒻": "f", - "𝓯": "f", - "𝔣": "f", - "𝕗": "f", - "𝖋": "f", - "𝖿": "f", - "𝗳": "f", - "𝘧": "f", - "𝙛": "f", - "𝚏": "f", - "ꬵ": "f", - "ꞙ": "f", - "ſ": "f", - "ẝ": "f", - "ք": "f", - "𝈓": "F", - "ℱ": "F", - "𝐅": "F", - "𝐹": "F", - "𝑭": "F", - "𝓕": "F", - "𝔉": "F", - "𝔽": "F", - "𝕱": "F", - "𝖥": "F", - "𝗙": "F", - "𝘍": "F", - "𝙁": "F", - "𝙵": "F", - "Ꞙ": "F", - "Ϝ": "F", - "𝟊": "F", - "ᖴ": "F", - "ꓝ": "F", - "𑣂": "F", - "𑢢": "F", - "𐊇": "F", - "𐊥": "F", - "𐔥": "F", - "ƒ": "f̦", - "Ƒ": "F̦", - "ᵮ": "f̴", - "℻": "FAX", - "ff": "ff", - "ffi": "ffi", - "ffl": "ffl", - "fi": "fi", - "fl": "fl", - "ʩ": "fŋ", - "ᖵ": "Ⅎ", - "ꓞ": "Ⅎ", - "𝈰": "ꟻ", - "ᖷ": "ꟻ", - "g": "g", - "ℊ": "g", - "𝐠": "g", - "𝑔": "g", - "𝒈": "g", - "𝓰": "g", - "𝔤": "g", - "𝕘": "g", - "𝖌": "g", - "𝗀": "g", - "𝗴": "g", - "𝘨": "g", - "𝙜": "g", - "𝚐": "g", - "ɡ": "g", - "ᶃ": "g", - "ƍ": "g", - "ց": "g", - "𝐆": "G", - "𝐺": "G", - "𝑮": "G", - "𝒢": "G", - "𝓖": "G", - "𝔊": "G", - "𝔾": "G", - "𝕲": "G", - "𝖦": "G", - "𝗚": "G", - "𝘎": "G", - "𝙂": "G", - "𝙶": "G", - "Ԍ": "G", - "Ꮐ": "G", - "Ᏻ": "G", - "ꓖ": "G", - "ᶢ": "ᵍ", - "ɠ": "g̔", - "ǧ": "ğ", - "Ǧ": "Ğ", - "ǵ": "ģ", - "ǥ": "g̵", - "Ǥ": "G̵", - "Ɠ": "G'", - "ԍ": "ɢ", - "ꮐ": "ɢ", - "ᏻ": "ɢ", - "h": "h", - "ℎ": "h", - "𝐡": "h", - "𝒉": "h", - "𝒽": "h", - "𝓱": "h", - "𝔥": "h", - "𝕙": "h", - "𝖍": "h", - "𝗁": "h", - "𝗵": "h", - "𝘩": "h", - "𝙝": "h", - "𝚑": "h", - "һ": "h", - "հ": "h", - "Ꮒ": "h", - "H": "H", - "ℋ": "H", - "ℌ": "H", - "ℍ": "H", - "𝐇": "H", - "𝐻": "H", - "𝑯": "H", - "𝓗": "H", - "𝕳": "H", - "𝖧": "H", - "𝗛": "H", - "𝘏": "H", - "𝙃": "H", - "𝙷": "H", - "Η": "H", - "𝚮": "H", - "𝛨": "H", - "𝜢": "H", - "𝝜": "H", - "𝞖": "H", - "Ⲏ": "H", - "Н": "H", - "Ꮋ": "H", - "ᕼ": "H", - "ꓧ": "H", - "𐋏": "H", - "ᵸ": "ᴴ", - "ɦ": "h̔", - "ꚕ": "h̔", - "Ᏺ": "h̔", - "Ⱨ": "H̩", - "Ң": "H̩", - "ħ": "h̵", - "ℏ": "h̵", - "ћ": "h̵", - "Ħ": "H̵", - "Ӊ": "H̦", - "Ӈ": "H̦", - "н": "ʜ", - "ꮋ": "ʜ", - "ң": "ʜ̩", - "ӊ": "ʜ̦", - "ӈ": "ʜ̦", - "Ԋ": "Ƕ", - "ꮀ": "ⱶ", - "Ͱ": "Ⱶ", - "Ꭸ": "Ⱶ", - "Ꮀ": "Ⱶ", - "ꚱ": "Ⱶ", - "ꞕ": "ꜧ", - "˛": "i", - "⍳": "i", - "i": "i", - "ⅰ": "i", - "ℹ": "i", - "ⅈ": "i", - "𝐢": "i", - "𝑖": "i", - "𝒊": "i", - "𝒾": "i", - "𝓲": "i", - "𝔦": "i", - "𝕚": "i", - "𝖎": "i", - "𝗂": "i", - "𝗶": "i", - "𝘪": "i", - "𝙞": "i", - "𝚒": "i", - "ı": "i", - "𝚤": "i", - "ɪ": "i", - "ɩ": "i", - "ι": "i", - "ι": "i", - "ͺ": "i", - "𝛊": "i", - "𝜄": "i", - "𝜾": "i", - "𝝸": "i", - "𝞲": "i", - "і": "i", - "ꙇ": "i", - "ӏ": "i", - "ꭵ": "i", - "Ꭵ": "i", - "𑣃": "i", - "ⓛ": "Ⓘ", - "⍸": "i̲", - "ǐ": "ĭ", - "Ǐ": "Ĭ", - "ɨ": "i̵", - "ᵻ": "i̵", - "ᵼ": "i̵", - "ⅱ": "ii", - "ⅲ": "iii", - "ij": "ij", - "ⅳ": "iv", - "ⅸ": "ix", - "j": "j", - "ⅉ": "j", - "𝐣": "j", - "𝑗": "j", - "𝒋": "j", - "𝒿": "j", - "𝓳": "j", - "𝔧": "j", - "𝕛": "j", - "𝖏": "j", - "𝗃": "j", - "𝗷": "j", - "𝘫": "j", - "𝙟": "j", - "𝚓": "j", - "ϳ": "j", - "ј": "j", - "J": "J", - "𝐉": "J", - "𝐽": "J", - "𝑱": "J", - "𝒥": "J", - "𝓙": "J", - "𝔍": "J", - "𝕁": "J", - "𝕵": "J", - "𝖩": "J", - "𝗝": "J", - "𝘑": "J", - "𝙅": "J", - "𝙹": "J", - "Ʝ": "J", - "Ϳ": "J", - "Ј": "J", - "Ꭻ": "J", - "ᒍ": "J", - "ꓙ": "J", - "ɉ": "j̵", - "Ɉ": "J̵", - "ᒙ": "J·", - "𝚥": "ȷ", - "յ": "ȷ", - "ꭻ": "ᴊ", - "𝐤": "k", - "𝑘": "k", - "𝒌": "k", - "𝓀": "k", - "𝓴": "k", - "𝔨": "k", - "𝕜": "k", - "𝖐": "k", - "𝗄": "k", - "𝗸": "k", - "𝘬": "k", - "𝙠": "k", - "𝚔": "k", - "K": "K", - "K": "K", - "𝐊": "K", - "𝐾": "K", - "𝑲": "K", - "𝒦": "K", - "𝓚": "K", - "𝔎": "K", - "𝕂": "K", - "𝕶": "K", - "𝖪": "K", - "𝗞": "K", - "𝘒": "K", - "𝙆": "K", - "𝙺": "K", - "Κ": "K", - "𝚱": "K", - "𝛫": "K", - "𝜥": "K", - "𝝟": "K", - "𝞙": "K", - "Ⲕ": "K", - "К": "K", - "Ꮶ": "K", - "ᛕ": "K", - "ꓗ": "K", - "𐔘": "K", - "ƙ": "k̔", - "Ⱪ": "K̩", - "Қ": "K̩", - "₭": "K̵", - "Ꝁ": "K̵", - "Ҟ": "K̵", - "Ƙ": "K'", - "׀": "l", - "|": "l", - "∣": "l", - "⏽": "l", - "│": "l", - "١": "l", - "۱": "l", - "𐌠": "l", - "𞣇": "l", - "𝟏": "l", - "𝟙": "l", - "𝟣": "l", - "𝟭": "l", - "𝟷": "l", - "🯱": "l", - "I": "l", - "I": "l", - "Ⅰ": "l", - "ℐ": "l", - "ℑ": "l", - "𝐈": "l", - "𝐼": "l", - "𝑰": "l", - "𝓘": "l", - "𝕀": "l", - "𝕴": "l", - "𝖨": "l", - "𝗜": "l", - "𝘐": "l", - "𝙄": "l", - "𝙸": "l", - "Ɩ": "l", - "l": "l", - "ⅼ": "l", - "ℓ": "l", - "𝐥": "l", - "𝑙": "l", - "𝒍": "l", - "𝓁": "l", - "𝓵": "l", - "𝔩": "l", - "𝕝": "l", - "𝖑": "l", - "𝗅": "l", - "𝗹": "l", - "𝘭": "l", - "𝙡": "l", - "𝚕": "l", - "ǀ": "l", - "Ι": "l", - "𝚰": "l", - "𝛪": "l", - "𝜤": "l", - "𝝞": "l", - "𝞘": "l", - "Ⲓ": "l", - "І": "l", - "Ӏ": "l", - "ו": "l", - "ן": "l", - "ا": "l", - "𞸀": "l", - "𞺀": "l", - "ﺎ": "l", - "ﺍ": "l", - "ߊ": "l", - "ⵏ": "l", - "ᛁ": "l", - "ꓲ": "l", - "𖼨": "l", - "𐊊": "l", - "𐌉": "l", - "𝈪": "L", - "Ⅼ": "L", - "ℒ": "L", - "𝐋": "L", - "𝐿": "L", - "𝑳": "L", - "𝓛": "L", - "𝔏": "L", - "𝕃": "L", - "𝕷": "L", - "𝖫": "L", - "𝗟": "L", - "𝘓": "L", - "𝙇": "L", - "𝙻": "L", - "Ⳑ": "L", - "Ꮮ": "L", - "ᒪ": "L", - "ꓡ": "L", - "𖼖": "L", - "𑢣": "L", - "𑢲": "L", - "𐐛": "L", - "𐔦": "L", - "ﴼ": "l̋", - "ﴽ": "l̋", - "ł": "l̸", - "Ł": "L̸", - "ɭ": "l̨", - "Ɨ": "l̵", - "ƚ": "l̵", - "ɫ": "l̴", - "إ": "lٕ", - "ﺈ": "lٕ", - "ﺇ": "lٕ", - "ٳ": "lٕ", - "ŀ": "l·", - "Ŀ": "l·", - "ᒷ": "l·", - "🄂": "l,", - "⒈": "l.", - "ױ": "l'", - "⒓": "l2.", - "㏫": "l2日", - "㋋": "l2月", - "㍤": "l2点", - "⒔": "l3.", - "㏬": "l3日", - "㍥": "l3点", - "⒕": "l4.", - "㏭": "l4日", - "㍦": "l4点", - "⒖": "l5.", - "㏮": "l5日", - "㍧": "l5点", - "⒗": "l6.", - "㏯": "l6日", - "㍨": "l6点", - "⒘": "l7.", - "㏰": "l7日", - "㍩": "l7点", - "⒙": "l8.", - "㏱": "l8日", - "㍪": "l8点", - "⒚": "l9.", - "㏲": "l9日", - "㍫": "l9点", - "lj": "lj", - "IJ": "lJ", - "Lj": "Lj", - "LJ": "LJ", - "‖": "ll", - "∥": "ll", - "Ⅱ": "ll", - "ǁ": "ll", - "װ": "ll", - "𐆙": "l̵l̵", - "⒒": "ll.", - "Ⅲ": "lll", - "𐆘": "l̵l̵S̵", - "㏪": "ll日", - "㋊": "ll月", - "㍣": "ll点", - "Ю": "lO", - "⒑": "lO.", - "㏩": "lO日", - "㋉": "lO月", - "㍢": "lO点", - "ʪ": "ls", - "₶": "lt", - "Ⅳ": "lV", - "Ⅸ": "lX", - "ɮ": "lȝ", - "ʫ": "lz", - "أ": "lٴ", - "ﺄ": "lٴ", - "ﺃ": "lٴ", - "ٲ": "lٴ", - "ٵ": "lٴ", - "ﷳ": "lكبر", - "ﷲ": "lللّٰo", - "㏠": "l日", - "㋀": "l月", - "㍙": "l点", - "ⳑ": "ʟ", - "ꮮ": "ʟ", - "𐑃": "ʟ", - "M": "M", - "Ⅿ": "M", - "ℳ": "M", - "𝐌": "M", - "𝑀": "M", - "𝑴": "M", - "𝓜": "M", - "𝔐": "M", - "𝕄": "M", - "𝕸": "M", - "𝖬": "M", - "𝗠": "M", - "𝘔": "M", - "𝙈": "M", - "𝙼": "M", - "Μ": "M", - "𝚳": "M", - "𝛭": "M", - "𝜧": "M", - "𝝡": "M", - "𝞛": "M", - "Ϻ": "M", - "Ⲙ": "M", - "М": "M", - "Ꮇ": "M", - "ᗰ": "M", - "ᛖ": "M", - "ꓟ": "M", - "𐊰": "M", - "𐌑": "M", - "Ӎ": "M̦", - "🝫": "MB", - "ⷨ": "ᷟ", - "𝐧": "n", - "𝑛": "n", - "𝒏": "n", - "𝓃": "n", - "𝓷": "n", - "𝔫": "n", - "𝕟": "n", - "𝖓": "n", - "𝗇": "n", - "𝗻": "n", - "𝘯": "n", - "𝙣": "n", - "𝚗": "n", - "ո": "n", - "ռ": "n", - "N": "N", - "ℕ": "N", - "𝐍": "N", - "𝑁": "N", - "𝑵": "N", - "𝒩": "N", - "𝓝": "N", - "𝔑": "N", - "𝕹": "N", - "𝖭": "N", - "𝗡": "N", - "𝘕": "N", - "𝙉": "N", - "𝙽": "N", - "Ν": "N", - "𝚴": "N", - "𝛮": "N", - "𝜨": "N", - "𝝢": "N", - "𝞜": "N", - "Ⲛ": "N", - "ꓠ": "N", - "𐔓": "N", - "𐆎": "N̊", - "ɳ": "n̨", - "ƞ": "n̩", - "η": "n̩", - "𝛈": "n̩", - "𝜂": "n̩", - "𝜼": "n̩", - "𝝶": "n̩", - "𝞰": "n̩", - "Ɲ": "N̦", - "ᵰ": "n̴", - "nj": "nj", - "Nj": "Nj", - "NJ": "NJ", - "№": "No", - "ͷ": "ᴎ", - "и": "ᴎ", - "𐑍": "ᴎ", - "ņ": "ɲ", - "ం": "o", - "ಂ": "o", - "ം": "o", - "ං": "o", - "०": "o", - "੦": "o", - "૦": "o", - "௦": "o", - "౦": "o", - "೦": "o", - "൦": "o", - "๐": "o", - "໐": "o", - "၀": "o", - "٥": "o", - "۵": "o", - "o": "o", - "ℴ": "o", - "𝐨": "o", - "𝑜": "o", - "𝒐": "o", - "𝓸": "o", - "𝔬": "o", - "𝕠": "o", - "𝖔": "o", - "𝗈": "o", - "𝗼": "o", - "𝘰": "o", - "𝙤": "o", - "𝚘": "o", - "ᴏ": "o", - "ᴑ": "o", - "ꬽ": "o", - "ο": "o", - "𝛐": "o", - "𝜊": "o", - "𝝄": "o", - "𝝾": "o", - "𝞸": "o", - "σ": "o", - "𝛔": "o", - "𝜎": "o", - "𝝈": "o", - "𝞂": "o", - "𝞼": "o", - "ⲟ": "o", - "о": "o", - "ჿ": "o", - "օ": "o", - "ס": "o", - "ه": "o", - "𞸤": "o", - "𞹤": "o", - "𞺄": "o", - "ﻫ": "o", - "ﻬ": "o", - "ﻪ": "o", - "ﻩ": "o", - "ھ": "o", - "ﮬ": "o", - "ﮭ": "o", - "ﮫ": "o", - "ﮪ": "o", - "ہ": "o", - "ﮨ": "o", - "ﮩ": "o", - "ﮧ": "o", - "ﮦ": "o", - "ە": "o", - "ഠ": "o", - "ဝ": "o", - "𐓪": "o", - "𑣈": "o", - "𑣗": "o", - "𐐬": "o", - "߀": "O", - "০": "O", - "୦": "O", - "〇": "O", - "𑓐": "O", - "𑣠": "O", - "𝟎": "O", - "𝟘": "O", - "𝟢": "O", - "𝟬": "O", - "𝟶": "O", - "🯰": "O", - "O": "O", - "𝐎": "O", - "𝑂": "O", - "𝑶": "O", - "𝒪": "O", - "𝓞": "O", - "𝔒": "O", - "𝕆": "O", - "𝕺": "O", - "𝖮": "O", - "𝗢": "O", - "𝘖": "O", - "𝙊": "O", - "𝙾": "O", - "Ο": "O", - "𝚶": "O", - "𝛰": "O", - "𝜪": "O", - "𝝤": "O", - "𝞞": "O", - "Ⲟ": "O", - "О": "O", - "Օ": "O", - "ⵔ": "O", - "ዐ": "O", - "ଠ": "O", - "𐓂": "O", - "ꓳ": "O", - "𑢵": "O", - "𐊒": "O", - "𐊫": "O", - "𐐄": "O", - "𐔖": "O", - "⁰": "º", - "ᵒ": "º", - "ǒ": "ŏ", - "Ǒ": "Ŏ", - "ۿ": "ô", - "Ő": "Ö", - "ø": "o̸", - "ꬾ": "o̸", - "Ø": "O̸", - "ⵁ": "O̸", - "Ǿ": "Ó̸", - "ɵ": "o̵", - "ꝋ": "o̵", - "ө": "o̵", - "ѳ": "o̵", - "ꮎ": "o̵", - "ꮻ": "o̵", - "⊖": "O̵", - "⊝": "O̵", - "⍬": "O̵", - "𝈚": "O̵", - "🜔": "O̵", - "Ɵ": "O̵", - "Ꝋ": "O̵", - "θ": "O̵", - "ϑ": "O̵", - "𝛉": "O̵", - "𝛝": "O̵", - "𝜃": "O̵", - "𝜗": "O̵", - "𝜽": "O̵", - "𝝑": "O̵", - "𝝷": "O̵", - "𝞋": "O̵", - "𝞱": "O̵", - "𝟅": "O̵", - "Θ": "O̵", - "ϴ": "O̵", - "𝚯": "O̵", - "𝚹": "O̵", - "𝛩": "O̵", - "𝛳": "O̵", - "𝜣": "O̵", - "𝜭": "O̵", - "𝝝": "O̵", - "𝝧": "O̵", - "𝞗": "O̵", - "𝞡": "O̵", - "Ө": "O̵", - "Ѳ": "O̵", - "ⴱ": "O̵", - "Ꮎ": "O̵", - "Ꮻ": "O̵", - "ꭴ": "ơ", - "ﳙ": "oٰ", - "🄁": "O,", - "🄀": "O.", - "ơ": "o'", - "Ơ": "O'", - "Ꭴ": "O'", - "%": "º/₀", - "٪": "º/₀", - "⁒": "º/₀", - "‰": "º/₀₀", - "؉": "º/₀₀", - "‱": "º/₀₀₀", - "؊": "º/₀₀₀", - "œ": "oe", - "Œ": "OE", - "ɶ": "oᴇ", - "∞": "oo", - "ꝏ": "oo", - "ꚙ": "oo", - "Ꝏ": "OO", - "Ꚙ": "OO", - "ﳗ": "oج", - "ﱑ": "oج", - "ﳘ": "oم", - "ﱒ": "oم", - "ﶓ": "oمج", - "ﶔ": "oمم", - "ﱓ": "oى", - "ﱔ": "oى", - "ൟ": "oരo", - "တ": "oာ", - "㍘": "O点", - "ↄ": "ɔ", - "ᴐ": "ɔ", - "ͻ": "ɔ", - "𐑋": "ɔ", - "Ↄ": "Ɔ", - "Ͻ": "Ɔ", - "ꓛ": "Ɔ", - "𐐣": "Ɔ", - "ꬿ": "ɔ̸", - "ꭢ": "ɔe", - "𐐿": "ɷ", - "⍴": "p", - "p": "p", - "𝐩": "p", - "𝑝": "p", - "𝒑": "p", - "𝓅": "p", - "𝓹": "p", - "𝔭": "p", - "𝕡": "p", - "𝖕": "p", - "𝗉": "p", - "𝗽": "p", - "𝘱": "p", - "𝙥": "p", - "𝚙": "p", - "ρ": "p", - "ϱ": "p", - "𝛒": "p", - "𝛠": "p", - "𝜌": "p", - "𝜚": "p", - "𝝆": "p", - "𝝔": "p", - "𝞀": "p", - "𝞎": "p", - "𝞺": "p", - "𝟈": "p", - "ⲣ": "p", - "р": "p", - "P": "P", - "ℙ": "P", - "𝐏": "P", - "𝑃": "P", - "𝑷": "P", - "𝒫": "P", - "𝓟": "P", - "𝔓": "P", - "𝕻": "P", - "𝖯": "P", - "𝗣": "P", - "𝘗": "P", - "𝙋": "P", - "𝙿": "P", - "Ρ": "P", - "𝚸": "P", - "𝛲": "P", - "𝜬": "P", - "𝝦": "P", - "𝞠": "P", - "Ⲣ": "P", - "Р": "P", - "Ꮲ": "P", - "ᑭ": "P", - "ꓑ": "P", - "𐊕": "P", - "ƥ": "p̔", - "ᵽ": "p̵", - "ᑷ": "p·", - "ᒆ": "P'", - "ᴩ": "ᴘ", - "ꮲ": "ᴘ", - "φ": "ɸ", - "ϕ": "ɸ", - "𝛗": "ɸ", - "𝛟": "ɸ", - "𝜑": "ɸ", - "𝜙": "ɸ", - "𝝋": "ɸ", - "𝝓": "ɸ", - "𝞅": "ɸ", - "𝞍": "ɸ", - "𝞿": "ɸ", - "𝟇": "ɸ", - "ⲫ": "ɸ", - "ф": "ɸ", - "𝐪": "q", - "𝑞": "q", - "𝒒": "q", - "𝓆": "q", - "𝓺": "q", - "𝔮": "q", - "𝕢": "q", - "𝖖": "q", - "𝗊": "q", - "𝗾": "q", - "𝘲": "q", - "𝙦": "q", - "𝚚": "q", - "ԛ": "q", - "գ": "q", - "զ": "q", - "ℚ": "Q", - "𝐐": "Q", - "𝑄": "Q", - "𝑸": "Q", - "𝒬": "Q", - "𝓠": "Q", - "𝔔": "Q", - "𝕼": "Q", - "𝖰": "Q", - "𝗤": "Q", - "𝘘": "Q", - "𝙌": "Q", - "𝚀": "Q", - "ⵕ": "Q", - "ʠ": "q̔", - "🜀": "QE", - "ᶐ": "ɋ", - "ᴋ": "ĸ", - "κ": "ĸ", - "ϰ": "ĸ", - "𝛋": "ĸ", - "𝛞": "ĸ", - "𝜅": "ĸ", - "𝜘": "ĸ", - "𝜿": "ĸ", - "𝝒": "ĸ", - "𝝹": "ĸ", - "𝞌": "ĸ", - "𝞳": "ĸ", - "𝟆": "ĸ", - "ⲕ": "ĸ", - "к": "ĸ", - "ꮶ": "ĸ", - "қ": "ĸ̩", - "ҟ": "ĸ̵", - "𝐫": "r", - "𝑟": "r", - "𝒓": "r", - "𝓇": "r", - "𝓻": "r", - "𝔯": "r", - "𝕣": "r", - "𝖗": "r", - "𝗋": "r", - "𝗿": "r", - "𝘳": "r", - "𝙧": "r", - "𝚛": "r", - "ꭇ": "r", - "ꭈ": "r", - "ᴦ": "r", - "ⲅ": "r", - "г": "r", - "ꮁ": "r", - "𝈖": "R", - "ℛ": "R", - "ℜ": "R", - "ℝ": "R", - "𝐑": "R", - "𝑅": "R", - "𝑹": "R", - "𝓡": "R", - "𝕽": "R", - "𝖱": "R", - "𝗥": "R", - "𝘙": "R", - "𝙍": "R", - "𝚁": "R", - "Ʀ": "R", - "Ꭱ": "R", - "Ꮢ": "R", - "𐒴": "R", - "ᖇ": "R", - "ꓣ": "R", - "𖼵": "R", - "ɽ": "r̨", - "ɼ": "r̩", - "ɍ": "r̵", - "ғ": "r̵", - "ᵲ": "r̴", - "ґ": "r'", - "𑣣": "rn", - "m": "rn", - "ⅿ": "rn", - "𝐦": "rn", - "𝑚": "rn", - "𝒎": "rn", - "𝓂": "rn", - "𝓶": "rn", - "𝔪": "rn", - "𝕞": "rn", - "𝖒": "rn", - "𝗆": "rn", - "𝗺": "rn", - "𝘮": "rn", - "𝙢": "rn", - "𝚖": "rn", - "𑜀": "rn", - "₥": "rn̸", - "ɱ": "rn̦", - "ᵯ": "rn̴", - "₨": "Rs", - "ꭱ": "ʀ", - "ꮢ": "ʀ", - "я": "ᴙ", - "ᵳ": "ɾ̴", - "℩": "ɿ", - "s": "s", - "𝐬": "s", - "𝑠": "s", - "𝒔": "s", - "𝓈": "s", - "𝓼": "s", - "𝔰": "s", - "𝕤": "s", - "𝖘": "s", - "𝗌": "s", - "𝘀": "s", - "𝘴": "s", - "𝙨": "s", - "𝚜": "s", - "ꜱ": "s", - "ƽ": "s", - "ѕ": "s", - "ꮪ": "s", - "𑣁": "s", - "𐑈": "s", - "S": "S", - "𝐒": "S", - "𝑆": "S", - "𝑺": "S", - "𝒮": "S", - "𝓢": "S", - "𝔖": "S", - "𝕊": "S", - "𝕾": "S", - "𝖲": "S", - "𝗦": "S", - "𝘚": "S", - "𝙎": "S", - "𝚂": "S", - "Ѕ": "S", - "Տ": "S", - "Ꮥ": "S", - "Ꮪ": "S", - "ꓢ": "S", - "𖼺": "S", - "𐊖": "S", - "𐐠": "S", - "ʂ": "s̨", - "ᵴ": "s̴", - "ꞵ": "ß", - "β": "ß", - "ϐ": "ß", - "𝛃": "ß", - "𝛽": "ß", - "𝜷": "ß", - "𝝱": "ß", - "𝞫": "ß", - "Ᏸ": "ß", - "🝜": "sss", - "st": "st", - "∫": "ʃ", - "ꭍ": "ʃ", - "∑": "Ʃ", - "⅀": "Ʃ", - "Σ": "Ʃ", - "𝚺": "Ʃ", - "𝛴": "Ʃ", - "𝜮": "Ʃ", - "𝝨": "Ʃ", - "𝞢": "Ʃ", - "ⵉ": "Ʃ", - "∬": "ʃʃ", - "∭": "ʃʃʃ", - "⨌": "ʃʃʃʃ", - "𝐭": "t", - "𝑡": "t", - "𝒕": "t", - "𝓉": "t", - "𝓽": "t", - "𝔱": "t", - "𝕥": "t", - "𝖙": "t", - "𝗍": "t", - "𝘁": "t", - "𝘵": "t", - "𝙩": "t", - "𝚝": "t", - "⊤": "T", - "⟙": "T", - "🝨": "T", - "T": "T", - "𝐓": "T", - "𝑇": "T", - "𝑻": "T", - "𝒯": "T", - "𝓣": "T", - "𝔗": "T", - "𝕋": "T", - "𝕿": "T", - "𝖳": "T", - "𝗧": "T", - "𝘛": "T", - "𝙏": "T", - "𝚃": "T", - "Τ": "T", - "𝚻": "T", - "𝛵": "T", - "𝜯": "T", - "𝝩": "T", - "𝞣": "T", - "Ⲧ": "T", - "Т": "T", - "Ꭲ": "T", - "ꓔ": "T", - "𖼊": "T", - "𑢼": "T", - "𐊗": "T", - "𐊱": "T", - "𐌕": "T", - "ƭ": "t̔", - "⍡": "T̈", - "Ⱦ": "T̸", - "Ț": "Ţ", - "Ʈ": "T̨", - "Ҭ": "T̩", - "₮": "T⃫", - "ŧ": "t̵", - "Ŧ": "T̵", - "ᵵ": "t̴", - "Ⴀ": "Ꞇ", - "Ꜩ": "T3", - "ʨ": "tɕ", - "℡": "TEL", - "ꝷ": "tf", - "ʦ": "ts", - "ʧ": "tʃ", - "ꜩ": "tȝ", - "τ": "ᴛ", - "𝛕": "ᴛ", - "𝜏": "ᴛ", - "𝝉": "ᴛ", - "𝞃": "ᴛ", - "𝞽": "ᴛ", - "т": "ᴛ", - "ꭲ": "ᴛ", - "ҭ": "ᴛ̩", - "ţ": "ƫ", - "ț": "ƫ", - "Ꮏ": "ƫ", - "𝐮": "u", - "𝑢": "u", - "𝒖": "u", - "𝓊": "u", - "𝓾": "u", - "𝔲": "u", - "𝕦": "u", - "𝖚": "u", - "𝗎": "u", - "𝘂": "u", - "𝘶": "u", - "𝙪": "u", - "𝚞": "u", - "ꞟ": "u", - "ᴜ": "u", - "ꭎ": "u", - "ꭒ": "u", - "ʋ": "u", - "υ": "u", - "𝛖": "u", - "𝜐": "u", - "𝝊": "u", - "𝞄": "u", - "𝞾": "u", - "ս": "u", - "𐓶": "u", - "𑣘": "u", - "∪": "U", - "⋃": "U", - "𝐔": "U", - "𝑈": "U", - "𝑼": "U", - "𝒰": "U", - "𝓤": "U", - "𝔘": "U", - "𝕌": "U", - "𝖀": "U", - "𝖴": "U", - "𝗨": "U", - "𝘜": "U", - "𝙐": "U", - "𝚄": "U", - "Ս": "U", - "ሀ": "U", - "𐓎": "U", - "ᑌ": "U", - "ꓴ": "U", - "𖽂": "U", - "𑢸": "U", - "ǔ": "ŭ", - "Ǔ": "Ŭ", - "ᵾ": "u̵", - "ꮜ": "u̵", - "Ʉ": "U̵", - "Ꮜ": "U̵", - "ᑘ": "U·", - "ᑧ": "U'", - "ᵫ": "ue", - "ꭣ": "uo", - "ṃ": "ꭑ", - "պ": "ɰ", - "ሣ": "ɰ", - "℧": "Ʊ", - "ᘮ": "Ʊ", - "ᘴ": "Ʊ", - "ᵿ": "ʊ̵", - "∨": "v", - "⋁": "v", - "v": "v", - "ⅴ": "v", - "𝐯": "v", - "𝑣": "v", - "𝒗": "v", - "𝓋": "v", - "𝓿": "v", - "𝔳": "v", - "𝕧": "v", - "𝖛": "v", - "𝗏": "v", - "𝘃": "v", - "𝘷": "v", - "𝙫": "v", - "𝚟": "v", - "ᴠ": "v", - "ν": "v", - "𝛎": "v", - "𝜈": "v", - "𝝂": "v", - "𝝼": "v", - "𝞶": "v", - "ѵ": "v", - "ט": "v", - "𑜆": "v", - "ꮩ": "v", - "𑣀": "v", - "𝈍": "V", - "٧": "V", - "۷": "V", - "Ⅴ": "V", - "𝐕": "V", - "𝑉": "V", - "𝑽": "V", - "𝒱": "V", - "𝓥": "V", - "𝔙": "V", - "𝕍": "V", - "𝖁": "V", - "𝖵": "V", - "𝗩": "V", - "𝘝": "V", - "𝙑": "V", - "𝚅": "V", - "Ѵ": "V", - "ⴸ": "V", - "Ꮩ": "V", - "ᐯ": "V", - "ꛟ": "V", - "ꓦ": "V", - "𖼈": "V", - "𑢠": "V", - "𐔝": "V", - "𐆗": "V̵", - "ᐻ": "V·", - "🝬": "VB", - "ⅵ": "vi", - "ⅶ": "vii", - "ⅷ": "viii", - "Ⅵ": "Vl", - "Ⅶ": "Vll", - "Ⅷ": "Vlll", - "🜈": "Vᷤ", - "ᴧ": "ʌ", - "𐓘": "ʌ", - "٨": "Ʌ", - "۸": "Ʌ", - "Λ": "Ʌ", - "𝚲": "Ʌ", - "𝛬": "Ʌ", - "𝜦": "Ʌ", - "𝝠": "Ʌ", - "𝞚": "Ʌ", - "Л": "Ʌ", - "ⴷ": "Ʌ", - "𐒰": "Ʌ", - "ᐱ": "Ʌ", - "ꛎ": "Ʌ", - "ꓥ": "Ʌ", - "𖼽": "Ʌ", - "𐊍": "Ʌ", - "Ӆ": "Ʌ̦", - "ᐽ": "Ʌ·", - "ɯ": "w", - "𝐰": "w", - "𝑤": "w", - "𝒘": "w", - "𝓌": "w", - "𝔀": "w", - "𝔴": "w", - "𝕨": "w", - "𝖜": "w", - "𝗐": "w", - "𝘄": "w", - "𝘸": "w", - "𝙬": "w", - "𝚠": "w", - "ᴡ": "w", - "ѡ": "w", - "ԝ": "w", - "ա": "w", - "𑜊": "w", - "𑜎": "w", - "𑜏": "w", - "ꮃ": "w", - "𑣯": "W", - "𑣦": "W", - "𝐖": "W", - "𝑊": "W", - "𝑾": "W", - "𝒲": "W", - "𝓦": "W", - "𝔚": "W", - "𝕎": "W", - "𝖂": "W", - "𝖶": "W", - "𝗪": "W", - "𝘞": "W", - "𝙒": "W", - "𝚆": "W", - "Ԝ": "W", - "Ꮃ": "W", - "Ꮤ": "W", - "ꓪ": "W", - "ѽ": "w҆҇", - "𑓅": "ẇ", - "₩": "W̵", - "ꝡ": "w̦", - "ᴍ": "ʍ", - "м": "ʍ", - "ꮇ": "ʍ", - "ӎ": "ʍ̦", - "᙮": "x", - "×": "x", - "⤫": "x", - "⤬": "x", - "⨯": "x", - "x": "x", - "ⅹ": "x", - "𝐱": "x", - "𝑥": "x", - "𝒙": "x", - "𝓍": "x", - "𝔁": "x", - "𝔵": "x", - "𝕩": "x", - "𝖝": "x", - "𝗑": "x", - "𝘅": "x", - "𝘹": "x", - "𝙭": "x", - "𝚡": "x", - "х": "x", - "ᕁ": "x", - "ᕽ": "x", - "ⷯ": "ͯ", - "᙭": "X", - "╳": "X", - "𐌢": "X", - "𑣬": "X", - "X": "X", - "Ⅹ": "X", - "𝐗": "X", - "𝑋": "X", - "𝑿": "X", - "𝒳": "X", - "𝓧": "X", - "𝔛": "X", - "𝕏": "X", - "𝖃": "X", - "𝖷": "X", - "𝗫": "X", - "𝘟": "X", - "𝙓": "X", - "𝚇": "X", - "Ꭓ": "X", - "Χ": "X", - "𝚾": "X", - "𝛸": "X", - "𝜲": "X", - "𝝬": "X", - "𝞦": "X", - "Ⲭ": "X", - "Х": "X", - "ⵝ": "X", - "ᚷ": "X", - "ꓫ": "X", - "𐊐": "X", - "𐊴": "X", - "𐌗": "X", - "𐔧": "X", - "⨰": "ẋ", - "Ҳ": "X̩", - "𐆖": "X̵", - "ⅺ": "xi", - "ⅻ": "xii", - "Ⅺ": "Xl", - "Ⅻ": "Xll", - "ɣ": "y", - "ᶌ": "y", - "y": "y", - "𝐲": "y", - "𝑦": "y", - "𝒚": "y", - "𝓎": "y", - "𝔂": "y", - "𝔶": "y", - "𝕪": "y", - "𝖞": "y", - "𝗒": "y", - "𝘆": "y", - "𝘺": "y", - "𝙮": "y", - "𝚢": "y", - "ʏ": "y", - "ỿ": "y", - "ꭚ": "y", - "γ": "y", - "ℽ": "y", - "𝛄": "y", - "𝛾": "y", - "𝜸": "y", - "𝝲": "y", - "𝞬": "y", - "у": "y", - "ү": "y", - "ყ": "y", - "𑣜": "y", - "Y": "Y", - "𝐘": "Y", - "𝑌": "Y", - "𝒀": "Y", - "𝒴": "Y", - "𝓨": "Y", - "𝔜": "Y", - "𝕐": "Y", - "𝖄": "Y", - "𝖸": "Y", - "𝗬": "Y", - "𝘠": "Y", - "𝙔": "Y", - "𝚈": "Y", - "Υ": "Y", - "ϒ": "Y", - "𝚼": "Y", - "𝛶": "Y", - "𝜰": "Y", - "𝝪": "Y", - "𝞤": "Y", - "Ⲩ": "Y", - "У": "Y", - "Ү": "Y", - "Ꭹ": "Y", - "Ꮍ": "Y", - "ꓬ": "Y", - "𖽃": "Y", - "𑢤": "Y", - "𐊲": "Y", - "ƴ": "y̔", - "ɏ": "y̵", - "ұ": "y̵", - "¥": "Y̵", - "Ɏ": "Y̵", - "Ұ": "Y̵", - "ʒ": "ȝ", - "ꝫ": "ȝ", - "ⳍ": "ȝ", - "ӡ": "ȝ", - "ჳ": "ȝ", - "𝐳": "z", - "𝑧": "z", - "𝒛": "z", - "𝓏": "z", - "𝔃": "z", - "𝔷": "z", - "𝕫": "z", - "𝖟": "z", - "𝗓": "z", - "𝘇": "z", - "𝘻": "z", - "𝙯": "z", - "𝚣": "z", - "ᴢ": "z", - "ꮓ": "z", - "𑣄": "z", - "𐋵": "Z", - "𑣥": "Z", - "Z": "Z", - "ℤ": "Z", - "ℨ": "Z", - "𝐙": "Z", - "𝑍": "Z", - "𝒁": "Z", - "𝒵": "Z", - "𝓩": "Z", - "𝖅": "Z", - "𝖹": "Z", - "𝗭": "Z", - "𝘡": "Z", - "𝙕": "Z", - "𝚉": "Z", - "Ζ": "Z", - "𝚭": "Z", - "𝛧": "Z", - "𝜡": "Z", - "𝝛": "Z", - "𝞕": "Z", - "Ꮓ": "Z", - "ꓜ": "Z", - "𑢩": "Z", - "ʐ": "z̨", - "ƶ": "z̵", - "Ƶ": "Z̵", - "ȥ": "z̦", - "Ȥ": "Z̦", - "ᵶ": "z̴", - "ƿ": "þ", - "ϸ": "þ", - "Ϸ": "Þ", - "𐓄": "Þ", - "⁹": "ꝰ", - "ᴤ": "ƨ", - "ϩ": "ƨ", - "ꙅ": "ƨ", - "ь": "ƅ", - "ꮟ": "ƅ", - "ы": "ƅi", - "ꭾ": "ɂ", - "ˤ": "ˁ", - "ꛍ": "ʡ", - "⊙": "ʘ", - "☉": "ʘ", - "⨀": "ʘ", - "Ꙩ": "ʘ", - "ⵙ": "ʘ", - "𐓃": "ʘ", - "ℾ": "Γ", - "𝚪": "Γ", - "𝛤": "Γ", - "𝜞": "Γ", - "𝝘": "Γ", - "𝞒": "Γ", - "Ⲅ": "Γ", - "Г": "Γ", - "Ꮁ": "Γ", - "ᒥ": "Γ", - "𖼇": "Γ", - "Ғ": "Γ̵", - "ᒯ": "Γ·", - "Ґ": "Γ'", - "∆": "Δ", - "△": "Δ", - "🜂": "Δ", - "𝚫": "Δ", - "𝛥": "Δ", - "𝜟": "Δ", - "𝝙": "Δ", - "𝞓": "Δ", - "Ⲇ": "Δ", - "ⵠ": "Δ", - "ᐃ": "Δ", - "𖼚": "Δ", - "𐊅": "Δ", - "𐊣": "Δ", - "⍙": "Δ̲", - "ᐏ": "Δ·", - "ᐬ": "Δᐠ", - "𝟋": "ϝ", - "𝛇": "ζ", - "𝜁": "ζ", - "𝜻": "ζ", - "𝝵": "ζ", - "𝞯": "ζ", - "ⳤ": "ϗ", - "𝛌": "λ", - "𝜆": "λ", - "𝝀": "λ", - "𝝺": "λ", - "𝞴": "λ", - "Ⲗ": "λ", - "𐓛": "λ", - "µ": "μ", - "𝛍": "μ", - "𝜇": "μ", - "𝝁": "μ", - "𝝻": "μ", - "𝞵": "μ", - "𝛏": "ξ", - "𝜉": "ξ", - "𝝃": "ξ", - "𝝽": "ξ", - "𝞷": "ξ", - "𝚵": "Ξ", - "𝛯": "Ξ", - "𝜩": "Ξ", - "𝝣": "Ξ", - "𝞝": "Ξ", - "ϖ": "π", - "ℼ": "π", - "𝛑": "π", - "𝛡": "π", - "𝜋": "π", - "𝜛": "π", - "𝝅": "π", - "𝝕": "π", - "𝝿": "π", - "𝞏": "π", - "𝞹": "π", - "𝟉": "π", - "ᴨ": "π", - "п": "π", - "∏": "Π", - "ℿ": "Π", - "𝚷": "Π", - "𝛱": "Π", - "𝜫": "Π", - "𝝥": "Π", - "𝞟": "Π", - "Ⲡ": "Π", - "П": "Π", - "ꛛ": "Π", - "𐊭": "Ϙ", - "𐌒": "Ϙ", - "ϛ": "ς", - "𝛓": "ς", - "𝜍": "ς", - "𝝇": "ς", - "𝞁": "ς", - "𝞻": "ς", - "𝚽": "Φ", - "𝛷": "Φ", - "𝜱": "Φ", - "𝝫": "Φ", - "𝞥": "Φ", - "Ⲫ": "Φ", - "Ф": "Φ", - "Փ": "Φ", - "ቀ": "Φ", - "ᛰ": "Φ", - "𐊳": "Φ", - "ꭓ": "χ", - "ꭕ": "χ", - "𝛘": "χ", - "𝜒": "χ", - "𝝌": "χ", - "𝞆": "χ", - "𝟀": "χ", - "ⲭ": "χ", - "𝛙": "ψ", - "𝜓": "ψ", - "𝝍": "ψ", - "𝞇": "ψ", - "𝟁": "ψ", - "ѱ": "ψ", - "𐓹": "ψ", - "𝚿": "Ψ", - "𝛹": "Ψ", - "𝜳": "Ψ", - "𝝭": "Ψ", - "𝞧": "Ψ", - "Ⲯ": "Ψ", - "Ѱ": "Ψ", - "𐓑": "Ψ", - "ᛘ": "Ψ", - "𐊵": "Ψ", - "⍵": "ω", - "ꞷ": "ω", - "𝛚": "ω", - "𝜔": "ω", - "𝝎": "ω", - "𝞈": "ω", - "𝟂": "ω", - "ⲱ": "ω", - "ꙍ": "ω", - "Ω": "Ω", - "𝛀": "Ω", - "𝛺": "Ω", - "𝜴": "Ω", - "𝝮": "Ω", - "𝞨": "Ω", - "ᘯ": "Ω", - "ᘵ": "Ω", - "𐊶": "Ω", - "⍹": "ω̲", - "ώ": "ῴ", - "☰": "Ⲷ", - "Ⳝ": "Ϭ", - "җ": "ж̩", - "Җ": "Ж̩", - "𝈋": "И", - "Ͷ": "И", - "ꚡ": "И", - "𐐥": "И", - "Й": "Ѝ", - "Ҋ": "Ѝ̦", - "ѝ": "й", - "ҋ": "й̦", - "𐒼": "Ӄ", - "ᴫ": "л", - "ӆ": "л̦", - "ꭠ": "љ", - "𐓫": "ꙩ", - "ᷮ": "ⷬ", - "𐓍": "Ћ", - "𝈂": "Ӿ", - "𝈢": "Ѡ", - "Ꮗ": "Ѡ", - "ᗯ": "Ѡ", - "Ѽ": "Ѡ҆҇", - "ᣭ": "Ѡ·", - "Ꞷ": "Ꙍ", - "ӌ": "ҷ", - "Ӌ": "Ҷ", - "Ҿ": "Ҽ̨", - "ⲽ": "ш", - "Ⲽ": "Ш", - "Ꙑ": "Ъl", - "℈": "Э", - "🜁": "Ꙙ", - "𖼜": "Ꙙ", - "ꦒ": "ⰿ", - "և": "եւ", - "ኔ": "ձ", - "ﬔ": "մե", - "ﬕ": "մի", - "ﬗ": "մխ", - "ﬓ": "մն", - "∩": "Ո", - "⋂": "Ո", - "𝉅": "Ո", - "በ": "Ո", - "ᑎ": "Ո", - "ꓵ": "Ո", - "ᑚ": "Ո·", - "ᑨ": "Ո'", - "ﬖ": "վն", - "₽": "Ք", - "˓": "ՙ", - "ʿ": "ՙ", - "ℵ": "א", - "ﬡ": "א", - "אָ": "אַ", - "אּ": "אַ", - "ﭏ": "אל", - "ℶ": "ב", - "ℷ": "ג", - "ℸ": "ד", - "ﬢ": "ד", - "ﬣ": "ה", - "יּ": "יִ", - "ﬤ": "כ", - "ﬥ": "ל", - "ﬦ": "ם", - "ﬠ": "ע", - "ﬧ": "ר", - "שׂ": "שׁ", - "שּ": "שׁ", - "שּׂ": "שּׁ", - "ﬨ": "ת", - "ﺀ": "ء", - "۽": "ء͈", - "ﺂ": "آ", - "ﺁ": "آ", - "ﭑ": "ٱ", - "ﭐ": "ٱ", - "𞸁": "ب", - "𞸡": "ب", - "𞹡": "ب", - "𞺁": "ب", - "𞺡": "ب", - "ﺑ": "ب", - "ﺒ": "ب", - "ﺐ": "ب", - "ﺏ": "ب", - "ݑ": "بۛ", - "ࢶ": "بۢ", - "ࢡ": "بٔ", - "ﲠ": "بo", - "ﳢ": "بo", - "ﲜ": "بج", - "ﰅ": "بج", - "ﲝ": "بح", - "ﰆ": "بح", - "ﷂ": "بحى", - "ﲞ": "بخ", - "ﰇ": "بخ", - "ﳒ": "بخ", - "ﱋ": "بخ", - "ﶞ": "بخى", - "ﱪ": "بر", - "ﱫ": "بز", - "ﲟ": "بم", - "ﳡ": "بم", - "ﱬ": "بم", - "ﰈ": "بم", - "ﱭ": "بن", - "ﱮ": "بى", - "ﰉ": "بى", - "ﱯ": "بى", - "ﰊ": "بى", - "ﭔ": "ٻ", - "ﭕ": "ٻ", - "ﭓ": "ٻ", - "ﭒ": "ٻ", - "ې": "ٻ", - "ﯦ": "ٻ", - "ﯧ": "ٻ", - "ﯥ": "ٻ", - "ﯤ": "ٻ", - "ﭜ": "ڀ", - "ﭝ": "ڀ", - "ﭛ": "ڀ", - "ﭚ": "ڀ", - "ࢩ": "ݔ", - "ݧ": "ݔ", - "⍥": "ة", - "ö": "ة", - "ﺔ": "ة", - "ﺓ": "ة", - "ۃ": "ة", - "𞸕": "ت", - "𞸵": "ت", - "𞹵": "ت", - "𞺕": "ت", - "𞺵": "ت", - "ﺗ": "ت", - "ﺘ": "ت", - "ﺖ": "ت", - "ﺕ": "ت", - "ﲥ": "تo", - "ﳤ": "تo", - "ﲡ": "تج", - "ﰋ": "تج", - "ﵐ": "تجم", - "ﶠ": "تجى", - "ﶟ": "تجى", - "ﲢ": "تح", - "ﰌ": "تح", - "ﵒ": "تحج", - "ﵑ": "تحج", - "ﵓ": "تحم", - "ﲣ": "تخ", - "ﰍ": "تخ", - "ﵔ": "تخم", - "ﶢ": "تخى", - "ﶡ": "تخى", - "ﱰ": "تر", - "ﱱ": "تز", - "ﲤ": "تم", - "ﳣ": "تم", - "ﱲ": "تم", - "ﰎ": "تم", - "ﵕ": "تمج", - "ﵖ": "تمح", - "ﵗ": "تمخ", - "ﶤ": "تمى", - "ﶣ": "تمى", - "ﱳ": "تن", - "ﱴ": "تى", - "ﰏ": "تى", - "ﱵ": "تى", - "ﰐ": "تى", - "ﭠ": "ٺ", - "ﭡ": "ٺ", - "ﭟ": "ٺ", - "ﭞ": "ٺ", - "ﭤ": "ٿ", - "ﭥ": "ٿ", - "ﭣ": "ٿ", - "ﭢ": "ٿ", - "𞸂": "ج", - "𞸢": "ج", - "𞹂": "ج", - "𞹢": "ج", - "𞺂": "ج", - "𞺢": "ج", - "ﺟ": "ج", - "ﺠ": "ج", - "ﺞ": "ج", - "ﺝ": "ج", - "ﲧ": "جح", - "ﰕ": "جح", - "ﶦ": "جحى", - "ﶾ": "جحى", - "ﷻ": "جل جلlلo", - "ﲨ": "جم", - "ﰖ": "جم", - "ﵙ": "جمح", - "ﵘ": "جمح", - "ﶧ": "جمى", - "ﶥ": "جمى", - "ﴝ": "جى", - "ﴁ": "جى", - "ﴞ": "جى", - "ﴂ": "جى", - "ﭸ": "ڃ", - "ﭹ": "ڃ", - "ﭷ": "ڃ", - "ﭶ": "ڃ", - "ﭴ": "ڄ", - "ﭵ": "ڄ", - "ﭳ": "ڄ", - "ﭲ": "ڄ", - "ﭼ": "چ", - "ﭽ": "چ", - "ﭻ": "چ", - "ﭺ": "چ", - "ﮀ": "ڇ", - "ﮁ": "ڇ", - "ﭿ": "ڇ", - "ﭾ": "ڇ", - "𞸇": "ح", - "𞸧": "ح", - "𞹇": "ح", - "𞹧": "ح", - "𞺇": "ح", - "𞺧": "ح", - "ﺣ": "ح", - "ﺤ": "ح", - "ﺢ": "ح", - "ﺡ": "ح", - "څ": "حۛ", - "ځ": "حٔ", - "ݲ": "حٔ", - "ﲩ": "حج", - "ﰗ": "حج", - "ﶿ": "حجى", - "ﲪ": "حم", - "ﰘ": "حم", - "ﵛ": "حمى", - "ﵚ": "حمى", - "ﴛ": "حى", - "ﳿ": "حى", - "ﴜ": "حى", - "ﴀ": "حى", - "𞸗": "خ", - "𞸷": "خ", - "𞹗": "خ", - "𞹷": "خ", - "𞺗": "خ", - "𞺷": "خ", - "ﺧ": "خ", - "ﺨ": "خ", - "ﺦ": "خ", - "ﺥ": "خ", - "ﲫ": "خج", - "ﰙ": "خج", - "ﰚ": "خح", - "ﲬ": "خم", - "ﰛ": "خم", - "ﴟ": "خى", - "ﴃ": "خى", - "ﴠ": "خى", - "ﴄ": "خى", - "𐋡": "د", - "𞸃": "د", - "𞺃": "د", - "𞺣": "د", - "ﺪ": "د", - "ﺩ": "د", - "ڈ": "دؕ", - "ﮉ": "دؕ", - "ﮈ": "دؕ", - "ڎ": "دۛ", - "ﮇ": "دۛ", - "ﮆ": "دۛ", - "ۮ": "د̂", - "ࢮ": "د̤̣", - "𞸘": "ذ", - "𞺘": "ذ", - "𞺸": "ذ", - "ﺬ": "ذ", - "ﺫ": "ذ", - "ﱛ": "ذٰ", - "ڋ": "ڊؕ", - "ﮅ": "ڌ", - "ﮄ": "ڌ", - "ﮃ": "ڍ", - "ﮂ": "ڍ", - "𞸓": "ر", - "𞺓": "ر", - "𞺳": "ر", - "ﺮ": "ر", - "ﺭ": "ر", - "ڑ": "رؕ", - "ﮍ": "رؕ", - "ﮌ": "رؕ", - "ژ": "رۛ", - "ﮋ": "رۛ", - "ﮊ": "رۛ", - "ڒ": "ر̆", - "ࢹ": "ر̆̇", - "ۯ": "ر̂", - "ݬ": "رٔ", - "ﱜ": "رٰ", - "ﷶ": "رسول", - "﷼": "رىlل", - "𞸆": "ز", - "𞺆": "ز", - "𞺦": "ز", - "ﺰ": "ز", - "ﺯ": "ز", - "ࢲ": "ز̂", - "ݱ": "ڗؕ", - "𞸎": "س", - "𞸮": "س", - "𞹎": "س", - "𞹮": "س", - "𞺎": "س", - "𞺮": "س", - "ﺳ": "س", - "ﺴ": "س", - "ﺲ": "س", - "ﺱ": "س", - "ش": "سۛ", - "𞸔": "سۛ", - "𞸴": "سۛ", - "𞹔": "سۛ", - "𞹴": "سۛ", - "𞺔": "سۛ", - "𞺴": "سۛ", - "ﺷ": "سۛ", - "ﺸ": "سۛ", - "ﺶ": "سۛ", - "ﺵ": "سۛ", - "ݾ": "س̂", - "ﴱ": "سo", - "ﳨ": "سo", - "ﴲ": "سۛo", - "ﳪ": "سۛo", - "ﲭ": "سج", - "ﴴ": "سج", - "ﰜ": "سج", - "ﴭ": "سۛج", - "ﴷ": "سۛج", - "ﴥ": "سۛج", - "ﴉ": "سۛج", - "ﵝ": "سجح", - "ﵞ": "سجى", - "ﵩ": "سۛجى", - "ﲮ": "سح", - "ﴵ": "سح", - "ﰝ": "سح", - "ﴮ": "سۛح", - "ﴸ": "سۛح", - "ﴦ": "سۛح", - "ﴊ": "سۛح", - "ﵜ": "سحج", - "ﵨ": "سۛحم", - "ﵧ": "سۛحم", - "ﶪ": "سۛحى", - "ﲯ": "سخ", - "ﴶ": "سخ", - "ﰞ": "سخ", - "ﴯ": "سۛخ", - "ﴹ": "سۛخ", - "ﴧ": "سۛخ", - "ﴋ": "سۛخ", - "ﶨ": "سخى", - "ﷆ": "سخى", - "ﴪ": "سر", - "ﴎ": "سر", - "ﴩ": "سۛر", - "ﴍ": "سۛر", - "ﲰ": "سم", - "ﳧ": "سم", - "ﰟ": "سم", - "ﴰ": "سۛم", - "ﳩ": "سۛم", - "ﴨ": "سۛم", - "ﴌ": "سۛم", - "ﵡ": "سمج", - "ﵠ": "سمح", - "ﵟ": "سمح", - "ﵫ": "سۛمخ", - "ﵪ": "سۛمخ", - "ﵣ": "سمم", - "ﵢ": "سمم", - "ﵭ": "سۛمم", - "ﵬ": "سۛمم", - "ﴗ": "سى", - "ﳻ": "سى", - "ﴘ": "سى", - "ﳼ": "سى", - "ﴙ": "سۛى", - "ﳽ": "سۛى", - "ﴚ": "سۛى", - "ﳾ": "سۛى", - "𐋲": "ص", - "𞸑": "ص", - "𞸱": "ص", - "𞹑": "ص", - "𞹱": "ص", - "𞺑": "ص", - "𞺱": "ص", - "ﺻ": "ص", - "ﺼ": "ص", - "ﺺ": "ص", - "ﺹ": "ص", - "ڞ": "صۛ", - "ࢯ": "ص̤̣", - "ﲱ": "صح", - "ﰠ": "صح", - "ﵥ": "صحح", - "ﵤ": "صحح", - "ﶩ": "صحى", - "ﲲ": "صخ", - "ﴫ": "صر", - "ﴏ": "صر", - "ﷵ": "صلعم", - "ﷹ": "صلى", - "ﷰ": "صلى", - "ﷺ": "صلى lللo علىo وسلم", - "ﲳ": "صم", - "ﰡ": "صم", - "ﷅ": "صمم", - "ﵦ": "صمم", - "ﴡ": "صى", - "ﴅ": "صى", - "ﴢ": "صى", - "ﴆ": "صى", - "𞸙": "ض", - "𞸹": "ض", - "𞹙": "ض", - "𞹹": "ض", - "𞺙": "ض", - "𞺹": "ض", - "ﺿ": "ض", - "ﻀ": "ض", - "ﺾ": "ض", - "ﺽ": "ض", - "ﲴ": "ضج", - "ﰢ": "ضج", - "ﲵ": "ضح", - "ﰣ": "ضح", - "ﵮ": "ضحى", - "ﶫ": "ضحى", - "ﲶ": "ضخ", - "ﰤ": "ضخ", - "ﵰ": "ضخم", - "ﵯ": "ضخم", - "ﴬ": "ضر", - "ﴐ": "ضر", - "ﲷ": "ضم", - "ﰥ": "ضم", - "ﴣ": "ضى", - "ﴇ": "ضى", - "ﴤ": "ضى", - "ﴈ": "ضى", - "𐋨": "ط", - "𞸈": "ط", - "𞹨": "ط", - "𞺈": "ط", - "𞺨": "ط", - "ﻃ": "ط", - "ﻄ": "ط", - "ﻂ": "ط", - "ﻁ": "ط", - "ڟ": "طۛ", - "ﲸ": "طح", - "ﰦ": "طح", - "ﴳ": "طم", - "ﴺ": "طم", - "ﰧ": "طم", - "ﵲ": "طمح", - "ﵱ": "طمح", - "ﵳ": "طمم", - "ﵴ": "طمى", - "ﴑ": "طى", - "ﳵ": "طى", - "ﴒ": "طى", - "ﳶ": "طى", - "𞸚": "ظ", - "𞹺": "ظ", - "𞺚": "ظ", - "𞺺": "ظ", - "ﻇ": "ظ", - "ﻈ": "ظ", - "ﻆ": "ظ", - "ﻅ": "ظ", - "ﲹ": "ظم", - "ﴻ": "ظم", - "ﰨ": "ظم", - "؏": "ع", - "𞸏": "ع", - "𞸯": "ع", - "𞹏": "ع", - "𞹯": "ع", - "𞺏": "ع", - "𞺯": "ع", - "ﻋ": "ع", - "ﻌ": "ع", - "ﻊ": "ع", - "ﻉ": "ع", - "ﲺ": "عج", - "ﰩ": "عج", - "ﷄ": "عجم", - "ﵵ": "عجم", - "ﷷ": "علىo", - "ﲻ": "عم", - "ﰪ": "عم", - "ﵷ": "عمم", - "ﵶ": "عمم", - "ﵸ": "عمى", - "ﶶ": "عمى", - "ﴓ": "عى", - "ﳷ": "عى", - "ﴔ": "عى", - "ﳸ": "عى", - "𞸛": "غ", - "𞸻": "غ", - "𞹛": "غ", - "𞹻": "غ", - "𞺛": "غ", - "𞺻": "غ", - "ﻏ": "غ", - "ﻐ": "غ", - "ﻎ": "غ", - "ﻍ": "غ", - "ﲼ": "غج", - "ﰫ": "غج", - "ﲽ": "غم", - "ﰬ": "غم", - "ﵹ": "غمم", - "ﵻ": "غمى", - "ﵺ": "غمى", - "ﴕ": "غى", - "ﳹ": "غى", - "ﴖ": "غى", - "ﳺ": "غى", - "𞸐": "ف", - "𞸰": "ف", - "𞹰": "ف", - "𞺐": "ف", - "𞺰": "ف", - "ﻓ": "ف", - "ﻔ": "ف", - "ﻒ": "ف", - "ﻑ": "ف", - "ڧ": "ف", - "ﲾ": "فج", - "ﰭ": "فج", - "ﲿ": "فح", - "ﰮ": "فح", - "ﳀ": "فخ", - "ﰯ": "فخ", - "ﵽ": "فخم", - "ﵼ": "فخم", - "ﳁ": "فم", - "ﰰ": "فم", - "ﷁ": "فمى", - "ﱼ": "فى", - "ﰱ": "فى", - "ﱽ": "فى", - "ﰲ": "فى", - "𞸞": "ڡ", - "𞹾": "ڡ", - "ࢻ": "ڡ", - "ٯ": "ڡ", - "𞸟": "ڡ", - "𞹟": "ڡ", - "ࢼ": "ڡ", - "ڤ": "ڡۛ", - "ﭬ": "ڡۛ", - "ﭭ": "ڡۛ", - "ﭫ": "ڡۛ", - "ﭪ": "ڡۛ", - "ڨ": "ڡۛ", - "ࢤ": "ڢۛ", - "ﭰ": "ڦ", - "ﭱ": "ڦ", - "ﭯ": "ڦ", - "ﭮ": "ڦ", - "𞸒": "ق", - "𞸲": "ق", - "𞹒": "ق", - "𞹲": "ق", - "𞺒": "ق", - "𞺲": "ق", - "ﻗ": "ق", - "ﻘ": "ق", - "ﻖ": "ق", - "ﻕ": "ق", - "ﳂ": "قح", - "ﰳ": "قح", - "ﷱ": "قلى", - "ﳃ": "قم", - "ﰴ": "قم", - "ﶴ": "قمح", - "ﵾ": "قمح", - "ﵿ": "قمم", - "ﶲ": "قمى", - "ﱾ": "قى", - "ﰵ": "قى", - "ﱿ": "قى", - "ﰶ": "قى", - "𞸊": "ك", - "𞸪": "ك", - "𞹪": "ك", - "ﻛ": "ك", - "ﻜ": "ك", - "ﻚ": "ك", - "ﻙ": "ك", - "ک": "ك", - "ﮐ": "ك", - "ﮑ": "ك", - "ﮏ": "ك", - "ﮎ": "ك", - "ڪ": "ك", - "ڭ": "كۛ", - "ﯕ": "كۛ", - "ﯖ": "كۛ", - "ﯔ": "كۛ", - "ﯓ": "كۛ", - "ݣ": "كۛ", - "ﲀ": "كl", - "ﰷ": "كl", - "ﳄ": "كج", - "ﰸ": "كج", - "ﳅ": "كح", - "ﰹ": "كح", - "ﳆ": "كخ", - "ﰺ": "كخ", - "ﳇ": "كل", - "ﳫ": "كل", - "ﲁ": "كل", - "ﰻ": "كل", - "ﳈ": "كم", - "ﳬ": "كم", - "ﲂ": "كم", - "ﰼ": "كم", - "ﷃ": "كمم", - "ﶻ": "كمم", - "ﶷ": "كمى", - "ﲃ": "كى", - "ﰽ": "كى", - "ﲄ": "كى", - "ﰾ": "كى", - "ݢ": "ڬ", - "ﮔ": "گ", - "ﮕ": "گ", - "ﮓ": "گ", - "ﮒ": "گ", - "ࢰ": "گ", - "ڴ": "گۛ", - "ﮜ": "ڱ", - "ﮝ": "ڱ", - "ﮛ": "ڱ", - "ﮚ": "ڱ", - "ﮘ": "ڳ", - "ﮙ": "ڳ", - "ﮗ": "ڳ", - "ﮖ": "ڳ", - "𞸋": "ل", - "𞸫": "ل", - "𞹋": "ل", - "𞺋": "ل", - "𞺫": "ل", - "ﻟ": "ل", - "ﻠ": "ل", - "ﻞ": "ل", - "ﻝ": "ل", - "ڷ": "لۛ", - "ڵ": "ل̆", - "ﻼ": "لl", - "ﻻ": "لl", - "ﻺ": "لlٕ", - "ﻹ": "لlٕ", - "ﻸ": "لlٴ", - "ﻷ": "لlٴ", - "ﳍ": "لo", - "ﻶ": "لآ", - "ﻵ": "لآ", - "ﳉ": "لج", - "ﰿ": "لج", - "ﶃ": "لجج", - "ﶄ": "لجج", - "ﶺ": "لجم", - "ﶼ": "لجم", - "ﶬ": "لجى", - "ﳊ": "لح", - "ﱀ": "لح", - "ﶵ": "لحم", - "ﶀ": "لحم", - "ﶂ": "لحى", - "ﶁ": "لحى", - "ﳋ": "لخ", - "ﱁ": "لخ", - "ﶆ": "لخم", - "ﶅ": "لخم", - "ﳌ": "لم", - "ﳭ": "لم", - "ﲅ": "لم", - "ﱂ": "لم", - "ﶈ": "لمح", - "ﶇ": "لمح", - "ﶭ": "لمى", - "ﲆ": "لى", - "ﱃ": "لى", - "ﲇ": "لى", - "ﱄ": "لى", - "𞸌": "م", - "𞸬": "م", - "𞹬": "م", - "𞺌": "م", - "𞺬": "م", - "ﻣ": "م", - "ﻤ": "م", - "ﻢ": "م", - "ﻡ": "م", - "ࢧ": "مۛ", - "۾": "م͈", - "ﲈ": "مl", - "ﳎ": "مج", - "ﱅ": "مج", - "ﶌ": "مجح", - "ﶒ": "مجخ", - "ﶍ": "مجم", - "ﷀ": "مجى", - "ﳏ": "مح", - "ﱆ": "مح", - "ﶉ": "محج", - "ﶊ": "محم", - "ﷴ": "محمد", - "ﶋ": "محى", - "ﳐ": "مخ", - "ﱇ": "مخ", - "ﶎ": "مخج", - "ﶏ": "مخم", - "ﶹ": "مخى", - "ﳑ": "مم", - "ﲉ": "مم", - "ﱈ": "مم", - "ﶱ": "ممى", - "ﱉ": "مى", - "ﱊ": "مى", - "𞸍": "ن", - "𞸭": "ن", - "𞹍": "ن", - "𞹭": "ن", - "𞺍": "ن", - "𞺭": "ن", - "ﻧ": "ن", - "ﻨ": "ن", - "ﻦ": "ن", - "ﻥ": "ن", - "ݨ": "نؕ", - "ݩ": "ن̆", - "ﳖ": "نo", - "ﳯ": "نo", - "ﶸ": "نجح", - "ﶽ": "نجح", - "ﶘ": "نجم", - "ﶗ": "نجم", - "ﶙ": "نجى", - "ﷇ": "نجى", - "ﳓ": "نح", - "ﱌ": "نح", - "ﶕ": "نحم", - "ﶖ": "نحى", - "ﶳ": "نحى", - "ﳔ": "نخ", - "ﱍ": "نخ", - "ﲊ": "نر", - "ﲋ": "نز", - "ﳕ": "نم", - "ﳮ": "نم", - "ﲌ": "نم", - "ﱎ": "نم", - "ﶛ": "نمى", - "ﶚ": "نمى", - "ﲍ": "نن", - "ﲎ": "نى", - "ﱏ": "نى", - "ﲏ": "نى", - "ﱐ": "نى", - "ۂ": "ۀ", - "ﮥ": "ۀ", - "ﮤ": "ۀ", - "𐋤": "و", - "𞸅": "و", - "𞺅": "و", - "𞺥": "و", - "ﻮ": "و", - "ﻭ": "و", - "ࢱ": "و", - "ۋ": "وۛ", - "ﯟ": "وۛ", - "ﯞ": "وۛ", - "ۇ": "و̓", - "ﯘ": "و̓", - "ﯗ": "و̓", - "ۆ": "و̆", - "ﯚ": "و̆", - "ﯙ": "و̆", - "ۉ": "و̂", - "ﯣ": "و̂", - "ﯢ": "و̂", - "ۈ": "وٰ", - "ﯜ": "وٰ", - "ﯛ": "وٰ", - "ؤ": "وٴ", - "ﺆ": "وٴ", - "ﺅ": "وٴ", - "ٶ": "وٴ", - "ٷ": "و̓ٴ", - "ﯝ": "و̓ٴ", - "ﷸ": "وسلم", - "ﯡ": "ۅ", - "ﯠ": "ۅ", - "ٮ": "ى", - "𞸜": "ى", - "𞹼": "ى", - "ں": "ى", - "𞸝": "ى", - "𞹝": "ى", - "ﮟ": "ى", - "ﮞ": "ى", - "ࢽ": "ى", - "ﯨ": "ى", - "ﯩ": "ى", - "ﻰ": "ى", - "ﻯ": "ى", - "ي": "ى", - "𞸉": "ى", - "𞸩": "ى", - "𞹉": "ى", - "𞹩": "ى", - "𞺉": "ى", - "𞺩": "ى", - "ﻳ": "ى", - "ﻴ": "ى", - "ﻲ": "ى", - "ﻱ": "ى", - "ی": "ى", - "ﯾ": "ى", - "ﯿ": "ى", - "ﯽ": "ى", - "ﯼ": "ى", - "ے": "ى", - "ﮯ": "ى", - "ﮮ": "ى", - "ٹ": "ىؕ", - "ﭨ": "ىؕ", - "ﭩ": "ىؕ", - "ﭧ": "ىؕ", - "ﭦ": "ىؕ", - "ڻ": "ىؕ", - "ﮢ": "ىؕ", - "ﮣ": "ىؕ", - "ﮡ": "ىؕ", - "ﮠ": "ىؕ", - "پ": "ىۛ", - "ﭘ": "ىۛ", - "ﭙ": "ىۛ", - "ﭗ": "ىۛ", - "ﭖ": "ىۛ", - "ث": "ىۛ", - "𞸖": "ىۛ", - "𞸶": "ىۛ", - "𞹶": "ىۛ", - "𞺖": "ىۛ", - "𞺶": "ىۛ", - "ﺛ": "ىۛ", - "ﺜ": "ىۛ", - "ﺚ": "ىۛ", - "ﺙ": "ىۛ", - "ڽ": "ىۛ", - "ۑ": "ىۛ", - "ؿ": "ىۛ", - "ࢷ": "ىۛۢ", - "ݖ": "ى̆", - "ێ": "ى̆", - "ࢺ": "ى̆̇", - "ؽ": "ى̂", - "ࢨ": "ىٔ", - "ﲐ": "ىٰ", - "ﱝ": "ىٰ", - "ﳞ": "ىo", - "ﳱ": "ىo", - "ﳦ": "ىۛo", - "ئ": "ىٴ", - "ﺋ": "ىٴ", - "ﺌ": "ىٴ", - "ﺊ": "ىٴ", - "ﺉ": "ىٴ", - "ٸ": "ىٴ", - "ﯫ": "ىٴl", - "ﯪ": "ىٴl", - "ﲛ": "ىٴo", - "ﳠ": "ىٴo", - "ﯭ": "ىٴo", - "ﯬ": "ىٴo", - "ﯸ": "ىٴٻ", - "ﯷ": "ىٴٻ", - "ﯶ": "ىٴٻ", - "ﲗ": "ىٴج", - "ﰀ": "ىٴج", - "ﲘ": "ىٴح", - "ﰁ": "ىٴح", - "ﲙ": "ىٴخ", - "ﱤ": "ىٴر", - "ﱥ": "ىٴز", - "ﲚ": "ىٴم", - "ﳟ": "ىٴم", - "ﱦ": "ىٴم", - "ﰂ": "ىٴم", - "ﱧ": "ىٴن", - "ﯯ": "ىٴو", - "ﯮ": "ىٴو", - "ﯱ": "ىٴو̓", - "ﯰ": "ىٴو̓", - "ﯳ": "ىٴو̆", - "ﯲ": "ىٴو̆", - "ﯵ": "ىٴوٰ", - "ﯴ": "ىٴوٰ", - "ﯻ": "ىٴى", - "ﯺ": "ىٴى", - "ﱨ": "ىٴى", - "ﯹ": "ىٴى", - "ﰃ": "ىٴى", - "ﱩ": "ىٴى", - "ﰄ": "ىٴى", - "ﳚ": "ىج", - "ﱕ": "ىج", - "ﰑ": "ىۛج", - "ﶯ": "ىجى", - "ﳛ": "ىح", - "ﱖ": "ىح", - "ﶮ": "ىحى", - "ﳜ": "ىخ", - "ﱗ": "ىخ", - "ﲑ": "ىر", - "ﱶ": "ىۛر", - "ﲒ": "ىز", - "ﱷ": "ىۛز", - "ﳝ": "ىم", - "ﳰ": "ىم", - "ﲓ": "ىم", - "ﱘ": "ىم", - "ﲦ": "ىۛم", - "ﳥ": "ىۛم", - "ﱸ": "ىۛم", - "ﰒ": "ىۛم", - "ﶝ": "ىمم", - "ﶜ": "ىمم", - "ﶰ": "ىمى", - "ﲔ": "ىن", - "ﱹ": "ىۛن", - "ﲕ": "ىى", - "ﱙ": "ىى", - "ﲖ": "ىى", - "ﱚ": "ىى", - "ﱺ": "ىۛى", - "ﰓ": "ىۛى", - "ﱻ": "ىۛى", - "ﰔ": "ىۛى", - "ﮱ": "ۓ", - "ﮰ": "ۓ", - "𐊸": "ⵀ", - "⁞": "ⵂ", - "⸽": "ⵂ", - "⦙": "ⵂ", - "︙": "ⵗ", - "⁝": "ⵗ", - "⋮": "ⵗ", - "Մ": "ሆ", - "Ռ": "ቡ", - "Ի": "ኮ", - "Պ": "ጣ", - "आ": "अा", - "ऒ": "अाॆ", - "ओ": "अाे", - "औ": "अाै", - "ऄ": "अॆ", - "ऑ": "अॉ", - "ऍ": "एॅ", - "ऎ": "एॆ", - "ऐ": "एे", - "ई": "र्इ", - "ઽ": "ऽ", - "𑇜": "ꣻ", - "𑇋": "ऺ", - "ુ": "ु", - "ૂ": "ू", - "ੋ": "ॆ", - "੍": "्", - "્": "्", - "আ": "অা", - "ৠ": "ঋৃ", - "ৡ": "ঋৃ", - "𑒒": "ঘ", - "𑒔": "চ", - "𑒖": "জ", - "𑒘": "ঞ", - "𑒙": "ট", - "𑒛": "ড", - "𑒪": "ণ", - "𑒞": "ত", - "𑒟": "থ", - "𑒠": "দ", - "𑒡": "ধ", - "𑒢": "ন", - "𑒣": "প", - "𑒩": "ব", - "𑒧": "ম", - "𑒨": "য", - "𑒫": "র", - "𑒝": "ল", - "𑒭": "ষ", - "𑒮": "স", - "𑓄": "ঽ", - "𑒰": "া", - "𑒱": "ি", - "𑒹": "ে", - "𑒼": "ো", - "𑒾": "ৌ", - "𑓂": "্", - "𑒽": "ৗ", - "ਉ": "ੳੁ", - "ਊ": "ੳੂ", - "ਆ": "ਅਾ", - "ਐ": "ਅੈ", - "ਔ": "ਅੌ", - "ਇ": "ੲਿ", - "ਈ": "ੲੀ", - "ਏ": "ੲੇ", - "આ": "અા", - "ઑ": "અાૅ", - "ઓ": "અાે", - "ઔ": "અાૈ", - "ઍ": "અૅ", - "એ": "અે", - "ઐ": "અૈ", - "ଆ": "ଅା", - "௮": "அ", - "ர": "ஈ", - "ா": "ஈ", - "௫": "ஈு", - "௨": "உ", - "ഉ": "உ", - "ஊ": "உள", - "ഊ": "உൗ", - "௭": "எ", - "௷": "எவ", - "ஜ": "ஐ", - "ജ": "ஐ", - "௧": "க", - "௪": "ச", - "௬": "சு", - "௲": "சூ", - "ഺ": "டி", - "ണ": "ண", - "௺": "நீ", - "௴": "மீ", - "௰": "ய", - "ഴ": "ழ", - "ௗ": "ள", - "ை": "ன", - "ശ": "ஶ", - "௸": "ஷ", - "ി": "ி", - "ീ": "ி", - "ொ": "ெஈ", - "ௌ": "ெள", - "ோ": "ேஈ", - "ಅ": "అ", - "ಆ": "ఆ", - "ಇ": "ఇ", - "ౠ": "ఋా", - "ౡ": "ఌా", - "ಒ": "ఒ", - "ఔ": "ఒౌ", - "ಔ": "ఒౌ", - "ఓ": "ఒౕ", - "ಓ": "ఒౕ", - "ಜ": "జ", - "ಞ": "ఞ", - "ఢ": "డ̣", - "ಣ": "ణ", - "థ": "ధּ", - "భ": "బ̣", - "ಯ": "య", - "ఠ": "రּ", - "ಱ": "ఱ", - "ಲ": "ల", - "ష": "వ̣", - "హ": "వా", - "మ": "వు", - "ూ": "ుా", - "ౄ": "ృా", - "ೡ": "ಌಾ", - "ഈ": "ഇൗ", - "ഐ": "എെ", - "ഓ": "ഒാ", - "ഔ": "ഒൗ", - "ൡ": "ഞ", - "൫": "ദ്ര", - "൹": "നു", - "ഌ": "നു", - "ങ": "നു", - "൯": "ന്", - "ൻ": "ന്", - "൬": "ന്ന", - "൚": "ന്മ", - "റ": "ര", - "൪": "ര്", - "ർ": "ര്", - "൮": "വ്ര", - "൶": "ഹ്മ", - "ൂ": "ു", - "ൃ": "ു", - "ൈ": "െെ", - "෪": "ජ", - "෫": "ද", - "𑐓": "𑐴𑑂𑐒", - "𑐙": "𑐴𑑂𑐘", - "𑐤": "𑐴𑑂𑐣", - "𑐪": "𑐴𑑂𑐩", - "𑐭": "𑐴𑑂𑐬", - "𑐯": "𑐴𑑂𑐮", - "𑗘": "𑖂", - "𑗙": "𑖂", - "𑗚": "𑖃", - "𑗛": "𑖄", - "𑗜": "𑖲", - "𑗝": "𑖳", - "ฃ": "ข", - "ด": "ค", - "ต": "ค", - "ม": "ฆ", - "ຈ": "จ", - "ซ": "ช", - "ฏ": "ฎ", - "ท": "ฑ", - "ບ": "บ", - "ປ": "ป", - "ຝ": "ฝ", - "ພ": "พ", - "ຟ": "ฟ", - "ฦ": "ภ", - "ຍ": "ย", - "។": "ฯ", - "ๅ": "า", - "ำ": "̊า", - "ិ": "ิ", - "ី": "ี", - "ឹ": "ึ", - "ឺ": "ื", - "ຸ": "ุ", - "ູ": "ู", - "แ": "เเ", - "ໜ": "ຫນ", - "ໝ": "ຫມ", - "ຳ": "̊າ", - "༂": "འུྂཿ", - "༃": "འུྂ༔", - "ཪ": "ར", - "ༀ": "ཨོཾ", - "ཷ": "ྲཱྀ", - "ཹ": "ླཱྀ", - "𑲲": "𑲪", - "ႁ": "ဂှ", - "က": "ဂာ", - "ၰ": "ဃှ", - "ၦ": "ပှ", - "ဟ": "ပာ", - "ၯ": "ပာှ", - "ၾ": "ၽှ", - "ဩ": "သြ", - "ဪ": "သြော်", - "႞": "ႃ̊", - "ឣ": "អ", - "᧐": "ᦞ", - "᧑": "ᦱ", - "᪀": "ᩅ", - "᪐": "ᩅ", - "꩓": "ꨁ", - "꩖": "ꨣ", - "᭒": "ᬍ", - "᭓": "ᬑ", - "᭘": "ᬨ", - "ꦣ": "ꦝ", - "ᢖ": "ᡜ", - "ᡕ": "ᠵ", - "ῶ": "Ꮿ", - "ᐍ": "ᐁ·", - "ᐫ": "ᐁᐠ", - "ᐑ": "ᐄ·", - "ᐓ": "ᐅ·", - "ᐭ": "ᐅᐠ", - "ᐕ": "ᐆ·", - "ᐘ": "ᐊ·", - "ᐮ": "ᐊᐠ", - "ᐚ": "ᐋ·", - "ᣝ": "ᐞᣟ", - "ᓑ": "ᐡ", - "ᕀ": "ᐩ", - "ᐿ": "ᐲ·", - "ᑃ": "ᐴ·", - "⍩": "ᐵ", - "ᑇ": "ᐹ·", - "ᑜ": "ᑏ·", - "⸧": "ᑐ", - "⊃": "ᑐ", - "ᑞ": "ᑐ·", - "ᑩ": "ᑐ'", - "⟉": "ᑐ/", - "⫗": "ᑐᑕ", - "ᑠ": "ᑑ·", - "⸦": "ᑕ", - "⊂": "ᑕ", - "ᑢ": "ᑕ·", - "ᑪ": "ᑕ'", - "ᑤ": "ᑖ·", - "ᑵ": "ᑫ·", - "ᒅ": "ᑫ'", - "ᑹ": "ᑮ·", - "ᑽ": "ᑰ·", - "ᘃ": "ᒉ", - "ᒓ": "ᒉ·", - "ᒕ": "ᒋ·", - "ᒗ": "ᒌ·", - "ᒛ": "ᒎ·", - "ᘂ": "ᒐ", - "ᒝ": "ᒐ·", - "ᒟ": "ᒑ·", - "ᒭ": "ᒣ·", - "ᒱ": "ᒦ·", - "ᒳ": "ᒧ·", - "ᒵ": "ᒨ·", - "ᒹ": "ᒫ·", - "ᓊ": "ᓀ·", - "ᣇ": "ᓂ·", - "ᣉ": "ᓃ·", - "ᣋ": "ᓄ·", - "ᣍ": "ᓅ·", - "ᓌ": "ᓇ·", - "ᓎ": "ᓈ·", - "ᘄ": "ᓓ", - "ᓝ": "ᓓ·", - "ᓟ": "ᓕ·", - "ᓡ": "ᓖ·", - "ᓣ": "ᓗ·", - "ᓥ": "ᓘ·", - "ᘇ": "ᓚ", - "ᓧ": "ᓚ·", - "ᓩ": "ᓛ·", - "ᓷ": "ᓭ·", - "ᓹ": "ᓯ·", - "ᓻ": "ᓰ·", - "ᓽ": "ᓱ·", - "ᓿ": "ᓲ·", - "ᔁ": "ᓴ·", - "ᔃ": "ᓵ·", - "ᔌ": "ᔋ<", - "ᔎ": "ᔋb", - "ᔍ": "ᔋᑕ", - "ᔏ": "ᔋᒐ", - "ᔘ": "ᔐ·", - "ᔚ": "ᔑ·", - "ᔜ": "ᔒ·", - "ᔞ": "ᔓ·", - "ᔠ": "ᔔ·", - "ᔢ": "ᔕ·", - "ᔤ": "ᔖ·", - "ᔲ": "ᔨ·", - "ᔴ": "ᔩ·", - "ᔶ": "ᔪ·", - "ᔸ": "ᔫ·", - "ᔺ": "ᔭ·", - "ᔼ": "ᔮ·", - "ᘢ": "ᕃ", - "ᣠ": "ᕃ·", - "ᘣ": "ᕆ", - "ᘤ": "ᕊ", - "ᕏ": "ᕌ·", - "ᖃ": "ᕐb", - "ᖄ": "ᕐḃ", - "ᖁ": "ᕐd", - "ᕿ": "ᕐP", - "ᙯ": "ᕐᑫ", - "ᕾ": "ᕐᑬ", - "ᖀ": "ᕐᑮ", - "ᖂ": "ᕐᑰ", - "ᖅ": "ᕐᒃ", - "ᕜ": "ᕚ·", - "ᣣ": "ᕞ·", - "ᣤ": "ᕦ·", - "ᕩ": "ᕧ·", - "ᣥ": "ᕫ·", - "ᣨ": "ᖆ·", - "ᖑ": "ᖕJ", - "ᙰ": "ᖕᒉ", - "ᖎ": "ᖕᒊ", - "ᖏ": "ᖕᒋ", - "ᖐ": "ᖕᒌ", - "ᖒ": "ᖕᒎ", - "ᖓ": "ᖕᒐ", - "ᖔ": "ᖕᒑ", - "ᙳ": "ᖖJ", - "ᙱ": "ᖖᒋ", - "ᙲ": "ᖖᒌ", - "ᙴ": "ᖖᒎ", - "ᙵ": "ᖖᒐ", - "ᙶ": "ᖖᒑ", - "ᣪ": "ᖗ·", - "ᙷ": "ᖧ·", - "ᙸ": "ᖨ·", - "ᙹ": "ᖩ·", - "ᙺ": "ᖪ·", - "ᙻ": "ᖫ·", - "ᙼ": "ᖬ·", - "ᙽ": "ᖭ·", - "⪫": "ᗒ", - "⪪": "ᗕ", - "ꓷ": "ᗡ", - "ᣰ": "ᗴ·", - "ᣲ": "ᘛ·", - "ᶻ": "ᙆ", - "ꓭ": "ᙠ", - "ᶺ": "ᣔ", - "ᴾ": "ᣖ", - "ᣜ": "ᣟᐞ", - "ˡ": "ᣳ", - "ʳ": "ᣴ", - "ˢ": "ᣵ", - "ᣛ": "ᣵ", - "ꚰ": "ᚹ", - "ᛡ": "ᚼ", - "⍿": "ᚽ", - "ᛂ": "ᚽ", - "𝈿": "ᛋ", - "↑": "ᛏ", - "↿": "ᛐ", - "⥮": "ᛐ⇂", - "⥣": "ᛐᛚ", - "ⵣ": "ᛯ", - "↾": "ᛚ", - "⨡": "ᛚ", - "⋄": "ᛜ", - "◇": "ᛜ", - "◊": "ᛜ", - "♢": "ᛜ", - "🝔": "ᛜ", - "𑢷": "ᛜ", - "𐊔": "ᛜ", - "⍚": "ᛜ̲", - "⋈": "ᛞ", - "⨝": "ᛞ", - "𐓐": "ᛦ", - "↕": "ᛨ", - "𐳼": "𐲂", - "𐳺": "𐲥", - "ㄱ": "ᄀ", - "ᆨ": "ᄀ", - "ᄁ": "ᄀᄀ", - "ㄲ": "ᄀᄀ", - "ᆩ": "ᄀᄀ", - "ᇺ": "ᄀᄂ", - "ᅚ": "ᄀᄃ", - "ᇃ": "ᄀᄅ", - "ᇻ": "ᄀᄇ", - "ᆪ": "ᄀᄉ", - "ㄳ": "ᄀᄉ", - "ᇄ": "ᄀᄉᄀ", - "ᇼ": "ᄀᄎ", - "ᇽ": "ᄀᄏ", - "ᇾ": "ᄀᄒ", - "ㄴ": "ᄂ", - "ᆫ": "ᄂ", - "ᄓ": "ᄂᄀ", - "ᇅ": "ᄂᄀ", - "ᄔ": "ᄂᄂ", - "ㅥ": "ᄂᄂ", - "ᇿ": "ᄂᄂ", - "ᄕ": "ᄂᄃ", - "ㅦ": "ᄂᄃ", - "ᇆ": "ᄂᄃ", - "ퟋ": "ᄂᄅ", - "ᄖ": "ᄂᄇ", - "ᅛ": "ᄂᄉ", - "ᇇ": "ᄂᄉ", - "ㅧ": "ᄂᄉ", - "ᅜ": "ᄂᄌ", - "ᆬ": "ᄂᄌ", - "ㄵ": "ᄂᄌ", - "ퟌ": "ᄂᄎ", - "ᇉ": "ᄂᄐ", - "ᅝ": "ᄂᄒ", - "ᆭ": "ᄂᄒ", - "ㄶ": "ᄂᄒ", - "ᇈ": "ᄂᅀ", - "ㅨ": "ᄂᅀ", - "ㄷ": "ᄃ", - "ᆮ": "ᄃ", - "ᄗ": "ᄃᄀ", - "ᇊ": "ᄃᄀ", - "ᄄ": "ᄃᄃ", - "ㄸ": "ᄃᄃ", - "ퟍ": "ᄃᄃ", - "ퟎ": "ᄃᄃᄇ", - "ᅞ": "ᄃᄅ", - "ᇋ": "ᄃᄅ", - "ꥠ": "ᄃᄆ", - "ꥡ": "ᄃᄇ", - "ퟏ": "ᄃᄇ", - "ꥢ": "ᄃᄉ", - "ퟐ": "ᄃᄉ", - "ퟑ": "ᄃᄉᄀ", - "ꥣ": "ᄃᄌ", - "ퟒ": "ᄃᄌ", - "ퟓ": "ᄃᄎ", - "ퟔ": "ᄃᄐ", - "ㄹ": "ᄅ", - "ᆯ": "ᄅ", - "ꥤ": "ᄅᄀ", - "ᆰ": "ᄅᄀ", - "ㄺ": "ᄅᄀ", - "ꥥ": "ᄅᄀᄀ", - "ퟕ": "ᄅᄀᄀ", - "ᇌ": "ᄅᄀᄉ", - "ㅩ": "ᄅᄀᄉ", - "ퟖ": "ᄅᄀᄒ", - "ᄘ": "ᄅᄂ", - "ᇍ": "ᄅᄂ", - "ꥦ": "ᄅᄃ", - "ᇎ": "ᄅᄃ", - "ㅪ": "ᄅᄃ", - "ꥧ": "ᄅᄃᄃ", - "ᇏ": "ᄅᄃᄒ", - "ᄙ": "ᄅᄅ", - "ᇐ": "ᄅᄅ", - "ퟗ": "ᄅᄅᄏ", - "ꥨ": "ᄅᄆ", - "ᆱ": "ᄅᄆ", - "ㄻ": "ᄅᄆ", - "ᇑ": "ᄅᄆᄀ", - "ᇒ": "ᄅᄆᄉ", - "ퟘ": "ᄅᄆᄒ", - "ꥩ": "ᄅᄇ", - "ᆲ": "ᄅᄇ", - "ㄼ": "ᄅᄇ", - "ퟙ": "ᄅᄇᄃ", - "ꥪ": "ᄅᄇᄇ", - "ᇓ": "ᄅᄇᄉ", - "ㅫ": "ᄅᄇᄉ", - "ꥫ": "ᄅᄇᄋ", - "ᇕ": "ᄅᄇᄋ", - "ퟚ": "ᄅᄇᄑ", - "ᇔ": "ᄅᄇᄒ", - "ꥬ": "ᄅᄉ", - "ᆳ": "ᄅᄉ", - "ㄽ": "ᄅᄉ", - "ᇖ": "ᄅᄉᄉ", - "ᄛ": "ᄅᄋ", - "ퟝ": "ᄅᄋ", - "ꥭ": "ᄅᄌ", - "ꥮ": "ᄅᄏ", - "ᇘ": "ᄅᄏ", - "ᆴ": "ᄅᄐ", - "ㄾ": "ᄅᄐ", - "ᆵ": "ᄅᄑ", - "ㄿ": "ᄅᄑ", - "ᄚ": "ᄅᄒ", - "ㅀ": "ᄅᄒ", - "ᄻ": "ᄅᄒ", - "ᆶ": "ᄅᄒ", - "ퟲ": "ᄅᄒ", - "ᇗ": "ᄅᅀ", - "ㅬ": "ᄅᅀ", - "ퟛ": "ᄅᅌ", - "ᇙ": "ᄅᅙ", - "ㅭ": "ᄅᅙ", - "ퟜ": "ᄅᅙᄒ", - "ㅁ": "ᄆ", - "ᆷ": "ᄆ", - "ꥯ": "ᄆᄀ", - "ᇚ": "ᄆᄀ", - "ퟞ": "ᄆᄂ", - "ퟟ": "ᄆᄂᄂ", - "ꥰ": "ᄆᄃ", - "ᇛ": "ᄆᄅ", - "ퟠ": "ᄆᄆ", - "ᄜ": "ᄆᄇ", - "ㅮ": "ᄆᄇ", - "ᇜ": "ᄆᄇ", - "ퟡ": "ᄆᄇᄉ", - "ꥱ": "ᄆᄉ", - "ᇝ": "ᄆᄉ", - "ㅯ": "ᄆᄉ", - "ᇞ": "ᄆᄉᄉ", - "ᄝ": "ᄆᄋ", - "ㅱ": "ᄆᄋ", - "ᇢ": "ᄆᄋ", - "ퟢ": "ᄆᄌ", - "ᇠ": "ᄆᄎ", - "ᇡ": "ᄆᄒ", - "ᇟ": "ᄆᅀ", - "ㅰ": "ᄆᅀ", - "ㅂ": "ᄇ", - "ᆸ": "ᄇ", - "ᄞ": "ᄇᄀ", - "ㅲ": "ᄇᄀ", - "ᄟ": "ᄇᄂ", - "ᄠ": "ᄇᄃ", - "ㅳ": "ᄇᄃ", - "ퟣ": "ᄇᄃ", - "ᇣ": "ᄇᄅ", - "ퟤ": "ᄇᄅᄑ", - "ퟥ": "ᄇᄆ", - "ᄈ": "ᄇᄇ", - "ㅃ": "ᄇᄇ", - "ퟦ": "ᄇᄇ", - "ᄬ": "ᄇᄇᄋ", - "ㅹ": "ᄇᄇᄋ", - "ᄡ": "ᄇᄉ", - "ㅄ": "ᄇᄉ", - "ᆹ": "ᄇᄉ", - "ᄢ": "ᄇᄉᄀ", - "ㅴ": "ᄇᄉᄀ", - "ᄣ": "ᄇᄉᄃ", - "ㅵ": "ᄇᄉᄃ", - "ퟧ": "ᄇᄉᄃ", - "ᄤ": "ᄇᄉᄇ", - "ᄥ": "ᄇᄉᄉ", - "ᄦ": "ᄇᄉᄌ", - "ꥲ": "ᄇᄉᄐ", - "ᄫ": "ᄇᄋ", - "ㅸ": "ᄇᄋ", - "ᇦ": "ᄇᄋ", - "ᄧ": "ᄇᄌ", - "ㅶ": "ᄇᄌ", - "ퟨ": "ᄇᄌ", - "ᄨ": "ᄇᄎ", - "ퟩ": "ᄇᄎ", - "ꥳ": "ᄇᄏ", - "ᄩ": "ᄇᄐ", - "ㅷ": "ᄇᄐ", - "ᄪ": "ᄇᄑ", - "ᇤ": "ᄇᄑ", - "ꥴ": "ᄇᄒ", - "ᇥ": "ᄇᄒ", - "ㅅ": "ᄉ", - "ᆺ": "ᄉ", - "ᄭ": "ᄉᄀ", - "ㅺ": "ᄉᄀ", - "ᇧ": "ᄉᄀ", - "ᄮ": "ᄉᄂ", - "ㅻ": "ᄉᄂ", - "ᄯ": "ᄉᄃ", - "ㅼ": "ᄉᄃ", - "ᇨ": "ᄉᄃ", - "ᄰ": "ᄉᄅ", - "ᇩ": "ᄉᄅ", - "ᄱ": "ᄉᄆ", - "ퟪ": "ᄉᄆ", - "ᄲ": "ᄉᄇ", - "ㅽ": "ᄉᄇ", - "ᇪ": "ᄉᄇ", - "ᄳ": "ᄉᄇᄀ", - "ퟫ": "ᄉᄇᄋ", - "ᄊ": "ᄉᄉ", - "ㅆ": "ᄉᄉ", - "ᆻ": "ᄉᄉ", - "ퟬ": "ᄉᄉᄀ", - "ퟭ": "ᄉᄉᄃ", - "ꥵ": "ᄉᄉᄇ", - "ᄴ": "ᄉᄉᄉ", - "ᄵ": "ᄉᄋ", - "ᄶ": "ᄉᄌ", - "ㅾ": "ᄉᄌ", - "ퟯ": "ᄉᄌ", - "ᄷ": "ᄉᄎ", - "ퟰ": "ᄉᄎ", - "ᄸ": "ᄉᄏ", - "ᄹ": "ᄉᄐ", - "ퟱ": "ᄉᄐ", - "ᄺ": "ᄉᄑ", - "ퟮ": "ᄉᅀ", - "ㅇ": "ᄋ", - "ᆼ": "ᄋ", - "ᅁ": "ᄋᄀ", - "ᇬ": "ᄋᄀ", - "ᇭ": "ᄋᄀᄀ", - "ᅂ": "ᄋᄃ", - "ꥶ": "ᄋᄅ", - "ᅃ": "ᄋᄆ", - "ᅄ": "ᄋᄇ", - "ᅅ": "ᄋᄉ", - "ᇱ": "ᄋᄉ", - "ㆂ": "ᄋᄉ", - "ᅇ": "ᄋᄋ", - "ㆀ": "ᄋᄋ", - "ᇮ": "ᄋᄋ", - "ᅈ": "ᄋᄌ", - "ᅉ": "ᄋᄎ", - "ᇯ": "ᄋᄏ", - "ᅊ": "ᄋᄐ", - "ᅋ": "ᄋᄑ", - "ꥷ": "ᄋᄒ", - "ᅆ": "ᄋᅀ", - "ᇲ": "ᄋᅀ", - "ㆃ": "ᄋᅀ", - "ㅈ": "ᄌ", - "ᆽ": "ᄌ", - "ퟷ": "ᄌᄇ", - "ퟸ": "ᄌᄇᄇ", - "ᅍ": "ᄌᄋ", - "ᄍ": "ᄌᄌ", - "ㅉ": "ᄌᄌ", - "ퟹ": "ᄌᄌ", - "ꥸ": "ᄌᄌᄒ", - "ㅊ": "ᄎ", - "ᆾ": "ᄎ", - "ᅒ": "ᄎᄏ", - "ᅓ": "ᄎᄒ", - "ㅋ": "ᄏ", - "ᆿ": "ᄏ", - "ㅌ": "ᄐ", - "ᇀ": "ᄐ", - "ꥹ": "ᄐᄐ", - "ㅍ": "ᄑ", - "ᇁ": "ᄑ", - "ᅖ": "ᄑᄇ", - "ᇳ": "ᄑᄇ", - "ퟺ": "ᄑᄉ", - "ᅗ": "ᄑᄋ", - "ㆄ": "ᄑᄋ", - "ᇴ": "ᄑᄋ", - "ퟻ": "ᄑᄐ", - "ꥺ": "ᄑᄒ", - "ㅎ": "ᄒ", - "ᇂ": "ᄒ", - "ᇵ": "ᄒᄂ", - "ᇶ": "ᄒᄅ", - "ᇷ": "ᄒᄆ", - "ᇸ": "ᄒᄇ", - "ꥻ": "ᄒᄉ", - "ᅘ": "ᄒᄒ", - "ㆅ": "ᄒᄒ", - "ᄽ": "ᄼᄼ", - "ᄿ": "ᄾᄾ", - "ㅿ": "ᅀ", - "ᇫ": "ᅀ", - "ퟳ": "ᅀᄇ", - "ퟴ": "ᅀᄇᄋ", - "ㆁ": "ᅌ", - "ᇰ": "ᅌ", - "ퟵ": "ᅌᄆ", - "ퟶ": "ᅌᄒ", - "ᅏ": "ᅎᅎ", - "ᅑ": "ᅐᅐ", - "ㆆ": "ᅙ", - "ᇹ": "ᅙ", - "ꥼ": "ᅙᅙ", - "ㅤ": "ᅠ", - "ㅏ": "ᅡ", - "ᆣ": "ᅡー", - "ᅶ": "ᅡᅩ", - "ᅷ": "ᅡᅮ", - "ᅢ": "ᅡ丨", - "ㅐ": "ᅡ丨", - "ㅑ": "ᅣ", - "ᅸ": "ᅣᅩ", - "ᅹ": "ᅣᅭ", - "ᆤ": "ᅣᅮ", - "ᅤ": "ᅣ丨", - "ㅒ": "ᅣ丨", - "ㅓ": "ᅥ", - "ᅼ": "ᅥー", - "ᅺ": "ᅥᅩ", - "ᅻ": "ᅥᅮ", - "ᅦ": "ᅥ丨", - "ㅔ": "ᅥ丨", - "ㅕ": "ᅧ", - "ᆥ": "ᅧᅣ", - "ᅽ": "ᅧᅩ", - "ᅾ": "ᅧᅮ", - "ᅨ": "ᅧ丨", - "ㅖ": "ᅧ丨", - "ㅗ": "ᅩ", - "ᅪ": "ᅩᅡ", - "ㅘ": "ᅩᅡ", - "ᅫ": "ᅩᅡ丨", - "ㅙ": "ᅩᅡ丨", - "ᆦ": "ᅩᅣ", - "ᆧ": "ᅩᅣ丨", - "ᅿ": "ᅩᅥ", - "ᆀ": "ᅩᅥ丨", - "ힰ": "ᅩᅧ", - "ᆁ": "ᅩᅧ丨", - "ᆂ": "ᅩᅩ", - "ힱ": "ᅩᅩ丨", - "ᆃ": "ᅩᅮ", - "ᅬ": "ᅩ丨", - "ㅚ": "ᅩ丨", - "ㅛ": "ᅭ", - "ힲ": "ᅭᅡ", - "ힳ": "ᅭᅡ丨", - "ᆄ": "ᅭᅣ", - "ㆇ": "ᅭᅣ", - "ᆆ": "ᅭᅣ", - "ᆅ": "ᅭᅣ丨", - "ㆈ": "ᅭᅣ丨", - "ힴ": "ᅭᅥ", - "ᆇ": "ᅭᅩ", - "ᆈ": "ᅭ丨", - "ㆉ": "ᅭ丨", - "ㅜ": "ᅮ", - "ᆉ": "ᅮᅡ", - "ᆊ": "ᅮᅡ丨", - "ᅯ": "ᅮᅥ", - "ㅝ": "ᅮᅥ", - "ᆋ": "ᅮᅥー", - "ᅰ": "ᅮᅥ丨", - "ㅞ": "ᅮᅥ丨", - "ힵ": "ᅮᅧ", - "ᆌ": "ᅮᅧ丨", - "ᆍ": "ᅮᅮ", - "ᅱ": "ᅮ丨", - "ㅟ": "ᅮ丨", - "ힶ": "ᅮ丨丨", - "ㅠ": "ᅲ", - "ᆎ": "ᅲᅡ", - "ힷ": "ᅲᅡ丨", - "ᆏ": "ᅲᅥ", - "ᆐ": "ᅲᅥ丨", - "ᆑ": "ᅲᅧ", - "ㆊ": "ᅲᅧ", - "ᆒ": "ᅲᅧ丨", - "ㆋ": "ᅲᅧ丨", - "ힸ": "ᅲᅩ", - "ᆓ": "ᅲᅮ", - "ᆔ": "ᅲ丨", - "ㆌ": "ᅲ丨", - "ㆍ": "ᆞ", - "ퟅ": "ᆞᅡ", - "ᆟ": "ᆞᅥ", - "ퟆ": "ᆞᅥ丨", - "ᆠ": "ᆞᅮ", - "ᆢ": "ᆞᆞ", - "ᆡ": "ᆞ丨", - "ㆎ": "ᆞ丨", - "ヘ": "へ", - "⍁": "〼", - "⧄": "〼", - "꒞": "ꁊ", - "꒬": "ꁐ", - "꒜": "ꃀ", - "꒨": "ꄲ", - "꒿": "ꉙ", - "꒾": "ꊱ", - "꒔": "ꋍ", - "꓀": "ꎫ", - "꓂": "ꎵ", - "꒺": "ꎿ", - "꒰": "ꏂ", - "꒧": "ꑘ", - "⊥": "ꓕ", - "⟂": "ꓕ", - "𝈜": "ꓕ", - "Ʇ": "ꓕ", - "Ꞟ": "ꓤ", - "⅁": "ꓨ", - "⅂": "ꓶ", - "𝈕": "ꓶ", - "𝈫": "ꓶ", - "𖼦": "ꓶ", - "𐐑": "ꓶ", - "⅃": "𖼀", - "𑫦": "𑫥𑫯", - "𑫨": "𑫥𑫥", - "𑫩": "𑫥𑫥𑫯", - "𑫪": "𑫥𑫥𑫰", - "𑫧": "𑫥𑫰", - "𑫴": "𑫳𑫯", - "𑫶": "𑫳𑫳", - "𑫷": "𑫳𑫳𑫯", - "𑫸": "𑫳𑫳𑫰", - "𑫵": "𑫳𑫰", - "𑫬": "𑫫𑫯", - "𑫭": "𑫫𑫫", - "𑫮": "𑫫𑫫𑫯", - "⊕": "𐊨", - "⨁": "𐊨", - "🜨": "𐊨", - "Ꚛ": "𐊨", - "▽": "𐊼", - "𝈔": "𐊼", - "🜄": "𐊼", - "⧖": "𐋀", - "ꞛ": "𐐺", - "Ꞛ": "𐐒", - "𐒠": "𐒆", - "𐏑": "𐎂", - "𐏓": "𐎓", - "𒀸": "𐎚", - "☥": "𐦞", - "𓋹": "𐦞", - "〹": "卄", - "不": "不", - "丽": "丽", - "並": "並", - "⎜": "丨", - "⎟": "丨", - "⎢": "丨", - "⎥": "丨", - "⎪": "丨", - "⎮": "丨", - "㇑": "丨", - "ᅵ": "丨", - "ㅣ": "丨", - "⼁": "丨", - "ᆜ": "丨ー", - "ᆘ": "丨ᅡ", - "ᆙ": "丨ᅣ", - "ힽ": "丨ᅣᅩ", - "ힾ": "丨ᅣ丨", - "ힿ": "丨ᅧ", - "ퟀ": "丨ᅧ丨", - "ᆚ": "丨ᅩ", - "ퟁ": "丨ᅩ丨", - "ퟂ": "丨ᅭ", - "ᆛ": "丨ᅮ", - "ퟃ": "丨ᅲ", - "ᆝ": "丨ᆞ", - "ퟄ": "丨丨", - "串": "串", - "丸": "丸", - "丹": "丹", - "乁": "乁", - "㇠": "乙", - "⼄": "乙", - "㇟": "乚", - "⺃": "乚", - "㇖": "乛", - "⺂": "乛", - "⻲": "亀", - "亂": "亂", - "㇚": "亅", - "⼅": "亅", - "了": "了", - "ニ": "二", - "⼆": "二", - "𠄢": "𠄢", - "⼇": "亠", - "亮": "亮", - "⼈": "人", - "イ": "亻", - "⺅": "亻", - "什": "什", - "仌": "仌", - "令": "令", - "你": "你", - "倂": "併", - "倂": "併", - "侀": "侀", - "來": "來", - "例": "例", - "侮": "侮", - "侮": "侮", - "侻": "侻", - "便": "便", - "值": "値", - "倫": "倫", - "偺": "偺", - "備": "備", - "像": "像", - "僚": "僚", - "僧": "僧", - "僧": "僧", - "㒞": "㒞", - "⼉": "儿", - "兀": "兀", - "⺎": "兀", - "充": "充", - "免": "免", - "免": "免", - "兔": "兔", - "兤": "兤", - "⼊": "入", - "內": "內", - "全": "全", - "兩": "兩", - "ハ": "八", - "⼋": "八", - "六": "六", - "具": "具", - "𠔜": "𠔜", - "𠔥": "𠔥", - "冀": "冀", - "㒹": "㒹", - "⼌": "冂", - "再": "再", - "𠕋": "𠕋", - "冒": "冒", - "冕": "冕", - "㒻": "㒻", - "最": "最", - "⼍": "冖", - "冗": "冗", - "冤": "冤", - "⼎": "冫", - "冬": "冬", - "况": "况", - "况": "况", - "冷": "冷", - "凉": "凉", - "凌": "凌", - "凜": "凜", - "凞": "凞", - "⼏": "几", - "𠘺": "𠘺", - "凵": "凵", - "⼐": "凵", - "⼑": "刀", - "⺉": "刂", - "刃": "刃", - "切": "切", - "切": "切", - "列": "列", - "利": "利", - "㓟": "㓟", - "刺": "刺", - "刻": "刻", - "剆": "剆", - "割": "割", - "剷": "剷", - "劉": "劉", - "𠠄": "𠠄", - "カ": "力", - "力": "力", - "⼒": "力", - "劣": "劣", - "㔕": "㔕", - "劳": "劳", - "勇": "勇", - "勇": "勇", - "勉": "勉", - "勉": "勉", - "勒": "勒", - "勞": "勞", - "勤": "勤", - "勤": "勤", - "勵": "勵", - "⼓": "勹", - "勺": "勺", - "勺": "勺", - "包": "包", - "匆": "匆", - "𠣞": "𠣞", - "⼔": "匕", - "北": "北", - "北": "北", - "⼕": "匚", - "⼖": "匸", - "匿": "匿", - "⼗": "十", - "〸": "十", - "〺": "卅", - "卉": "卉", - "࿖": "卍", - "࿕": "卐", - "卑": "卑", - "卑": "卑", - "博": "博", - "ト": "卜", - "⼘": "卜", - "⼙": "卩", - "⺋": "㔾", - "即": "即", - "卵": "卵", - "卽": "卽", - "卿": "卿", - "卿": "卿", - "卿": "卿", - "⼚": "厂", - "𠨬": "𠨬", - "⼛": "厶", - "參": "參", - "⼜": "又", - "及": "及", - "叟": "叟", - "𠭣": "𠭣", - "ロ": "口", - "⼝": "口", - "囗": "口", - "⼞": "口", - "句": "句", - "叫": "叫", - "叱": "叱", - "吆": "吆", - "吏": "吏", - "吝": "吝", - "吸": "吸", - "呂": "呂", - "呈": "呈", - "周": "周", - "咞": "咞", - "咢": "咢", - "咽": "咽", - "䎛": "㖈", - "哶": "哶", - "唐": "唐", - "啓": "啓", - "啟": "啓", - "啕": "啕", - "啣": "啣", - "善": "善", - "善": "善", - "喇": "喇", - "喙": "喙", - "喙": "喙", - "喝": "喝", - "喝": "喝", - "喫": "喫", - "喳": "喳", - "嗀": "嗀", - "嗂": "嗂", - "嗢": "嗢", - "嘆": "嘆", - "嘆": "嘆", - "噑": "噑", - "噴": "噴", - "器": "器", - "囹": "囹", - "圖": "圖", - "圗": "圗", - "⼟": "土", - "士": "土", - "⼠": "土", - "型": "型", - "城": "城", - "㦳": "㘽", - "埴": "埴", - "堍": "堍", - "報": "報", - "堲": "堲", - "塀": "塀", - "塚": "塚", - "塚": "塚", - "塞": "塞", - "填": "塡", - "壿": "墫", - "墬": "墬", - "墳": "墳", - "壘": "壘", - "壟": "壟", - "𡓤": "𡓤", - "壮": "壮", - "売": "売", - "壷": "壷", - "⼡": "夂", - "夆": "夆", - "⼢": "夊", - "タ": "夕", - "⼣": "夕", - "多": "多", - "夢": "夢", - "⼤": "大", - "奄": "奄", - "奈": "奈", - "契": "契", - "奔": "奔", - "奢": "奢", - "女": "女", - "⼥": "女", - "𡚨": "𡚨", - "𡛪": "𡛪", - "姘": "姘", - "姬": "姬", - "娛": "娛", - "娧": "娧", - "婢": "婢", - "婦": "婦", - "嬀": "媯", - "㛮": "㛮", - "㛼": "㛼", - "媵": "媵", - "嬈": "嬈", - "嬨": "嬨", - "嬾": "嬾", - "嬾": "嬾", - "⼦": "子", - "⼧": "宀", - "宅": "宅", - "𡧈": "𡧈", - "寃": "寃", - "寘": "寘", - "寧": "寧", - "寧": "寧", - "寧": "寧", - "寮": "寮", - "寳": "寳", - "𡬘": "𡬘", - "⼨": "寸", - "寿": "寿", - "将": "将", - "⼩": "小", - "尢": "尢", - "⺐": "尢", - "⼪": "尢", - "⺏": "尣", - "㞁": "㞁", - "⼫": "尸", - "尿": "尿", - "屠": "屠", - "屢": "屢", - "層": "層", - "履": "履", - "屮": "屮", - "屮": "屮", - "⼬": "屮", - "𡴋": "𡴋", - "⼭": "山", - "峀": "峀", - "岍": "岍", - "𡷤": "𡷤", - "𡷦": "𡷦", - "崙": "崙", - "嵃": "嵃", - "嵐": "嵐", - "嵫": "嵫", - "嵮": "嵮", - "嵼": "嵼", - "嶲": "嶲", - "嶺": "嶺", - "⼮": "巛", - "巢": "巢", - "エ": "工", - "⼯": "工", - "⼰": "己", - "⺒": "巳", - "㠯": "㠯", - "巽": "巽", - "⼱": "巾", - "帲": "帡", - "帨": "帨", - "帽": "帽", - "幩": "幩", - "㡢": "㡢", - "𢆃": "𢆃", - "⼲": "干", - "年": "年", - "𢆟": "𢆟", - "⺓": "幺", - "⼳": "幺", - "⼴": "广", - "度": "度", - "㡼": "㡼", - "庰": "庰", - "庳": "庳", - "庶": "庶", - "廊": "廊", - "廊": "廊", - "廉": "廉", - "廒": "廒", - "廓": "廓", - "廙": "廙", - "廬": "廬", - "⼵": "廴", - "廾": "廾", - "⼶": "廾", - "𢌱": "𢌱", - "𢌱": "𢌱", - "弄": "弄", - "⼷": "弋", - "⼸": "弓", - "弢": "弢", - "弢": "弢", - "⼹": "彐", - "⺔": "彑", - "当": "当", - "㣇": "㣇", - "⼺": "彡", - "形": "形", - "彩": "彩", - "彫": "彫", - "⼻": "彳", - "律": "律", - "㣣": "㣣", - "徚": "徚", - "復": "復", - "徭": "徭", - "⼼": "心", - "⺖": "忄", - "⺗": "㣺", - "忍": "忍", - "志": "志", - "念": "念", - "忹": "忹", - "怒": "怒", - "怜": "怜", - "恵": "恵", - "㤜": "㤜", - "㤺": "㤺", - "悁": "悁", - "悔": "悔", - "悔": "悔", - "惇": "惇", - "惘": "惘", - "惡": "惡", - "𢛔": "𢛔", - "愈": "愈", - "慨": "慨", - "慄": "慄", - "慈": "慈", - "慌": "慌", - "慌": "慌", - "慎": "慎", - "慎": "慎", - "慠": "慠", - "慺": "慺", - "憎": "憎", - "憎": "憎", - "憎": "憎", - "憐": "憐", - "憤": "憤", - "憯": "憯", - "憲": "憲", - "𢡄": "𢡄", - "𢡊": "𢡊", - "懞": "懞", - "懲": "懲", - "懲": "懲", - "懲": "懲", - "懶": "懶", - "懶": "懶", - "戀": "戀", - "⼽": "戈", - "成": "成", - "戛": "戛", - "戮": "戮", - "戴": "戴", - "⼾": "戶", - "戸": "戶", - "⼿": "手", - "⺘": "扌", - "扝": "扝", - "抱": "抱", - "拉": "拉", - "拏": "拏", - "拓": "拓", - "拔": "拔", - "拼": "拼", - "拾": "拾", - "𢬌": "𢬌", - "挽": "挽", - "捐": "捐", - "捨": "捨", - "捻": "捻", - "掃": "掃", - "掠": "掠", - "掩": "掩", - "揄": "揄", - "揤": "揤", - "摒": "摒", - "𢯱": "𢯱", - "搜": "搜", - "搢": "搢", - "揅": "揅", - "摩": "摩", - "摷": "摷", - "摾": "摾", - "㨮": "㨮", - "搉": "㩁", - "撚": "撚", - "撝": "撝", - "擄": "擄", - "㩬": "㩬", - "⽀": "支", - "⽁": "攴", - "⺙": "攵", - "敏": "敏", - "敏": "敏", - "敖": "敖", - "敬": "敬", - "數": "數", - "𣀊": "𣀊", - "⽂": "文", - "⻫": "斉", - "⽃": "斗", - "料": "料", - "⽄": "斤", - "⽅": "方", - "旅": "旅", - "⽆": "无", - "⺛": "旡", - "既": "既", - "旣": "旣", - "⽇": "日", - "易": "易", - "曶": "㫚", - "㫤": "㫤", - "晉": "晉", - "晩": "晚", - "晴": "晴", - "晴": "晴", - "暑": "暑", - "暑": "暑", - "暈": "暈", - "㬈": "㬈", - "暜": "暜", - "暴": "暴", - "曆": "曆", - "㬙": "㬙", - "𣊸": "𣊸", - "⽈": "曰", - "更": "更", - "書": "書", - "⽉": "月", - "𣍟": "𣍟", - "肦": "朌", - "胐": "朏", - "胊": "朐", - "脁": "朓", - "胶": "㬵", - "朗": "朗", - "朗": "朗", - "朗": "朗", - "脧": "朘", - "望": "望", - "望": "望", - "幐": "㬺", - "䐠": "㬻", - "𣎓": "𣎓", - "膧": "朣", - "𣎜": "𣎜", - "⽊": "木", - "李": "李", - "杓": "杓", - "杖": "杖", - "杞": "杞", - "𣏃": "𣏃", - "柿": "杮", - "杻": "杻", - "枅": "枅", - "林": "林", - "㭉": "㭉", - "𣏕": "𣏕", - "柳": "柳", - "柺": "柺", - "栗": "栗", - "栟": "栟", - "桒": "桒", - "𣑭": "𣑭", - "梁": "梁", - "梅": "梅", - "梅": "梅", - "梎": "梎", - "梨": "梨", - "椔": "椔", - "楂": "楂", - "㮝": "㮝", - "㮝": "㮝", - "槩": "㮣", - "樧": "榝", - "榣": "榣", - "槪": "槪", - "樂": "樂", - "樂": "樂", - "樂": "樂", - "樓": "樓", - "𣚣": "𣚣", - "檨": "檨", - "櫓": "櫓", - "櫛": "櫛", - "欄": "欄", - "㰘": "㰘", - "⽋": "欠", - "次": "次", - "𣢧": "𣢧", - "歔": "歔", - "㱎": "㱎", - "⽌": "止", - "⻭": "歯", - "歲": "歲", - "歷": "歷", - "歹": "歹", - "⽍": "歹", - "⺞": "歺", - "殟": "殟", - "殮": "殮", - "⽎": "殳", - "殺": "殺", - "殺": "殺", - "殺": "殺", - "殻": "殻", - "𣪍": "𣪍", - "⽏": "毋", - "⺟": "母", - "𣫺": "𣫺", - "⽐": "比", - "⽑": "毛", - "⽒": "氏", - "⺠": "民", - "⽓": "气", - "⽔": "水", - "⺡": "氵", - "⺢": "氺", - "汎": "汎", - "汧": "汧", - "沈": "沈", - "沿": "沿", - "泌": "泌", - "泍": "泍", - "泥": "泥", - "𣲼": "𣲼", - "洛": "洛", - "洞": "洞", - "洴": "洴", - "派": "派", - "流": "流", - "流": "流", - "流": "流", - "洖": "洖", - "浩": "浩", - "浪": "浪", - "海": "海", - "海": "海", - "浸": "浸", - "涅": "涅", - "𣴞": "𣴞", - "淋": "淋", - "淚": "淚", - "淪": "淪", - "淹": "淹", - "渚": "渚", - "港": "港", - "湮": "湮", - "潙": "溈", - "滋": "滋", - "滋": "滋", - "溜": "溜", - "溺": "溺", - "滇": "滇", - "滑": "滑", - "滛": "滛", - "㴳": "㴳", - "漏": "漏", - "漢": "漢", - "漢": "漢", - "漣": "漣", - "𣻑": "𣻑", - "潮": "潮", - "𣽞": "𣽞", - "𣾎": "𣾎", - "濆": "濆", - "濫": "濫", - "濾": "濾", - "瀛": "瀛", - "瀞": "瀞", - "瀞": "瀞", - "瀹": "瀹", - "灊": "灊", - "㶖": "㶖", - "⽕": "火", - "⺣": "灬", - "灰": "灰", - "灷": "灷", - "災": "災", - "炙": "炙", - "炭": "炭", - "烈": "烈", - "烙": "烙", - "煮": "煮", - "煮": "煮", - "𤉣": "𤉣", - "煅": "煅", - "煉": "煉", - "𤋮": "𤋮", - "熜": "熜", - "燎": "燎", - "燐": "燐", - "𤎫": "𤎫", - "爐": "爐", - "爛": "爛", - "爨": "爨", - "⽖": "爪", - "爫": "爫", - "⺤": "爫", - "爵": "爵", - "爵": "爵", - "⽗": "父", - "⽘": "爻", - "⺦": "丬", - "⽙": "爿", - "⽚": "片", - "牐": "牐", - "⽛": "牙", - "𤘈": "𤘈", - "⽜": "牛", - "牢": "牢", - "犀": "犀", - "犕": "犕", - "⽝": "犬", - "⺨": "犭", - "犯": "犯", - "狀": "狀", - "𤜵": "𤜵", - "狼": "狼", - "猪": "猪", - "猪": "猪", - "𤠔": "𤠔", - "獵": "獵", - "獺": "獺", - "⽞": "玄", - "率": "率", - "率": "率", - "⽟": "玉", - "王": "王", - "㺬": "㺬", - "玥": "玥", - "玲": "玲", - "㺸": "㺸", - "㺸": "㺸", - "珞": "珞", - "琉": "琉", - "理": "理", - "琢": "琢", - "瑇": "瑇", - "瑜": "瑜", - "瑩": "瑩", - "瑱": "瑱", - "瑱": "瑱", - "璅": "璅", - "璉": "璉", - "璘": "璘", - "瓊": "瓊", - "⽠": "瓜", - "⽡": "瓦", - "㼛": "㼛", - "甆": "甆", - "⽢": "甘", - "⽣": "生", - "甤": "甤", - "⽤": "用", - "⽥": "田", - "画": "画", - "甾": "甾", - "𤰶": "𤰶", - "留": "留", - "略": "略", - "異": "異", - "異": "異", - "𤲒": "𤲒", - "⽦": "疋", - "⽧": "疒", - "痢": "痢", - "瘐": "瘐", - "瘟": "瘟", - "瘝": "瘝", - "療": "療", - "癩": "癩", - "⽨": "癶", - "⽩": "白", - "𤾡": "𤾡", - "𤾸": "𤾸", - "⽪": "皮", - "⽫": "皿", - "𥁄": "𥁄", - "㿼": "㿼", - "益": "益", - "益": "益", - "盛": "盛", - "盧": "盧", - "䀈": "䀈", - "⽬": "目", - "直": "直", - "直": "直", - "𥃲": "𥃲", - "𥃳": "𥃳", - "省": "省", - "䀘": "䀘", - "𥄙": "𥄙", - "眞": "眞", - "真": "真", - "真": "真", - "𥄳": "𥄳", - "着": "着", - "睊": "睊", - "睊": "睊", - "鿃": "䀹", - "䀹": "䀹", - "䀹": "䀹", - "晣": "䀿", - "䁆": "䁆", - "瞋": "瞋", - "𥉉": "𥉉", - "瞧": "瞧", - "⽭": "矛", - "⽮": "矢", - "⽯": "石", - "䂖": "䂖", - "𥐝": "𥐝", - "硏": "研", - "硎": "硎", - "硫": "硫", - "碌": "碌", - "碌": "碌", - "碑": "碑", - "磊": "磊", - "磌": "磌", - "磌": "磌", - "磻": "磻", - "䃣": "䃣", - "礪": "礪", - "⽰": "示", - "⺭": "礻", - "礼": "礼", - "社": "社", - "祈": "祈", - "祉": "祉", - "𥘦": "𥘦", - "祐": "祐", - "祖": "祖", - "祖": "祖", - "祝": "祝", - "神": "神", - "祥": "祥", - "視": "視", - "視": "視", - "祿": "祿", - "𥚚": "𥚚", - "禍": "禍", - "禎": "禎", - "福": "福", - "福": "福", - "𥛅": "𥛅", - "禮": "禮", - "⽱": "禸", - "⽲": "禾", - "秊": "秊", - "䄯": "䄯", - "秫": "秫", - "稜": "稜", - "穊": "穊", - "穀": "穀", - "穀": "穀", - "穏": "穏", - "⽳": "穴", - "突": "突", - "𥥼": "𥥼", - "窱": "窱", - "立": "立", - "⽴": "立", - "⻯": "竜", - "𥪧": "𥪧", - "𥪧": "𥪧", - "竮": "竮", - "⽵": "竹", - "笠": "笠", - "節": "節", - "節": "節", - "䈂": "䈂", - "𥮫": "𥮫", - "篆": "篆", - "䈧": "䈧", - "築": "築", - "𥲀": "𥲀", - "𥳐": "𥳐", - "簾": "簾", - "籠": "籠", - "⽶": "米", - "类": "类", - "粒": "粒", - "精": "精", - "糒": "糒", - "糖": "糖", - "糨": "糨", - "䊠": "䊠", - "糣": "糣", - "糧": "糧", - "⽷": "糸", - "⺯": "糹", - "𥾆": "𥾆", - "紀": "紀", - "紐": "紐", - "索": "索", - "累": "累", - "絶": "絕", - "絣": "絣", - "絛": "絛", - "綠": "綠", - "綾": "綾", - "緇": "緇", - "練": "練", - "練": "練", - "練": "練", - "縂": "縂", - "䌁": "䌁", - "縉": "縉", - "縷": "縷", - "繁": "繁", - "繅": "繅", - "𦇚": "𦇚", - "䌴": "䌴", - "⽸": "缶", - "𦈨": "𦈨", - "缾": "缾", - "𦉇": "𦉇", - "⽹": "网", - "⺫": "罒", - "⺲": "罒", - "⺱": "罓", - "䍙": "䍙", - "署": "署", - "𦋙": "𦋙", - "罹": "罹", - "罺": "罺", - "羅": "羅", - "𦌾": "𦌾", - "⽺": "羊", - "羕": "羕", - "羚": "羚", - "羽": "羽", - "⽻": "羽", - "翺": "翺", - "老": "老", - "⽼": "老", - "⺹": "耂", - "者": "者", - "者": "者", - "者": "者", - "⽽": "而", - "𦓚": "𦓚", - "⽾": "耒", - "𦔣": "𦔣", - "⽿": "耳", - "聆": "聆", - "聠": "聠", - "𦖨": "𦖨", - "聯": "聯", - "聰": "聰", - "聾": "聾", - "⾀": "聿", - "⺺": "肀", - "⾁": "肉", - "肋": "肋", - "肭": "肭", - "育": "育", - "䏕": "䏕", - "䏙": "䏙", - "腁": "胼", - "脃": "脃", - "脾": "脾", - "䐋": "䐋", - "朡": "朡", - "𦞧": "𦞧", - "𦞵": "𦞵", - "朦": "䑃", - "臘": "臘", - "⾂": "臣", - "臨": "臨", - "⾃": "自", - "臭": "臭", - "⾄": "至", - "⾅": "臼", - "舁": "舁", - "舁": "舁", - "舄": "舄", - "⾆": "舌", - "舘": "舘", - "⾇": "舛", - "⾈": "舟", - "䑫": "䑫", - "⾉": "艮", - "良": "良", - "⾊": "色", - "⾋": "艸", - "艹": "艹", - "艹": "艹", - "⺾": "艹", - "⺿": "艹", - "⻀": "艹", - "芋": "芋", - "芑": "芑", - "芝": "芝", - "花": "花", - "芳": "芳", - "芽": "芽", - "若": "若", - "若": "若", - "苦": "苦", - "𦬼": "𦬼", - "茶": "茶", - "荒": "荒", - "荣": "荣", - "茝": "茝", - "茣": "茣", - "莽": "莽", - "荓": "荓", - "菉": "菉", - "菊": "菊", - "菌": "菌", - "菜": "菜", - "菧": "菧", - "華": "華", - "菱": "菱", - "著": "著", - "著": "著", - "𦰶": "𦰶", - "莭": "莭", - "落": "落", - "葉": "葉", - "蔿": "蒍", - "𦳕": "𦳕", - "𦵫": "𦵫", - "蓮": "蓮", - "蓱": "蓱", - "蓳": "蓳", - "蓼": "蓼", - "蔖": "蔖", - "䔫": "䔫", - "蕤": "蕤", - "𦼬": "𦼬", - "藍": "藍", - "䕝": "䕝", - "𦾱": "𦾱", - "䕡": "䕡", - "藺": "藺", - "蘆": "蘆", - "䕫": "䕫", - "蘒": "蘒", - "蘭": "蘭", - "𧃒": "𧃒", - "虁": "蘷", - "蘿": "蘿", - "⾌": "虍", - "⻁": "虎", - "虐": "虐", - "虜": "虜", - "虜": "虜", - "虧": "虧", - "虩": "虩", - "⾍": "虫", - "蚩": "蚩", - "蚈": "蚈", - "蛢": "蛢", - "蜎": "蜎", - "蜨": "蜨", - "蝫": "蝫", - "蟡": "蟡", - "蝹": "蝹", - "蝹": "蝹", - "螆": "螆", - "䗗": "䗗", - "𧏊": "𧏊", - "螺": "螺", - "蠁": "蠁", - "䗹": "䗹", - "蠟": "蠟", - "⾎": "血", - "行": "行", - "⾏": "行", - "衠": "衠", - "衣": "衣", - "⾐": "衣", - "⻂": "衤", - "裂": "裂", - "𧙧": "𧙧", - "裏": "裏", - "裗": "裗", - "裞": "裞", - "裡": "裡", - "裸": "裸", - "裺": "裺", - "䘵": "䘵", - "褐": "褐", - "襁": "襁", - "襤": "襤", - "⾑": "襾", - "⻄": "西", - "⻃": "覀", - "覆": "覆", - "見": "見", - "⾒": "見", - "𧢮": "𧢮", - "⻅": "见", - "⾓": "角", - "⾔": "言", - "𧥦": "𧥦", - "詽": "訮", - "訞": "䚶", - "䚾": "䚾", - "䛇": "䛇", - "誠": "誠", - "說": "說", - "說": "說", - "調": "調", - "請": "請", - "諒": "諒", - "論": "論", - "諭": "諭", - "諭": "諭", - "諸": "諸", - "諸": "諸", - "諾": "諾", - "諾": "諾", - "謁": "謁", - "謁": "謁", - "謹": "謹", - "謹": "謹", - "識": "識", - "讀": "讀", - "讏": "讆", - "變": "變", - "變": "變", - "⻈": "讠", - "⾕": "谷", - "⾖": "豆", - "豈": "豈", - "豕": "豕", - "⾗": "豕", - "豣": "豜", - "⾘": "豸", - "𧲨": "𧲨", - "⾙": "貝", - "貫": "貫", - "賁": "賁", - "賂": "賂", - "賈": "賈", - "賓": "賓", - "贈": "贈", - "贈": "贈", - "贛": "贛", - "⻉": "贝", - "⾚": "赤", - "⾛": "走", - "起": "起", - "趆": "赿", - "𧻓": "𧻓", - "𧼯": "𧼯", - "⾜": "足", - "跋": "跋", - "趼": "趼", - "跺": "跥", - "路": "路", - "跰": "跰", - "躛": "躗", - "⾝": "身", - "車": "車", - "⾞": "車", - "軔": "軔", - "輧": "軿", - "輦": "輦", - "輪": "輪", - "輸": "輸", - "輸": "輸", - "輻": "輻", - "轢": "轢", - "⻋": "车", - "⾟": "辛", - "辞": "辞", - "辰": "辰", - "⾠": "辰", - "⾡": "辵", - "辶": "辶", - "⻌": "辶", - "⻍": "辶", - "巡": "巡", - "連": "連", - "逸": "逸", - "逸": "逸", - "遲": "遲", - "遼": "遼", - "𨗒": "𨗒", - "𨗭": "𨗭", - "邏": "邏", - "⾢": "邑", - "邔": "邔", - "郎": "郎", - "郞": "郎", - "郞": "郎", - "郱": "郱", - "都": "都", - "𨜮": "𨜮", - "鄑": "鄑", - "鄛": "鄛", - "⾣": "酉", - "酪": "酪", - "醙": "醙", - "醴": "醴", - "⾤": "釆", - "里": "里", - "⾥": "里", - "量": "量", - "金": "金", - "⾦": "金", - "鈴": "鈴", - "鈸": "鈸", - "鉶": "鉶", - "鋗": "鋗", - "鋘": "鋘", - "鉼": "鉼", - "錄": "錄", - "鍊": "鍊", - "鎮": "鎭", - "鏹": "鏹", - "鐕": "鐕", - "𨯺": "𨯺", - "⻐": "钅", - "⻑": "長", - "⾧": "長", - "⻒": "镸", - "⻓": "长", - "⾨": "門", - "開": "開", - "䦕": "䦕", - "閭": "閭", - "閷": "閷", - "𨵷": "𨵷", - "⻔": "门", - "⾩": "阜", - "⻏": "阝", - "⻖": "阝", - "阮": "阮", - "陋": "陋", - "降": "降", - "陵": "陵", - "陸": "陸", - "陼": "陼", - "隆": "隆", - "隣": "隣", - "䧦": "䧦", - "⾪": "隶", - "隷": "隷", - "隸": "隷", - "隸": "隷", - "⾫": "隹", - "雃": "雃", - "離": "離", - "難": "難", - "難": "難", - "⾬": "雨", - "零": "零", - "雷": "雷", - "霣": "霣", - "𩅅": "𩅅", - "露": "露", - "靈": "靈", - "⾭": "靑", - "⻘": "青", - "靖": "靖", - "靖": "靖", - "𩇟": "𩇟", - "⾮": "非", - "⾯": "面", - "𩈚": "𩈚", - "⾰": "革", - "䩮": "䩮", - "䩶": "䩶", - "⾱": "韋", - "韛": "韛", - "韠": "韠", - "⻙": "韦", - "⾲": "韭", - "𩐊": "𩐊", - "⾳": "音", - "響": "響", - "響": "響", - "⾴": "頁", - "䪲": "䪲", - "頋": "頋", - "頋": "頋", - "頋": "頋", - "領": "領", - "頩": "頩", - "𩒖": "𩒖", - "頻": "頻", - "頻": "頻", - "類": "類", - "⻚": "页", - "⾵": "風", - "𩖶": "𩖶", - "⻛": "风", - "⾶": "飛", - "⻜": "飞", - "⻝": "食", - "⾷": "食", - "⻟": "飠", - "飢": "飢", - "飯": "飯", - "飼": "飼", - "䬳": "䬳", - "館": "館", - "餩": "餩", - "⻠": "饣", - "⾸": "首", - "⾹": "香", - "馧": "馧", - "⾺": "馬", - "駂": "駂", - "駱": "駱", - "駾": "駾", - "驪": "驪", - "⻢": "马", - "⾻": "骨", - "䯎": "䯎", - "⾼": "高", - "⾽": "髟", - "𩬰": "𩬰", - "鬒": "鬒", - "鬒": "鬒", - "⾾": "鬥", - "⾿": "鬯", - "⿀": "鬲", - "⿁": "鬼", - "⻤": "鬼", - "⿂": "魚", - "魯": "魯", - "鱀": "鱀", - "鱗": "鱗", - "⻥": "鱼", - "⿃": "鳥", - "鳽": "鳽", - "䳎": "䳎", - "鵧": "鵧", - "䳭": "䳭", - "𪃎": "𪃎", - "鶴": "鶴", - "𪄅": "𪄅", - "䳸": "䳸", - "鷺": "鷺", - "𪈎": "𪈎", - "鸞": "鸞", - "鹃": "鹂", - "⿄": "鹵", - "鹿": "鹿", - "⿅": "鹿", - "𪊑": "𪊑", - "麗": "麗", - "麟": "麟", - "⿆": "麥", - "⻨": "麦", - "麻": "麻", - "⿇": "麻", - "𪎒": "𪎒", - "⿈": "黃", - "⻩": "黄", - "⿉": "黍", - "黎": "黎", - "䵖": "䵖", - "⿊": "黑", - "黒": "黑", - "墨": "墨", - "黹": "黹", - "⿋": "黹", - "⿌": "黽", - "鼅": "鼅", - "黾": "黾", - "⿍": "鼎", - "鼏": "鼏", - "⿎": "鼓", - "鼖": "鼖", - "⿏": "鼠", - "鼻": "鼻", - "⿐": "鼻", - "齃": "齃", - "⿑": "齊", - "⻬": "齐", - "⿒": "齒", - "𪘀": "𪘀", - "⻮": "齿", - "龍": "龍", - "⿓": "龍", - "龎": "龎", - "⻰": "龙", - "龜": "龜", - "龜": "龜", - "龜": "龜", - "⿔": "龜", - "⻳": "龟", - "⿕": "龠" -} -},{}],282:[function(require,module,exports){ -'use strict'; - - -var data = require('./data.json'); - -function escapeRegexp(str) { - return str.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); -} - -var REPLACE_RE = RegExp(Object.keys(data).map(escapeRegexp).join('|'), 'g'); - -function replace_fn(match) { - return data[match]; -} - -function unhomoglyph(str) { - return str.replace(REPLACE_RE, replace_fn); -} - -module.exports = unhomoglyph; - -},{"./data.json":281}],283:[function(require,module,exports){ -(function (global){(function (){ - -/** - * Module exports. - */ - -module.exports = deprecate; - -/** - * Mark that a method should not be used. - * Returns a modified function which warns once by default. - * - * If `localStorage.noDeprecation = true` is set, then it is a no-op. - * - * If `localStorage.throwDeprecation = true` is set, then deprecated functions - * will throw an Error when invoked. - * - * If `localStorage.traceDeprecation = true` is set, then deprecated functions - * will invoke `console.trace()` instead of `console.error()`. - * - * @param {Function} fn - the function to deprecate - * @param {String} msg - the string to print to the console when `fn` is invoked - * @returns {Function} a new "deprecated" version of `fn` - * @api public - */ - -function deprecate (fn, msg) { - if (config('noDeprecation')) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (config('throwDeprecation')) { - throw new Error(msg); - } else if (config('traceDeprecation')) { - console.trace(msg); - } else { - console.warn(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -} - -/** - * Checks `localStorage` for boolean values for the given `name`. - * - * @param {String} name - * @returns {Boolean} - * @api private - */ - -function config (name) { - // accessing global.localStorage can trigger a DOMException in sandboxed iframes - try { - if (!global.localStorage) return false; - } catch (_) { - return false; - } - var val = global.localStorage[name]; - if (null == val) return false; - return String(val).toLowerCase() === 'true'; -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{}],284:[function(require,module,exports){ -module.exports = function isBuffer(arg) { - return arg && typeof arg === 'object' - && typeof arg.copy === 'function' - && typeof arg.fill === 'function' - && typeof arg.readUInt8 === 'function'; -} -},{}],285:[function(require,module,exports){ -// Currently in sync with Node.js lib/internal/util/types.js -// https://github.com/nodejs/node/commit/112cc7c27551254aa2b17098fb774867f05ed0d9 - -'use strict'; - -var isArgumentsObject = require('is-arguments'); -var isGeneratorFunction = require('is-generator-function'); -var whichTypedArray = require('which-typed-array'); -var isTypedArray = require('is-typed-array'); - -function uncurryThis(f) { - return f.call.bind(f); -} - -var BigIntSupported = typeof BigInt !== 'undefined'; -var SymbolSupported = typeof Symbol !== 'undefined'; - -var ObjectToString = uncurryThis(Object.prototype.toString); - -var numberValue = uncurryThis(Number.prototype.valueOf); -var stringValue = uncurryThis(String.prototype.valueOf); -var booleanValue = uncurryThis(Boolean.prototype.valueOf); - -if (BigIntSupported) { - var bigIntValue = uncurryThis(BigInt.prototype.valueOf); -} - -if (SymbolSupported) { - var symbolValue = uncurryThis(Symbol.prototype.valueOf); -} - -function checkBoxedPrimitive(value, prototypeValueOf) { - if (typeof value !== 'object') { - return false; - } - try { - prototypeValueOf(value); - return true; - } catch(e) { - return false; - } -} - -exports.isArgumentsObject = isArgumentsObject; -exports.isGeneratorFunction = isGeneratorFunction; -exports.isTypedArray = isTypedArray; - -// Taken from here and modified for better browser support -// https://github.com/sindresorhus/p-is-promise/blob/cda35a513bda03f977ad5cde3a079d237e82d7ef/index.js -function isPromise(input) { - return ( - ( - typeof Promise !== 'undefined' && - input instanceof Promise - ) || - ( - input !== null && - typeof input === 'object' && - typeof input.then === 'function' && - typeof input.catch === 'function' - ) - ); -} -exports.isPromise = isPromise; - -function isArrayBufferView(value) { - if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView) { - return ArrayBuffer.isView(value); - } - - return ( - isTypedArray(value) || - isDataView(value) - ); -} -exports.isArrayBufferView = isArrayBufferView; - - -function isUint8Array(value) { - return whichTypedArray(value) === 'Uint8Array'; -} -exports.isUint8Array = isUint8Array; - -function isUint8ClampedArray(value) { - return whichTypedArray(value) === 'Uint8ClampedArray'; -} -exports.isUint8ClampedArray = isUint8ClampedArray; - -function isUint16Array(value) { - return whichTypedArray(value) === 'Uint16Array'; -} -exports.isUint16Array = isUint16Array; - -function isUint32Array(value) { - return whichTypedArray(value) === 'Uint32Array'; -} -exports.isUint32Array = isUint32Array; - -function isInt8Array(value) { - return whichTypedArray(value) === 'Int8Array'; -} -exports.isInt8Array = isInt8Array; - -function isInt16Array(value) { - return whichTypedArray(value) === 'Int16Array'; -} -exports.isInt16Array = isInt16Array; - -function isInt32Array(value) { - return whichTypedArray(value) === 'Int32Array'; -} -exports.isInt32Array = isInt32Array; - -function isFloat32Array(value) { - return whichTypedArray(value) === 'Float32Array'; -} -exports.isFloat32Array = isFloat32Array; - -function isFloat64Array(value) { - return whichTypedArray(value) === 'Float64Array'; -} -exports.isFloat64Array = isFloat64Array; - -function isBigInt64Array(value) { - return whichTypedArray(value) === 'BigInt64Array'; -} -exports.isBigInt64Array = isBigInt64Array; - -function isBigUint64Array(value) { - return whichTypedArray(value) === 'BigUint64Array'; -} -exports.isBigUint64Array = isBigUint64Array; - -function isMapToString(value) { - return ObjectToString(value) === '[object Map]'; -} -isMapToString.working = ( - typeof Map !== 'undefined' && - isMapToString(new Map()) -); - -function isMap(value) { - if (typeof Map === 'undefined') { - return false; - } - - return isMapToString.working - ? isMapToString(value) - : value instanceof Map; -} -exports.isMap = isMap; - -function isSetToString(value) { - return ObjectToString(value) === '[object Set]'; -} -isSetToString.working = ( - typeof Set !== 'undefined' && - isSetToString(new Set()) -); -function isSet(value) { - if (typeof Set === 'undefined') { - return false; - } - - return isSetToString.working - ? isSetToString(value) - : value instanceof Set; -} -exports.isSet = isSet; - -function isWeakMapToString(value) { - return ObjectToString(value) === '[object WeakMap]'; -} -isWeakMapToString.working = ( - typeof WeakMap !== 'undefined' && - isWeakMapToString(new WeakMap()) -); -function isWeakMap(value) { - if (typeof WeakMap === 'undefined') { - return false; - } - - return isWeakMapToString.working - ? isWeakMapToString(value) - : value instanceof WeakMap; -} -exports.isWeakMap = isWeakMap; - -function isWeakSetToString(value) { - return ObjectToString(value) === '[object WeakSet]'; -} -isWeakSetToString.working = ( - typeof WeakSet !== 'undefined' && - isWeakSetToString(new WeakSet()) -); -function isWeakSet(value) { - return isWeakSetToString(value); -} -exports.isWeakSet = isWeakSet; - -function isArrayBufferToString(value) { - return ObjectToString(value) === '[object ArrayBuffer]'; -} -isArrayBufferToString.working = ( - typeof ArrayBuffer !== 'undefined' && - isArrayBufferToString(new ArrayBuffer()) -); -function isArrayBuffer(value) { - if (typeof ArrayBuffer === 'undefined') { - return false; - } - - return isArrayBufferToString.working - ? isArrayBufferToString(value) - : value instanceof ArrayBuffer; -} -exports.isArrayBuffer = isArrayBuffer; - -function isDataViewToString(value) { - return ObjectToString(value) === '[object DataView]'; -} -isDataViewToString.working = ( - typeof ArrayBuffer !== 'undefined' && - typeof DataView !== 'undefined' && - isDataViewToString(new DataView(new ArrayBuffer(1), 0, 1)) -); -function isDataView(value) { - if (typeof DataView === 'undefined') { - return false; - } - - return isDataViewToString.working - ? isDataViewToString(value) - : value instanceof DataView; -} -exports.isDataView = isDataView; - -// Store a copy of SharedArrayBuffer in case it's deleted elsewhere -var SharedArrayBufferCopy = typeof SharedArrayBuffer !== 'undefined' ? SharedArrayBuffer : undefined; -function isSharedArrayBufferToString(value) { - return ObjectToString(value) === '[object SharedArrayBuffer]'; -} -function isSharedArrayBuffer(value) { - if (typeof SharedArrayBufferCopy === 'undefined') { - return false; - } - - if (typeof isSharedArrayBufferToString.working === 'undefined') { - isSharedArrayBufferToString.working = isSharedArrayBufferToString(new SharedArrayBufferCopy()); - } - - return isSharedArrayBufferToString.working - ? isSharedArrayBufferToString(value) - : value instanceof SharedArrayBufferCopy; -} -exports.isSharedArrayBuffer = isSharedArrayBuffer; - -function isAsyncFunction(value) { - return ObjectToString(value) === '[object AsyncFunction]'; -} -exports.isAsyncFunction = isAsyncFunction; - -function isMapIterator(value) { - return ObjectToString(value) === '[object Map Iterator]'; -} -exports.isMapIterator = isMapIterator; - -function isSetIterator(value) { - return ObjectToString(value) === '[object Set Iterator]'; -} -exports.isSetIterator = isSetIterator; - -function isGeneratorObject(value) { - return ObjectToString(value) === '[object Generator]'; -} -exports.isGeneratorObject = isGeneratorObject; - -function isWebAssemblyCompiledModule(value) { - return ObjectToString(value) === '[object WebAssembly.Module]'; -} -exports.isWebAssemblyCompiledModule = isWebAssemblyCompiledModule; - -function isNumberObject(value) { - return checkBoxedPrimitive(value, numberValue); -} -exports.isNumberObject = isNumberObject; - -function isStringObject(value) { - return checkBoxedPrimitive(value, stringValue); -} -exports.isStringObject = isStringObject; - -function isBooleanObject(value) { - return checkBoxedPrimitive(value, booleanValue); -} -exports.isBooleanObject = isBooleanObject; - -function isBigIntObject(value) { - return BigIntSupported && checkBoxedPrimitive(value, bigIntValue); -} -exports.isBigIntObject = isBigIntObject; - -function isSymbolObject(value) { - return SymbolSupported && checkBoxedPrimitive(value, symbolValue); -} -exports.isSymbolObject = isSymbolObject; - -function isBoxedPrimitive(value) { - return ( - isNumberObject(value) || - isStringObject(value) || - isBooleanObject(value) || - isBigIntObject(value) || - isSymbolObject(value) - ); -} -exports.isBoxedPrimitive = isBoxedPrimitive; - -function isAnyArrayBuffer(value) { - return typeof Uint8Array !== 'undefined' && ( - isArrayBuffer(value) || - isSharedArrayBuffer(value) - ); -} -exports.isAnyArrayBuffer = isAnyArrayBuffer; - -['isProxy', 'isExternal', 'isModuleNamespaceObject'].forEach(function(method) { - Object.defineProperty(exports, method, { - enumerable: false, - value: function() { - throw new Error(method + ' is not supported in userland'); - } - }); -}); - -},{"is-arguments":147,"is-generator-function":149,"is-typed-array":150,"which-typed-array":303}],286:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || - function getOwnPropertyDescriptors(obj) { - var keys = Object.keys(obj); - var descriptors = {}; - for (var i = 0; i < keys.length; i++) { - descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]); - } - return descriptors; - }; - -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); - } - return objects.join(' '); - } - - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); - } - } - return str; -}; - - -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - if (typeof process !== 'undefined' && process.noDeprecation === true) { - return fn; - } - - // Allow for deprecating things in the process of starting up. - if (typeof process === 'undefined') { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -}; - - -var debugs = {}; -var debugEnvRegex = /^$/; - -if (process.env.NODE_DEBUG) { - var debugEnv = process.env.NODE_DEBUG; - debugEnv = debugEnv.replace(/[|\\{}()[\]^$+?.]/g, '\\$&') - .replace(/\*/g, '.*') - .replace(/,/g, '$|^') - .toUpperCase(); - debugEnvRegex = new RegExp('^' + debugEnv + '$', 'i'); -} -exports.debuglog = function(set) { - set = set.toUpperCase(); - if (!debugs[set]) { - if (debugEnvRegex.test(set)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } - } - return debugs[set]; -}; - - -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. - */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); - } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); -} -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; - - -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; - } -} - - -function stylizeNoColor(str, styleType) { - return str; -} - - -function arrayToHash(array) { - var hash = {}; - - array.forEach(function(val, idx) { - hash[val] = true; - }); - - return hash; -} - - -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } - - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } - - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); - - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); - } - - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); - } - - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } - - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } - - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; - } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } - - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } - - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } - - ctx.seen.push(value); - - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } - - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - - -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').slice(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.slice(1, -1); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -exports.types = require('./support/types'); - -function isArray(ar) { - return Array.isArray(ar); -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; -exports.types.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; -exports.types.isDate = isDate; - -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; -exports.types.isNativeError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = require('./support/isBuffer'); - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - - -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); -} - - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} - - -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = require('inherits'); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; - - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; - } - return origin; -}; - -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined; - -exports.promisify = function promisify(original) { - if (typeof original !== 'function') - throw new TypeError('The "original" argument must be of type Function'); - - if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) { - var fn = original[kCustomPromisifiedSymbol]; - if (typeof fn !== 'function') { - throw new TypeError('The "util.promisify.custom" argument must be of type Function'); - } - Object.defineProperty(fn, kCustomPromisifiedSymbol, { - value: fn, enumerable: false, writable: false, configurable: true - }); - return fn; - } - - function fn() { - var promiseResolve, promiseReject; - var promise = new Promise(function (resolve, reject) { - promiseResolve = resolve; - promiseReject = reject; - }); - - var args = []; - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - args.push(function (err, value) { - if (err) { - promiseReject(err); - } else { - promiseResolve(value); - } - }); - - try { - original.apply(this, args); - } catch (err) { - promiseReject(err); - } - - return promise; - } - - Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); - - if (kCustomPromisifiedSymbol) Object.defineProperty(fn, kCustomPromisifiedSymbol, { - value: fn, enumerable: false, writable: false, configurable: true - }); - return Object.defineProperties( - fn, - getOwnPropertyDescriptors(original) - ); -} - -exports.promisify.custom = kCustomPromisifiedSymbol - -function callbackifyOnRejected(reason, cb) { - // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M). - // Because `null` is a special error value in callbacks which means "no error - // occurred", we error-wrap so the callback consumer can distinguish between - // "the promise rejected with null" or "the promise fulfilled with undefined". - if (!reason) { - var newReason = new Error('Promise was rejected with a falsy value'); - newReason.reason = reason; - reason = newReason; - } - return cb(reason); -} - -function callbackify(original) { - if (typeof original !== 'function') { - throw new TypeError('The "original" argument must be of type Function'); - } - - // We DO NOT return the promise as it gives the user a false sense that - // the promise is actually somehow related to the callback's execution - // and that the callback throwing will reject the promise. - function callbackified() { - var args = []; - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - - var maybeCb = args.pop(); - if (typeof maybeCb !== 'function') { - throw new TypeError('The last argument must be of type Function'); - } - var self = this; - var cb = function() { - return maybeCb.apply(self, arguments); - }; - // In true node style we process the callback on `nextTick` with all the - // implications (stack, `uncaughtException`, `async_hooks`) - original.apply(this, args) - .then(function(ret) { process.nextTick(cb.bind(null, null, ret)) }, - function(rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) }); - } - - Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original)); - Object.defineProperties(callbackified, - getOwnPropertyDescriptors(original)); - return callbackified; -} -exports.callbackify = callbackify; - -}).call(this)}).call(this,require('_process')) - -},{"./support/isBuffer":284,"./support/types":285,"_process":237,"inherits":146}],287:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -Object.defineProperty(exports, "NIL", { - enumerable: true, - get: function () { - return _nil.default; - } -}); -Object.defineProperty(exports, "parse", { - enumerable: true, - get: function () { - return _parse.default; - } -}); -Object.defineProperty(exports, "stringify", { - enumerable: true, - get: function () { - return _stringify.default; - } -}); -Object.defineProperty(exports, "v1", { - enumerable: true, - get: function () { - return _v.default; - } -}); -Object.defineProperty(exports, "v3", { - enumerable: true, - get: function () { - return _v2.default; - } -}); -Object.defineProperty(exports, "v4", { - enumerable: true, - get: function () { - return _v3.default; - } -}); -Object.defineProperty(exports, "v5", { - enumerable: true, - get: function () { - return _v4.default; - } -}); -Object.defineProperty(exports, "validate", { - enumerable: true, - get: function () { - return _validate.default; - } -}); -Object.defineProperty(exports, "version", { - enumerable: true, - get: function () { - return _version.default; - } -}); - -var _v = _interopRequireDefault(require("./v1.js")); - -var _v2 = _interopRequireDefault(require("./v3.js")); - -var _v3 = _interopRequireDefault(require("./v4.js")); - -var _v4 = _interopRequireDefault(require("./v5.js")); - -var _nil = _interopRequireDefault(require("./nil.js")); - -var _version = _interopRequireDefault(require("./version.js")); - -var _validate = _interopRequireDefault(require("./validate.js")); - -var _stringify = _interopRequireDefault(require("./stringify.js")); - -var _parse = _interopRequireDefault(require("./parse.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -},{"./nil.js":290,"./parse.js":291,"./stringify.js":295,"./v1.js":296,"./v3.js":297,"./v4.js":299,"./v5.js":300,"./validate.js":301,"./version.js":302}],288:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -/* - * Browser-compatible JavaScript MD5 - * - * Modification of JavaScript MD5 - * https://github.com/blueimp/JavaScript-MD5 - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * https://opensource.org/licenses/MIT - * - * Based on - * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - * Digest Algorithm, as defined in RFC 1321. - * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for more info. - */ -function md5(bytes) { - if (typeof bytes === 'string') { - const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape - - bytes = new Uint8Array(msg.length); - - for (let i = 0; i < msg.length; ++i) { - bytes[i] = msg.charCodeAt(i); - } - } - - return md5ToHexEncodedArray(wordsToMd5(bytesToWords(bytes), bytes.length * 8)); -} -/* - * Convert an array of little-endian words to an array of bytes - */ - - -function md5ToHexEncodedArray(input) { - const output = []; - const length32 = input.length * 32; - const hexTab = '0123456789abcdef'; - - for (let i = 0; i < length32; i += 8) { - const x = input[i >> 5] >>> i % 32 & 0xff; - const hex = parseInt(hexTab.charAt(x >>> 4 & 0x0f) + hexTab.charAt(x & 0x0f), 16); - output.push(hex); - } - - return output; -} -/** - * Calculate output length with padding and bit length - */ - - -function getOutputLength(inputLength8) { - return (inputLength8 + 64 >>> 9 << 4) + 14 + 1; -} -/* - * Calculate the MD5 of an array of little-endian words, and a bit length. - */ - - -function wordsToMd5(x, len) { - /* append padding */ - x[len >> 5] |= 0x80 << len % 32; - x[getOutputLength(len) - 1] = len; - let a = 1732584193; - let b = -271733879; - let c = -1732584194; - let d = 271733878; - - for (let i = 0; i < x.length; i += 16) { - const olda = a; - const oldb = b; - const oldc = c; - const oldd = d; - a = md5ff(a, b, c, d, x[i], 7, -680876936); - d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); - c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); - b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); - a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); - d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); - c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); - b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); - a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); - d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); - c = md5ff(c, d, a, b, x[i + 10], 17, -42063); - b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); - a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); - d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); - c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); - b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); - a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); - d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); - c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); - b = md5gg(b, c, d, a, x[i], 20, -373897302); - a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); - d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); - c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); - b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); - a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); - d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); - c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); - b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); - a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); - d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); - c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); - b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); - a = md5hh(a, b, c, d, x[i + 5], 4, -378558); - d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); - c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); - b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); - a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); - d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); - c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); - b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); - a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); - d = md5hh(d, a, b, c, x[i], 11, -358537222); - c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); - b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); - a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); - d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); - c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); - b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); - a = md5ii(a, b, c, d, x[i], 6, -198630844); - d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); - c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); - b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); - a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); - d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); - c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); - b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); - a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); - d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); - c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); - b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); - a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); - d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); - c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); - b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); - a = safeAdd(a, olda); - b = safeAdd(b, oldb); - c = safeAdd(c, oldc); - d = safeAdd(d, oldd); - } - - return [a, b, c, d]; -} -/* - * Convert an array bytes to an array of little-endian words - * Characters >255 have their high-byte silently ignored. - */ - - -function bytesToWords(input) { - if (input.length === 0) { - return []; - } - - const length8 = input.length * 8; - const output = new Uint32Array(getOutputLength(length8)); - - for (let i = 0; i < length8; i += 8) { - output[i >> 5] |= (input[i / 8] & 0xff) << i % 32; - } - - return output; -} -/* - * Add integers, wrapping at 2^32. This uses 16-bit operations internally - * to work around bugs in some JS interpreters. - */ - - -function safeAdd(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. - */ - - -function bitRotateLeft(num, cnt) { - return num << cnt | num >>> 32 - cnt; -} -/* - * These functions implement the four basic operations the algorithm uses. - */ - - -function md5cmn(q, a, b, x, s, t) { - return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b); -} - -function md5ff(a, b, c, d, x, s, t) { - return md5cmn(b & c | ~b & d, a, b, x, s, t); -} - -function md5gg(a, b, c, d, x, s, t) { - return md5cmn(b & d | c & ~d, a, b, x, s, t); -} - -function md5hh(a, b, c, d, x, s, t) { - return md5cmn(b ^ c ^ d, a, b, x, s, t); -} - -function md5ii(a, b, c, d, x, s, t) { - return md5cmn(c ^ (b | ~d), a, b, x, s, t); -} - -var _default = md5; -exports.default = _default; -},{}],289:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); -var _default = { - randomUUID -}; -exports.default = _default; -},{}],290:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -var _default = '00000000-0000-0000-0000-000000000000'; -exports.default = _default; -},{}],291:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _validate = _interopRequireDefault(require("./validate.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function parse(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - let v; - const arr = new Uint8Array(16); // Parse ########-....-....-....-............ - - arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; - arr[1] = v >>> 16 & 0xff; - arr[2] = v >>> 8 & 0xff; - arr[3] = v & 0xff; // Parse ........-####-....-....-............ - - arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; - arr[5] = v & 0xff; // Parse ........-....-####-....-............ - - arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; - arr[7] = v & 0xff; // Parse ........-....-....-####-............ - - arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; - arr[9] = v & 0xff; // Parse ........-....-....-....-############ - // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) - - arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; - arr[11] = v / 0x100000000 & 0xff; - arr[12] = v >>> 24 & 0xff; - arr[13] = v >>> 16 & 0xff; - arr[14] = v >>> 8 & 0xff; - arr[15] = v & 0xff; - return arr; -} - -var _default = parse; -exports.default = _default; -},{"./validate.js":301}],292:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; -exports.default = _default; -},{}],293:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = rng; -// Unique ID creation requires a high quality random # generator. In the browser we therefore -// require the crypto API and do not support built-in fallback to lower quality random number -// generators (like Math.random()). -let getRandomValues; -const rnds8 = new Uint8Array(16); - -function rng() { - // lazy load so that environments that need to polyfill have a chance to do so - if (!getRandomValues) { - // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. - getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto); - - if (!getRandomValues) { - throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); - } - } - - return getRandomValues(rnds8); -} -},{}],294:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -// Adapted from Chris Veness' SHA1 code at -// http://www.movable-type.co.uk/scripts/sha1.html -function f(s, x, y, z) { - switch (s) { - case 0: - return x & y ^ ~x & z; - - case 1: - return x ^ y ^ z; - - case 2: - return x & y ^ x & z ^ y & z; - - case 3: - return x ^ y ^ z; - } -} - -function ROTL(x, n) { - return x << n | x >>> 32 - n; -} - -function sha1(bytes) { - const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; - const H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; - - if (typeof bytes === 'string') { - const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape - - bytes = []; - - for (let i = 0; i < msg.length; ++i) { - bytes.push(msg.charCodeAt(i)); - } - } else if (!Array.isArray(bytes)) { - // Convert Array-like to Array - bytes = Array.prototype.slice.call(bytes); - } - - bytes.push(0x80); - const l = bytes.length / 4 + 2; - const N = Math.ceil(l / 16); - const M = new Array(N); - - for (let i = 0; i < N; ++i) { - const arr = new Uint32Array(16); - - for (let j = 0; j < 16; ++j) { - arr[j] = bytes[i * 64 + j * 4] << 24 | bytes[i * 64 + j * 4 + 1] << 16 | bytes[i * 64 + j * 4 + 2] << 8 | bytes[i * 64 + j * 4 + 3]; - } - - M[i] = arr; - } - - M[N - 1][14] = (bytes.length - 1) * 8 / Math.pow(2, 32); - M[N - 1][14] = Math.floor(M[N - 1][14]); - M[N - 1][15] = (bytes.length - 1) * 8 & 0xffffffff; - - for (let i = 0; i < N; ++i) { - const W = new Uint32Array(80); - - for (let t = 0; t < 16; ++t) { - W[t] = M[i][t]; - } - - for (let t = 16; t < 80; ++t) { - W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); - } - - let a = H[0]; - let b = H[1]; - let c = H[2]; - let d = H[3]; - let e = H[4]; - - for (let t = 0; t < 80; ++t) { - const s = Math.floor(t / 20); - const T = ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t] >>> 0; - e = d; - d = c; - c = ROTL(b, 30) >>> 0; - b = a; - a = T; - } - - H[0] = H[0] + a >>> 0; - H[1] = H[1] + b >>> 0; - H[2] = H[2] + c >>> 0; - H[3] = H[3] + d >>> 0; - H[4] = H[4] + e >>> 0; - } - - return [H[0] >> 24 & 0xff, H[0] >> 16 & 0xff, H[0] >> 8 & 0xff, H[0] & 0xff, H[1] >> 24 & 0xff, H[1] >> 16 & 0xff, H[1] >> 8 & 0xff, H[1] & 0xff, H[2] >> 24 & 0xff, H[2] >> 16 & 0xff, H[2] >> 8 & 0xff, H[2] & 0xff, H[3] >> 24 & 0xff, H[3] >> 16 & 0xff, H[3] >> 8 & 0xff, H[3] & 0xff, H[4] >> 24 & 0xff, H[4] >> 16 & 0xff, H[4] >> 8 & 0xff, H[4] & 0xff]; -} - -var _default = sha1; -exports.default = _default; -},{}],295:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -exports.unsafeStringify = unsafeStringify; - -var _validate = _interopRequireDefault(require("./validate.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -const byteToHex = []; - -for (let i = 0; i < 256; ++i) { - byteToHex.push((i + 0x100).toString(16).slice(1)); -} - -function unsafeStringify(arr, offset = 0) { - // Note: Be careful editing this code! It's been tuned for performance - // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 - return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); -} - -function stringify(arr, offset = 0) { - const uuid = unsafeStringify(arr, offset); // Consistency check for valid UUID. If this throws, it's likely due to one - // of the following: - // - One or more input array values don't map to a hex octet (leading to - // "undefined" in the uuid) - // - Invalid input values for the RFC `version` or `variant` fields - - if (!(0, _validate.default)(uuid)) { - throw TypeError('Stringified UUID is invalid'); - } - - return uuid; -} - -var _default = stringify; -exports.default = _default; -},{"./validate.js":301}],296:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _rng = _interopRequireDefault(require("./rng.js")); - -var _stringify = require("./stringify.js"); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// **`v1()` - Generate time-based UUID** -// -// Inspired by https://github.com/LiosK/UUID.js -// and http://docs.python.org/library/uuid.html -let _nodeId; - -let _clockseq; // Previous uuid creation time - - -let _lastMSecs = 0; -let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details - -function v1(options, buf, offset) { - let i = buf && offset || 0; - const b = buf || new Array(16); - options = options || {}; - let node = options.node || _nodeId; - let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not - // specified. We do this lazily to minimize issues related to insufficient - // system entropy. See #189 - - if (node == null || clockseq == null) { - const seedBytes = options.random || (options.rng || _rng.default)(); - - if (node == null) { - // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) - node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; - } - - if (clockseq == null) { - // Per 4.2.2, randomize (14 bit) clockseq - clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; - } - } // UUID timestamps are 100 nano-second units since the Gregorian epoch, - // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so - // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' - // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. - - - let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock - // cycle to simulate higher resolution clock - - let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) - - const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression - - if (dt < 0 && options.clockseq === undefined) { - clockseq = clockseq + 1 & 0x3fff; - } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new - // time interval - - - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { - nsecs = 0; - } // Per 4.2.1.2 Throw error if too many uuids are requested - - - if (nsecs >= 10000) { - throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch - - msecs += 12219292800000; // `time_low` - - const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = tl >>> 24 & 0xff; - b[i++] = tl >>> 16 & 0xff; - b[i++] = tl >>> 8 & 0xff; - b[i++] = tl & 0xff; // `time_mid` - - const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; - b[i++] = tmh >>> 8 & 0xff; - b[i++] = tmh & 0xff; // `time_high_and_version` - - b[i++] = tmh >>> 24 & 0xf | 0x10; // include version - - b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) - - b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` - - b[i++] = clockseq & 0xff; // `node` - - for (let n = 0; n < 6; ++n) { - b[i + n] = node[n]; - } - - return buf || (0, _stringify.unsafeStringify)(b); -} - -var _default = v1; -exports.default = _default; -},{"./rng.js":293,"./stringify.js":295}],297:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _v = _interopRequireDefault(require("./v35.js")); - -var _md = _interopRequireDefault(require("./md5.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v3 = (0, _v.default)('v3', 0x30, _md.default); -var _default = v3; -exports.default = _default; -},{"./md5.js":288,"./v35.js":298}],298:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.URL = exports.DNS = void 0; -exports.default = v35; - -var _stringify = require("./stringify.js"); - -var _parse = _interopRequireDefault(require("./parse.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function stringToBytes(str) { - str = unescape(encodeURIComponent(str)); // UTF8 escape - - const bytes = []; - - for (let i = 0; i < str.length; ++i) { - bytes.push(str.charCodeAt(i)); - } - - return bytes; -} - -const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; -exports.DNS = DNS; -const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; -exports.URL = URL; - -function v35(name, version, hashfunc) { - function generateUUID(value, namespace, buf, offset) { - var _namespace; - - if (typeof value === 'string') { - value = stringToBytes(value); - } - - if (typeof namespace === 'string') { - namespace = (0, _parse.default)(namespace); - } - - if (((_namespace = namespace) === null || _namespace === void 0 ? void 0 : _namespace.length) !== 16) { - throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); - } // Compute hash of namespace and value, Per 4.3 - // Future: Use spread syntax when supported on all platforms, e.g. `bytes = - // hashfunc([...namespace, ... value])` - - - let bytes = new Uint8Array(16 + value.length); - bytes.set(namespace); - bytes.set(value, namespace.length); - bytes = hashfunc(bytes); - bytes[6] = bytes[6] & 0x0f | version; - bytes[8] = bytes[8] & 0x3f | 0x80; - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = bytes[i]; - } - - return buf; - } - - return (0, _stringify.unsafeStringify)(bytes); - } // Function#name is not settable on some platforms (#270) - - - try { - generateUUID.name = name; // eslint-disable-next-line no-empty - } catch (err) {} // For CommonJS default export support - - - generateUUID.DNS = DNS; - generateUUID.URL = URL; - return generateUUID; -} -},{"./parse.js":291,"./stringify.js":295}],299:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _native = _interopRequireDefault(require("./native.js")); - -var _rng = _interopRequireDefault(require("./rng.js")); - -var _stringify = require("./stringify.js"); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function v4(options, buf, offset) { - if (_native.default.randomUUID && !buf && !options) { - return _native.default.randomUUID(); - } - - options = options || {}; - - const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - - - rnds[6] = rnds[6] & 0x0f | 0x40; - rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = rnds[i]; - } - - return buf; - } - - return (0, _stringify.unsafeStringify)(rnds); -} - -var _default = v4; -exports.default = _default; -},{"./native.js":289,"./rng.js":293,"./stringify.js":295}],300:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _v = _interopRequireDefault(require("./v35.js")); - -var _sha = _interopRequireDefault(require("./sha1.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v5 = (0, _v.default)('v5', 0x50, _sha.default); -var _default = v5; -exports.default = _default; -},{"./sha1.js":294,"./v35.js":298}],301:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _regex = _interopRequireDefault(require("./regex.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function validate(uuid) { - return typeof uuid === 'string' && _regex.default.test(uuid); -} - -var _default = validate; -exports.default = _default; -},{"./regex.js":292}],302:[function(require,module,exports){ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; - -var _validate = _interopRequireDefault(require("./validate.js")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function version(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - return parseInt(uuid.slice(14, 15), 16); -} - -var _default = version; -exports.default = _default; -},{"./validate.js":301}],303:[function(require,module,exports){ -(function (global){(function (){ -'use strict'; - -var forEach = require('for-each'); -var availableTypedArrays = require('available-typed-arrays'); -var callBound = require('call-bind/callBound'); -var gOPD = require('gopd'); - -var $toString = callBound('Object.prototype.toString'); -var hasToStringTag = require('has-tostringtag/shams')(); - -var g = typeof globalThis === 'undefined' ? global : globalThis; -var typedArrays = availableTypedArrays(); - -var $slice = callBound('String.prototype.slice'); -var toStrTags = {}; -var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof'); -if (hasToStringTag && gOPD && getPrototypeOf) { - forEach(typedArrays, function (typedArray) { - if (typeof g[typedArray] === 'function') { - var arr = new g[typedArray](); - if (Symbol.toStringTag in arr) { - var proto = getPrototypeOf(arr); - var descriptor = gOPD(proto, Symbol.toStringTag); - if (!descriptor) { - var superProto = getPrototypeOf(proto); - descriptor = gOPD(superProto, Symbol.toStringTag); - } - toStrTags[typedArray] = descriptor.get; - } - } - }); -} - -var tryTypedArrays = function tryAllTypedArrays(value) { - var foundName = false; - forEach(toStrTags, function (getter, typedArray) { - if (!foundName) { - try { - var name = getter.call(value); - if (name === typedArray) { - foundName = name; - } - } catch (e) {} - } - }); - return foundName; -}; - -var isTypedArray = require('is-typed-array'); - -module.exports = function whichTypedArray(value) { - if (!isTypedArray(value)) { return false; } - if (!hasToStringTag || !(Symbol.toStringTag in value)) { return $slice($toString(value), 8, -1); } - return tryTypedArrays(value); -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"available-typed-arrays":16,"call-bind/callBound":69,"for-each":107,"gopd":111,"has-tostringtag/shams":114,"is-typed-array":150}],304:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RuleId = exports.PushRuleKind = exports.ConditionKind = exports.isDmMemberCountCondition = exports.DMMemberCountCondition = exports.ConditionOperator = exports.TweakName = exports.PushRuleActionName = void 0; -// allow camelcase as these are things that go onto the wire -/* eslint-disable camelcase */ -var PushRuleActionName; -(function (PushRuleActionName) { - PushRuleActionName["DontNotify"] = "dont_notify"; - PushRuleActionName["Notify"] = "notify"; - PushRuleActionName["Coalesce"] = "coalesce"; -})(PushRuleActionName = exports.PushRuleActionName || (exports.PushRuleActionName = {})); -var TweakName; -(function (TweakName) { - TweakName["Highlight"] = "highlight"; - TweakName["Sound"] = "sound"; -})(TweakName = exports.TweakName || (exports.TweakName = {})); -var ConditionOperator; -(function (ConditionOperator) { - ConditionOperator["ExactEquals"] = "=="; - ConditionOperator["LessThan"] = "<"; - ConditionOperator["GreaterThan"] = ">"; - ConditionOperator["GreaterThanOrEqual"] = ">="; - ConditionOperator["LessThanOrEqual"] = "<="; -})(ConditionOperator = exports.ConditionOperator || (exports.ConditionOperator = {})); -exports.DMMemberCountCondition = "2"; -function isDmMemberCountCondition(condition) { - return condition === "==2" || condition === "2"; -} -exports.isDmMemberCountCondition = isDmMemberCountCondition; -var ConditionKind; -(function (ConditionKind) { - ConditionKind["EventMatch"] = "event_match"; - ConditionKind["EventPropertyIs"] = "event_property_is"; - ConditionKind["EventPropertyContains"] = "event_property_contains"; - ConditionKind["ContainsDisplayName"] = "contains_display_name"; - ConditionKind["RoomMemberCount"] = "room_member_count"; - ConditionKind["SenderNotificationPermission"] = "sender_notification_permission"; - ConditionKind["CallStarted"] = "call_started"; - ConditionKind["CallStartedPrefix"] = "org.matrix.msc3914.call_started"; -})(ConditionKind = exports.ConditionKind || (exports.ConditionKind = {})); -var PushRuleKind; -(function (PushRuleKind) { - PushRuleKind["Override"] = "override"; - PushRuleKind["ContentSpecific"] = "content"; - PushRuleKind["RoomSpecific"] = "room"; - PushRuleKind["SenderSpecific"] = "sender"; - PushRuleKind["Underride"] = "underride"; -})(PushRuleKind = exports.PushRuleKind || (exports.PushRuleKind = {})); -var RuleId; -(function (RuleId) { - RuleId["Master"] = ".m.rule.master"; - RuleId["IsUserMention"] = ".org.matrix.msc3952.is_user_mention"; - RuleId["IsRoomMention"] = ".org.matrix.msc3952.is_room_mention"; - RuleId["ContainsDisplayName"] = ".m.rule.contains_display_name"; - RuleId["ContainsUserName"] = ".m.rule.contains_user_name"; - RuleId["AtRoomNotification"] = ".m.rule.roomnotif"; - RuleId["DM"] = ".m.rule.room_one_to_one"; - RuleId["EncryptedDM"] = ".m.rule.encrypted_room_one_to_one"; - RuleId["Message"] = ".m.rule.message"; - RuleId["EncryptedMessage"] = ".m.rule.encrypted"; - RuleId["InviteToSelf"] = ".m.rule.invite_for_me"; - RuleId["MemberEvent"] = ".m.rule.member_event"; - RuleId["IncomingCall"] = ".m.rule.call"; - RuleId["SuppressNotices"] = ".m.rule.suppress_notices"; - RuleId["Tombstone"] = ".m.rule.tombstone"; - RuleId["PollStart"] = ".m.rule.poll_start"; - RuleId["PollStartUnstable"] = ".org.matrix.msc3930.rule.poll_start"; - RuleId["PollEnd"] = ".m.rule.poll_end"; - RuleId["PollEndUnstable"] = ".org.matrix.msc3930.rule.poll_end"; - RuleId["PollStartOneToOne"] = ".m.rule.poll_start_one_to_one"; - RuleId["PollStartOneToOneUnstable"] = ".org.matrix.msc3930.rule.poll_start_one_to_one"; - RuleId["PollEndOneToOne"] = ".m.rule.poll_end_one_to_one"; - RuleId["PollEndOneToOneUnstable"] = ".org.matrix.msc3930.rule.poll_end_one_to_one"; -})(RuleId = exports.RuleId || (exports.RuleId = {})); -/* eslint-enable camelcase */ - -},{}],305:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.M_BEACON = exports.M_BEACON_INFO = void 0; -const NamespacedValue_1 = require("../NamespacedValue"); -/** - * Beacon info and beacon event types as described in MSC3672 - * https://github.com/matrix-org/matrix-spec-proposals/pull/3672 - */ -/** - * Beacon info events are state events. - * We have two requirements for these events: - * 1. they can only be written by their owner - * 2. a user can have an arbitrary number of beacon_info events - * - * 1. is achieved by setting the state_key to the owners mxid. - * Event keys in room state are a combination of `type` + `state_key`. - * To achieve an arbitrary number of only owner-writable state events - * we introduce a variable suffix to the event type - * - * @example - * ``` - * { - * "type": "m.beacon_info.@matthew:matrix.org.1", - * "state_key": "@matthew:matrix.org", - * "content": { - * "m.beacon_info": { - * "description": "The Matthew Tracker", - * "timeout": 86400000, - * }, - * // more content as described below - * } - * }, - * { - * "type": "m.beacon_info.@matthew:matrix.org.2", - * "state_key": "@matthew:matrix.org", - * "content": { - * "m.beacon_info": { - * "description": "Another different Matthew tracker", - * "timeout": 400000, - * }, - * // more content as described below - * } - * } - * ``` - */ -/** - * Non-variable type for m.beacon_info event content - */ -exports.M_BEACON_INFO = new NamespacedValue_1.UnstableValue("m.beacon_info", "org.matrix.msc3672.beacon_info"); -exports.M_BEACON = new NamespacedValue_1.UnstableValue("m.beacon", "org.matrix.msc3672.beacon"); - -},{"../NamespacedValue":316}],306:[function(require,module,exports){ -"use strict"; -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LOCAL_NOTIFICATION_SETTINGS_PREFIX = exports.PUSHER_DEVICE_ID = exports.PUSHER_ENABLED = exports.EVENT_VISIBILITY_CHANGE_TYPE = exports.UNSTABLE_ELEMENT_FUNCTIONAL_USERS = exports.MSC3912_RELATION_BASED_REDACTIONS_PROP = exports.UNSTABLE_MSC2716_MARKER = exports.UNSTABLE_MSC3089_BRANCH = exports.UNSTABLE_MSC3089_LEAF = exports.UNSTABLE_MSC3089_TREE_SUBTYPE = exports.UNSTABLE_MSC3088_ENABLED = exports.UNSTABLE_MSC3088_PURPOSE = exports.ToDeviceMessageId = exports.RoomType = exports.RoomCreateTypeField = exports.MsgType = exports.RelationType = exports.EventType = void 0; -const NamespacedValue_1 = require("../NamespacedValue"); -var EventType; -(function (EventType) { - // Room state events - EventType["RoomCanonicalAlias"] = "m.room.canonical_alias"; - EventType["RoomCreate"] = "m.room.create"; - EventType["RoomJoinRules"] = "m.room.join_rules"; - EventType["RoomMember"] = "m.room.member"; - EventType["RoomThirdPartyInvite"] = "m.room.third_party_invite"; - EventType["RoomPowerLevels"] = "m.room.power_levels"; - EventType["RoomName"] = "m.room.name"; - EventType["RoomTopic"] = "m.room.topic"; - EventType["RoomAvatar"] = "m.room.avatar"; - EventType["RoomPinnedEvents"] = "m.room.pinned_events"; - EventType["RoomEncryption"] = "m.room.encryption"; - EventType["RoomHistoryVisibility"] = "m.room.history_visibility"; - EventType["RoomGuestAccess"] = "m.room.guest_access"; - EventType["RoomServerAcl"] = "m.room.server_acl"; - EventType["RoomTombstone"] = "m.room.tombstone"; - EventType["RoomPredecessor"] = "org.matrix.msc3946.room_predecessor"; - EventType["SpaceChild"] = "m.space.child"; - EventType["SpaceParent"] = "m.space.parent"; - // Room timeline events - EventType["RoomRedaction"] = "m.room.redaction"; - EventType["RoomMessage"] = "m.room.message"; - EventType["RoomMessageEncrypted"] = "m.room.encrypted"; - EventType["Sticker"] = "m.sticker"; - EventType["CallInvite"] = "m.call.invite"; - EventType["CallCandidates"] = "m.call.candidates"; - EventType["CallAnswer"] = "m.call.answer"; - EventType["CallHangup"] = "m.call.hangup"; - EventType["CallReject"] = "m.call.reject"; - EventType["CallSelectAnswer"] = "m.call.select_answer"; - EventType["CallNegotiate"] = "m.call.negotiate"; - EventType["CallSDPStreamMetadataChanged"] = "m.call.sdp_stream_metadata_changed"; - EventType["CallSDPStreamMetadataChangedPrefix"] = "org.matrix.call.sdp_stream_metadata_changed"; - EventType["CallReplaces"] = "m.call.replaces"; - EventType["CallAssertedIdentity"] = "m.call.asserted_identity"; - EventType["CallAssertedIdentityPrefix"] = "org.matrix.call.asserted_identity"; - EventType["KeyVerificationRequest"] = "m.key.verification.request"; - EventType["KeyVerificationStart"] = "m.key.verification.start"; - EventType["KeyVerificationCancel"] = "m.key.verification.cancel"; - EventType["KeyVerificationMac"] = "m.key.verification.mac"; - EventType["KeyVerificationDone"] = "m.key.verification.done"; - EventType["KeyVerificationKey"] = "m.key.verification.key"; - EventType["KeyVerificationAccept"] = "m.key.verification.accept"; - // Not used directly - see READY_TYPE in VerificationRequest. - EventType["KeyVerificationReady"] = "m.key.verification.ready"; - // use of this is discouraged https://matrix.org/docs/spec/client_server/r0.6.1#m-room-message-feedback - EventType["RoomMessageFeedback"] = "m.room.message.feedback"; - EventType["Reaction"] = "m.reaction"; - EventType["PollStart"] = "org.matrix.msc3381.poll.start"; - // Room ephemeral events - EventType["Typing"] = "m.typing"; - EventType["Receipt"] = "m.receipt"; - EventType["Presence"] = "m.presence"; - // Room account_data events - EventType["FullyRead"] = "m.fully_read"; - EventType["Tag"] = "m.tag"; - EventType["SpaceOrder"] = "org.matrix.msc3230.space_order"; - // User account_data events - EventType["PushRules"] = "m.push_rules"; - EventType["Direct"] = "m.direct"; - EventType["IgnoredUserList"] = "m.ignored_user_list"; - // to_device events - EventType["RoomKey"] = "m.room_key"; - EventType["RoomKeyRequest"] = "m.room_key_request"; - EventType["ForwardedRoomKey"] = "m.forwarded_room_key"; - EventType["Dummy"] = "m.dummy"; - // Group call events - EventType["GroupCallPrefix"] = "org.matrix.msc3401.call"; - EventType["GroupCallMemberPrefix"] = "org.matrix.msc3401.call.member"; -})(EventType = exports.EventType || (exports.EventType = {})); -var RelationType; -(function (RelationType) { - RelationType["Annotation"] = "m.annotation"; - RelationType["Replace"] = "m.replace"; - RelationType["Reference"] = "m.reference"; - RelationType["Thread"] = "m.thread"; -})(RelationType = exports.RelationType || (exports.RelationType = {})); -var MsgType; -(function (MsgType) { - MsgType["Text"] = "m.text"; - MsgType["Emote"] = "m.emote"; - MsgType["Notice"] = "m.notice"; - MsgType["Image"] = "m.image"; - MsgType["File"] = "m.file"; - MsgType["Audio"] = "m.audio"; - MsgType["Location"] = "m.location"; - MsgType["Video"] = "m.video"; - MsgType["KeyVerificationRequest"] = "m.key.verification.request"; -})(MsgType = exports.MsgType || (exports.MsgType = {})); -exports.RoomCreateTypeField = "type"; -var RoomType; -(function (RoomType) { - RoomType["Space"] = "m.space"; - RoomType["UnstableCall"] = "org.matrix.msc3417.call"; - RoomType["ElementVideo"] = "io.element.video"; -})(RoomType = exports.RoomType || (exports.RoomType = {})); -exports.ToDeviceMessageId = "org.matrix.msgid"; -/** - * Identifier for an [MSC3088](https://github.com/matrix-org/matrix-doc/pull/3088) - * room purpose. Note that this reference is UNSTABLE and subject to breaking changes, - * including its eventual removal. - */ -exports.UNSTABLE_MSC3088_PURPOSE = new NamespacedValue_1.UnstableValue("m.room.purpose", "org.matrix.msc3088.purpose"); -/** - * Enabled flag for an [MSC3088](https://github.com/matrix-org/matrix-doc/pull/3088) - * room purpose. Note that this reference is UNSTABLE and subject to breaking changes, - * including its eventual removal. - */ -exports.UNSTABLE_MSC3088_ENABLED = new NamespacedValue_1.UnstableValue("m.enabled", "org.matrix.msc3088.enabled"); -/** - * Subtype for an [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) space-room. - * Note that this reference is UNSTABLE and subject to breaking changes, including its - * eventual removal. - */ -exports.UNSTABLE_MSC3089_TREE_SUBTYPE = new NamespacedValue_1.UnstableValue("m.data_tree", "org.matrix.msc3089.data_tree"); -/** - * Leaf type for an event in a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) space-room. - * Note that this reference is UNSTABLE and subject to breaking changes, including its - * eventual removal. - */ -exports.UNSTABLE_MSC3089_LEAF = new NamespacedValue_1.UnstableValue("m.leaf", "org.matrix.msc3089.leaf"); -/** - * Branch (Leaf Reference) type for the index approach in a - * [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) space-room. Note that this reference is - * UNSTABLE and subject to breaking changes, including its eventual removal. - */ -exports.UNSTABLE_MSC3089_BRANCH = new NamespacedValue_1.UnstableValue("m.branch", "org.matrix.msc3089.branch"); -/** - * Marker event type to point back at imported historical content in a room. See - * [MSC2716](https://github.com/matrix-org/matrix-spec-proposals/pull/2716). - * Note that this reference is UNSTABLE and subject to breaking changes, - * including its eventual removal. - */ -exports.UNSTABLE_MSC2716_MARKER = new NamespacedValue_1.UnstableValue("m.room.marker", "org.matrix.msc2716.marker"); -/** - * Name of the "with_relations" request property for relation based redactions. - * {@link https://github.com/matrix-org/matrix-spec-proposals/pull/3912} - */ -exports.MSC3912_RELATION_BASED_REDACTIONS_PROP = new NamespacedValue_1.UnstableValue("with_relations", "org.matrix.msc3912.with_relations"); -/** - * Functional members type for declaring a purpose of room members (e.g. helpful bots). - * Note that this reference is UNSTABLE and subject to breaking changes, including its - * eventual removal. - * - * Schema (TypeScript): - * ``` - * { - * service_members?: string[] - * } - * ``` - * - * @example - * ``` - * { - * "service_members": [ - * "@helperbot:localhost", - * "@reminderbot:alice.tdl" - * ] - * } - * ``` - */ -exports.UNSTABLE_ELEMENT_FUNCTIONAL_USERS = new NamespacedValue_1.UnstableValue("io.element.functional_members", "io.element.functional_members"); -/** - * A type of message that affects visibility of a message, - * as per https://github.com/matrix-org/matrix-doc/pull/3531 - * - * @experimental - */ -exports.EVENT_VISIBILITY_CHANGE_TYPE = new NamespacedValue_1.UnstableValue("m.visibility", "org.matrix.msc3531.visibility"); -/** - * https://github.com/matrix-org/matrix-doc/pull/3881 - * - * @experimental - */ -exports.PUSHER_ENABLED = new NamespacedValue_1.UnstableValue("enabled", "org.matrix.msc3881.enabled"); -/** - * https://github.com/matrix-org/matrix-doc/pull/3881 - * - * @experimental - */ -exports.PUSHER_DEVICE_ID = new NamespacedValue_1.UnstableValue("device_id", "org.matrix.msc3881.device_id"); -/** - * https://github.com/matrix-org/matrix-doc/pull/3890 - * - * @experimental - */ -exports.LOCAL_NOTIFICATION_SETTINGS_PREFIX = new NamespacedValue_1.UnstableValue("m.local_notification_settings", "org.matrix.msc3890.local_notification_settings"); - -},{"../NamespacedValue":316}],307:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEventTypeSame = exports.REFERENCE_RELATION = exports.M_HTML = exports.M_TEXT = exports.M_MESSAGE = void 0; -const matrix_events_sdk_1 = require("matrix-events-sdk"); -const utilities_1 = require("../extensible_events_v1/utilities"); -/** - * The namespaced value for m.message - */ -exports.M_MESSAGE = new matrix_events_sdk_1.UnstableValue("m.message", "org.matrix.msc1767.message"); -/** - * The namespaced value for m.text - */ -exports.M_TEXT = new matrix_events_sdk_1.UnstableValue("m.text", "org.matrix.msc1767.text"); -/** - * The namespaced value for m.html - */ -exports.M_HTML = new matrix_events_sdk_1.UnstableValue("m.html", "org.matrix.msc1767.html"); -/** - * The namespaced value for an m.reference relation - */ -exports.REFERENCE_RELATION = new matrix_events_sdk_1.NamespacedValue("m.reference"); -/** - * Determines if two event types are the same, including namespaces. - * @param given - The given event type. This will be compared - * against the expected type. - * @param expected - The expected event type. - * @returns True if the given type matches the expected type. - */ -function isEventTypeSame(given, expected) { - if (typeof given === "string") { - if (typeof expected === "string") { - return expected === given; - } - else { - return expected.matches(given); - } - } - else { - if (typeof expected === "string") { - return given.matches(expected); - } - else { - const expectedNs = expected; - const givenNs = given; - return (expectedNs.matches(givenNs.name) || - ((0, utilities_1.isProvided)(givenNs.altName) && expectedNs.matches(givenNs.altName))); - } - } -} -exports.isEventTypeSame = isEventTypeSame; - -},{"../extensible_events_v1/utilities":361,"matrix-events-sdk":167}],308:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.M_LOCATION = exports.M_TIMESTAMP = exports.M_ASSET = exports.LocationAssetType = void 0; -const NamespacedValue_1 = require("../NamespacedValue"); -const extensible_events_1 = require("./extensible_events"); -var LocationAssetType; -(function (LocationAssetType) { - LocationAssetType["Self"] = "m.self"; - LocationAssetType["Pin"] = "m.pin"; -})(LocationAssetType = exports.LocationAssetType || (exports.LocationAssetType = {})); -exports.M_ASSET = new NamespacedValue_1.UnstableValue("m.asset", "org.matrix.msc3488.asset"); -exports.M_TIMESTAMP = new NamespacedValue_1.UnstableValue("m.ts", "org.matrix.msc3488.ts"); -exports.M_LOCATION = new NamespacedValue_1.UnstableValue("m.location", "org.matrix.msc3488.location"); - -},{"../NamespacedValue":316,"./extensible_events":307}],309:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HistoryVisibility = exports.GuestAccess = exports.RestrictedAllowType = exports.JoinRule = exports.Preset = exports.Visibility = void 0; -var Visibility; -(function (Visibility) { - Visibility["Public"] = "public"; - Visibility["Private"] = "private"; -})(Visibility = exports.Visibility || (exports.Visibility = {})); -var Preset; -(function (Preset) { - Preset["PrivateChat"] = "private_chat"; - Preset["TrustedPrivateChat"] = "trusted_private_chat"; - Preset["PublicChat"] = "public_chat"; -})(Preset = exports.Preset || (exports.Preset = {})); -// Knock and private are reserved keywords which are not yet implemented. -var JoinRule; -(function (JoinRule) { - JoinRule["Public"] = "public"; - JoinRule["Invite"] = "invite"; - /** - * @deprecated Reserved keyword. Should not be used. Not yet implemented. - */ - JoinRule["Private"] = "private"; - JoinRule["Knock"] = "knock"; - JoinRule["Restricted"] = "restricted"; -})(JoinRule = exports.JoinRule || (exports.JoinRule = {})); -var RestrictedAllowType; -(function (RestrictedAllowType) { - RestrictedAllowType["RoomMembership"] = "m.room_membership"; -})(RestrictedAllowType = exports.RestrictedAllowType || (exports.RestrictedAllowType = {})); -var GuestAccess; -(function (GuestAccess) { - GuestAccess["CanJoin"] = "can_join"; - GuestAccess["Forbidden"] = "forbidden"; -})(GuestAccess = exports.GuestAccess || (exports.GuestAccess = {})); -var HistoryVisibility; -(function (HistoryVisibility) { - HistoryVisibility["Invited"] = "invited"; - HistoryVisibility["Joined"] = "joined"; - HistoryVisibility["Shared"] = "shared"; - HistoryVisibility["WorldReadable"] = "world_readable"; -})(HistoryVisibility = exports.HistoryVisibility || (exports.HistoryVisibility = {})); - -},{}],310:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.M_POLL_END = exports.M_POLL_RESPONSE = exports.M_POLL_START = exports.M_POLL_KIND_UNDISCLOSED = exports.M_POLL_KIND_DISCLOSED = void 0; -const matrix_events_sdk_1 = require("matrix-events-sdk"); -/** - * Identifier for a disclosed poll. - */ -exports.M_POLL_KIND_DISCLOSED = new matrix_events_sdk_1.UnstableValue("m.poll.disclosed", "org.matrix.msc3381.poll.disclosed"); -/** - * Identifier for an undisclosed poll. - */ -exports.M_POLL_KIND_UNDISCLOSED = new matrix_events_sdk_1.UnstableValue("m.poll.undisclosed", "org.matrix.msc3381.poll.undisclosed"); -/** - * The namespaced value for m.poll.start - */ -exports.M_POLL_START = new matrix_events_sdk_1.UnstableValue("m.poll.start", "org.matrix.msc3381.poll.start"); -/** - * The namespaced value for m.poll.response - */ -exports.M_POLL_RESPONSE = new matrix_events_sdk_1.UnstableValue("m.poll.response", "org.matrix.msc3381.poll.response"); -/** - * The namespaced value for m.poll.end - */ -exports.M_POLL_END = new matrix_events_sdk_1.UnstableValue("m.poll.end", "org.matrix.msc3381.poll.end"); - -},{"matrix-events-sdk":167}],311:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 Šimon Brandner - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MAIN_ROOM_TIMELINE = exports.ReceiptType = void 0; -var ReceiptType; -(function (ReceiptType) { - ReceiptType["Read"] = "m.read"; - ReceiptType["FullyRead"] = "m.fully_read"; - ReceiptType["ReadPrivate"] = "m.read.private"; -})(ReceiptType = exports.ReceiptType || (exports.ReceiptType = {})); -exports.MAIN_ROOM_TIMELINE = "main"; - -},{}],312:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-enable camelcase */ - -},{}],313:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SearchOrderBy = void 0; -var GroupKey; -(function (GroupKey) { - GroupKey["RoomId"] = "room_id"; - GroupKey["Sender"] = "sender"; -})(GroupKey || (GroupKey = {})); -var SearchOrderBy; -(function (SearchOrderBy) { - SearchOrderBy["Recent"] = "recent"; - SearchOrderBy["Rank"] = "rank"; -})(SearchOrderBy = exports.SearchOrderBy || (exports.SearchOrderBy = {})); -/* eslint-enable camelcase */ - -},{}],314:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UNREAD_THREAD_NOTIFICATIONS = void 0; -const NamespacedValue_1 = require("../NamespacedValue"); -/** - * https://github.com/matrix-org/matrix-doc/pull/3773 - * - * @experimental - */ -exports.UNREAD_THREAD_NOTIFICATIONS = new NamespacedValue_1.ServerControlledNamespacedValue("unread_thread_notifications", "org.matrix.msc3773.unread_thread_notifications"); - -},{"../NamespacedValue":316}],315:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.M_TOPIC = void 0; -const NamespacedValue_1 = require("../NamespacedValue"); -/** - * Extensible topic event type based on MSC3765 - * https://github.com/matrix-org/matrix-spec-proposals/pull/3765 - * - * @example - * ``` - * { - * "type": "m.room.topic, - * "state_key": "", - * "content": { - * "topic": "All about **pizza**", - * "m.topic": [{ - * "body": "All about **pizza**", - * "mimetype": "text/plain", - * }, { - * "body": "All about pizza", - * "mimetype": "text/html", - * }], - * } - * } - * ``` - */ -/** - * The event type for an m.topic event (in content) - */ -exports.M_TOPIC = new NamespacedValue_1.UnstableValue("m.topic", "org.matrix.msc3765.topic"); - -},{"../NamespacedValue":316}],316:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UnstableValue = exports.ServerControlledNamespacedValue = exports.NamespacedValue = void 0; -/** - * Represents a simple Matrix namespaced value. This will assume that if a stable prefix - * is provided that the stable prefix should be used when representing the identifier. - */ -class NamespacedValue { - constructor(stable, unstable) { - this.stable = stable; - this.unstable = unstable; - if (!this.unstable && !this.stable) { - throw new Error("One of stable or unstable values must be supplied"); - } - } - get name() { - if (this.stable) { - return this.stable; - } - return this.unstable; - } - get altName() { - if (!this.stable) { - return null; - } - return this.unstable; - } - get names() { - const names = [this.name]; - const altName = this.altName; - if (altName) - names.push(altName); - return names; - } - matches(val) { - return this.name === val || this.altName === val; - } - // this desperately wants https://github.com/microsoft/TypeScript/pull/26349 at the top level of the class - // so we can instantiate `NamespacedValue` as a default type for that namespace. - findIn(obj) { - let val = undefined; - if (this.name) { - val = obj === null || obj === void 0 ? void 0 : obj[this.name]; - } - if (!val && this.altName) { - val = obj === null || obj === void 0 ? void 0 : obj[this.altName]; - } - return val; - } - includedIn(arr) { - let included = false; - if (this.name) { - included = arr.includes(this.name); - } - if (!included && this.altName) { - included = arr.includes(this.altName); - } - return included; - } -} -exports.NamespacedValue = NamespacedValue; -class ServerControlledNamespacedValue extends NamespacedValue { - constructor() { - super(...arguments); - this.preferUnstable = false; - } - setPreferUnstable(preferUnstable) { - this.preferUnstable = preferUnstable; - } - get name() { - if (this.stable && !this.preferUnstable) { - return this.stable; - } - return this.unstable; - } -} -exports.ServerControlledNamespacedValue = ServerControlledNamespacedValue; -/** - * Represents a namespaced value which prioritizes the unstable value over the stable - * value. - */ -class UnstableValue extends NamespacedValue { - // Note: Constructor difference is that `unstable` is *required*. - constructor(stable, unstable) { - super(stable, unstable); - if (!this.unstable) { - throw new Error("Unstable value must be supplied"); - } - } - get name() { - return this.unstable; - } - get altName() { - return this.stable; - } -} -exports.UnstableValue = UnstableValue; - -},{}],317:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2017 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TypedReEmitter = exports.ReEmitter = void 0; -class ReEmitter { - constructor(target) { - this.target = target; - // Map from emitter to event name to re-emitter - this.reEmitters = new Map(); - } - reEmit(source, eventNames) { - let reEmittersByEvent = this.reEmitters.get(source); - if (!reEmittersByEvent) { - reEmittersByEvent = new Map(); - this.reEmitters.set(source, reEmittersByEvent); - } - for (const eventName of eventNames) { - // We include the source as the last argument for event handlers which may need it, - // such as read receipt listeners on the client class which won't have the context - // of the room. - const forSource = (...args) => { - // EventEmitter special cases 'error' to make the emit function throw if no - // handler is attached, which sort of makes sense for making sure that something - // handles an error, but for re-emitting, there could be a listener on the original - // source object so the test doesn't really work. We *could* try to replicate the - // same logic and throw if there is no listener on either the source or the target, - // but this behaviour is fairly undesireable for us anyway: the main place we throw - // 'error' events is for calls, where error events are usually emitted some time - // later by a different part of the code where 'emit' throwing because the app hasn't - // added an error handler isn't terribly helpful. (A better fix in retrospect may - // have been to just avoid using the event name 'error', but backwards compat...) - if (eventName === "error" && this.target.listenerCount("error") === 0) - return; - this.target.emit(eventName, ...args, source); - }; - source.on(eventName, forSource); - reEmittersByEvent.set(eventName, forSource); - } - } - stopReEmitting(source, eventNames) { - const reEmittersByEvent = this.reEmitters.get(source); - if (!reEmittersByEvent) - return; // We were never re-emitting these events in the first place - for (const eventName of eventNames) { - source.off(eventName, reEmittersByEvent.get(eventName)); - reEmittersByEvent.delete(eventName); - } - if (reEmittersByEvent.size === 0) - this.reEmitters.delete(source); - } -} -exports.ReEmitter = ReEmitter; -class TypedReEmitter extends ReEmitter { - constructor(target) { - super(target); - } - reEmit(source, eventNames) { - super.reEmit(source, eventNames); - } - stopReEmitting(source, eventNames) { - super.stopReEmitting(source, eventNames); - } -} -exports.TypedReEmitter = TypedReEmitter; - -},{}],318:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ToDeviceMessageQueue = void 0; -const event_1 = require("./@types/event"); -const logger_1 = require("./logger"); -const client_1 = require("./client"); -const scheduler_1 = require("./scheduler"); -const sync_1 = require("./sync"); -const utils_1 = require("./utils"); -const MAX_BATCH_SIZE = 20; -/** - * Maintains a queue of outgoing to-device messages, sending them - * as soon as the homeserver is reachable. - */ -class ToDeviceMessageQueue { - constructor(client) { - this.client = client; - this.sending = false; - this.running = true; - this.retryTimeout = null; - this.retryAttempts = 0; - this.sendQueue = () => __awaiter(this, void 0, void 0, function* () { - if (this.retryTimeout !== null) - clearTimeout(this.retryTimeout); - this.retryTimeout = null; - if (this.sending || !this.running) - return; - logger_1.logger.debug("Attempting to send queued to-device messages"); - this.sending = true; - let headBatch; - try { - while (this.running) { - headBatch = yield this.client.store.getOldestToDeviceBatch(); - if (headBatch === null) - break; - yield this.sendBatch(headBatch); - yield this.client.store.removeToDeviceBatch(headBatch.id); - this.retryAttempts = 0; - } - // Make sure we're still running after the async tasks: if not, stop. - if (!this.running) - return; - logger_1.logger.debug("All queued to-device messages sent"); - } - catch (e) { - ++this.retryAttempts; - // eslint-disable-next-line @typescript-eslint/naming-convention - // eslint-disable-next-line new-cap - const retryDelay = scheduler_1.MatrixScheduler.RETRY_BACKOFF_RATELIMIT(null, this.retryAttempts, e); - if (retryDelay === -1) { - // the scheduler function doesn't differentiate between fatal errors and just getting - // bored and giving up for now - if (Math.floor(e.httpStatus / 100) === 4) { - logger_1.logger.error("Fatal error when sending to-device message - dropping to-device batch!", e); - yield this.client.store.removeToDeviceBatch(headBatch.id); - } - else { - logger_1.logger.info("Automatic retry limit reached for to-device messages."); - } - return; - } - logger_1.logger.info(`Failed to send batch of to-device messages. Will retry in ${retryDelay}ms`, e); - this.retryTimeout = setTimeout(this.sendQueue, retryDelay); - } - finally { - this.sending = false; - } - }); - /** - * Listen to sync state changes and automatically resend any pending events - * once syncing is resumed - */ - this.onResumedSync = (state, oldState) => { - if (state === sync_1.SyncState.Syncing && oldState !== sync_1.SyncState.Syncing) { - logger_1.logger.info(`Resuming queue after resumed sync`); - this.sendQueue(); - } - }; - } - start() { - this.running = true; - this.sendQueue(); - this.client.on(client_1.ClientEvent.Sync, this.onResumedSync); - } - stop() { - this.running = false; - if (this.retryTimeout !== null) - clearTimeout(this.retryTimeout); - this.retryTimeout = null; - this.client.removeListener(client_1.ClientEvent.Sync, this.onResumedSync); - } - queueBatch(batch) { - return __awaiter(this, void 0, void 0, function* () { - const batches = []; - for (let i = 0; i < batch.batch.length; i += MAX_BATCH_SIZE) { - const batchWithTxnId = { - eventType: batch.eventType, - batch: batch.batch.slice(i, i + MAX_BATCH_SIZE), - txnId: this.client.makeTxnId(), - }; - batches.push(batchWithTxnId); - const msgmap = batchWithTxnId.batch.map((msg) => `${msg.userId}/${msg.deviceId} (msgid ${msg.payload[event_1.ToDeviceMessageId]})`); - logger_1.logger.info(`Enqueuing batch of to-device messages. type=${batch.eventType} txnid=${batchWithTxnId.txnId}`, msgmap); - } - yield this.client.store.saveToDeviceBatches(batches); - this.sendQueue(); - }); - } - /** - * Attempts to send a batch of to-device messages. - */ - sendBatch(batch) { - return __awaiter(this, void 0, void 0, function* () { - const contentMap = new utils_1.MapWithDefault(() => new Map()); - for (const item of batch.batch) { - contentMap.getOrCreate(item.userId).set(item.deviceId, item.payload); - } - logger_1.logger.info(`Sending batch of ${batch.batch.length} to-device messages with ID ${batch.id} and txnId ${batch.txnId}`); - yield this.client.sendToDevice(batch.eventType, contentMap, batch.txnId); - }); - } -} -exports.ToDeviceMessageQueue = ToDeviceMessageQueue; - -},{"./@types/event":306,"./client":321,"./logger":374,"./scheduler":403,"./sync":414,"./utils":416}],319:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AutoDiscovery = exports.AutoDiscoveryAction = void 0; -const logger_1 = require("./logger"); -const http_api_1 = require("./http-api"); -// Dev note: Auto discovery is part of the spec. -// See: https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery -var AutoDiscoveryAction; -(function (AutoDiscoveryAction) { - AutoDiscoveryAction["SUCCESS"] = "SUCCESS"; - AutoDiscoveryAction["IGNORE"] = "IGNORE"; - AutoDiscoveryAction["PROMPT"] = "PROMPT"; - AutoDiscoveryAction["FAIL_PROMPT"] = "FAIL_PROMPT"; - AutoDiscoveryAction["FAIL_ERROR"] = "FAIL_ERROR"; -})(AutoDiscoveryAction = exports.AutoDiscoveryAction || (exports.AutoDiscoveryAction = {})); -var AutoDiscoveryError; -(function (AutoDiscoveryError) { - AutoDiscoveryError["Invalid"] = "Invalid homeserver discovery response"; - AutoDiscoveryError["GenericFailure"] = "Failed to get autodiscovery configuration from server"; - AutoDiscoveryError["InvalidHsBaseUrl"] = "Invalid base_url for m.homeserver"; - AutoDiscoveryError["InvalidHomeserver"] = "Homeserver URL does not appear to be a valid Matrix homeserver"; - AutoDiscoveryError["InvalidIsBaseUrl"] = "Invalid base_url for m.identity_server"; - AutoDiscoveryError["InvalidIdentityServer"] = "Identity server URL does not appear to be a valid identity server"; - AutoDiscoveryError["InvalidIs"] = "Invalid identity server discovery response"; - AutoDiscoveryError["MissingWellknown"] = "No .well-known JSON file found"; - AutoDiscoveryError["InvalidJson"] = "Invalid JSON"; -})(AutoDiscoveryError || (AutoDiscoveryError = {})); -/** - * Utilities for automatically discovery resources, such as homeservers - * for users to log in to. - */ -class AutoDiscovery { - /** - * Validates and verifies client configuration information for purposes - * of logging in. Such information includes the homeserver URL - * and identity server URL the client would want. Additional details - * may also be included, and will be transparently brought into the - * response object unaltered. - * @param wellknown - The configuration object itself, as returned - * by the .well-known auto-discovery endpoint. - * @returns Promise which resolves to the verified - * configuration, which may include error states. Rejects on unexpected - * failure, not when verification fails. - */ - static fromDiscoveryConfig(wellknown) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // Step 1 is to get the config, which is provided to us here. - // We default to an error state to make the first few checks easier to - // write. We'll update the properties of this object over the duration - // of this function. - const clientConfig = { - "m.homeserver": { - state: AutoDiscovery.FAIL_ERROR, - error: AutoDiscovery.ERROR_INVALID, - base_url: null, - }, - "m.identity_server": { - // Technically, we don't have a problem with the identity server - // config at this point. - state: AutoDiscovery.PROMPT, - error: null, - base_url: null, - }, - }; - if (!wellknown || !wellknown["m.homeserver"]) { - logger_1.logger.error("No m.homeserver key in config"); - clientConfig["m.homeserver"].state = AutoDiscovery.FAIL_PROMPT; - clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_INVALID; - return Promise.resolve(clientConfig); - } - if (!wellknown["m.homeserver"]["base_url"]) { - logger_1.logger.error("No m.homeserver base_url in config"); - clientConfig["m.homeserver"].state = AutoDiscovery.FAIL_PROMPT; - clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_INVALID_HS_BASE_URL; - return Promise.resolve(clientConfig); - } - // Step 2: Make sure the homeserver URL is valid *looking*. We'll make - // sure it points to a homeserver in Step 3. - const hsUrl = this.sanitizeWellKnownUrl(wellknown["m.homeserver"]["base_url"]); - if (!hsUrl) { - logger_1.logger.error("Invalid base_url for m.homeserver"); - clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_INVALID_HS_BASE_URL; - return Promise.resolve(clientConfig); - } - // Step 3: Make sure the homeserver URL points to a homeserver. - const hsVersions = yield this.fetchWellKnownObject(`${hsUrl}/_matrix/client/versions`); - if (!hsVersions || !((_a = hsVersions.raw) === null || _a === void 0 ? void 0 : _a["versions"])) { - logger_1.logger.error("Invalid /versions response"); - clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_INVALID_HOMESERVER; - // Supply the base_url to the caller because they may be ignoring liveliness - // errors, like this one. - clientConfig["m.homeserver"].base_url = hsUrl; - return Promise.resolve(clientConfig); - } - // Step 4: Now that the homeserver looks valid, update our client config. - clientConfig["m.homeserver"] = { - state: AutoDiscovery.SUCCESS, - error: null, - base_url: hsUrl, - }; - // Step 5: Try to pull out the identity server configuration - let isUrl = ""; - if (wellknown["m.identity_server"]) { - // We prepare a failing identity server response to save lines later - // in this branch. - const failingClientConfig = { - "m.homeserver": clientConfig["m.homeserver"], - "m.identity_server": { - state: AutoDiscovery.FAIL_PROMPT, - error: AutoDiscovery.ERROR_INVALID_IS, - base_url: null, - }, - }; - // Step 5a: Make sure the URL is valid *looking*. We'll make sure it - // points to an identity server in Step 5b. - isUrl = this.sanitizeWellKnownUrl(wellknown["m.identity_server"]["base_url"]); - if (!isUrl) { - logger_1.logger.error("Invalid base_url for m.identity_server"); - failingClientConfig["m.identity_server"].error = AutoDiscovery.ERROR_INVALID_IS_BASE_URL; - return Promise.resolve(failingClientConfig); - } - // Step 5b: Verify there is an identity server listening on the provided - // URL. - const isResponse = yield this.fetchWellKnownObject(`${isUrl}/_matrix/identity/v2`); - if (!(isResponse === null || isResponse === void 0 ? void 0 : isResponse.raw) || isResponse.action !== AutoDiscoveryAction.SUCCESS) { - logger_1.logger.error("Invalid /v2 response"); - failingClientConfig["m.identity_server"].error = AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER; - // Supply the base_url to the caller because they may be ignoring - // liveliness errors, like this one. - failingClientConfig["m.identity_server"].base_url = isUrl; - return Promise.resolve(failingClientConfig); - } - } - // Step 6: Now that the identity server is valid, or never existed, - // populate the IS section. - if (isUrl && isUrl.toString().length > 0) { - clientConfig["m.identity_server"] = { - state: AutoDiscovery.SUCCESS, - error: null, - base_url: isUrl, - }; - } - // Step 7: Copy any other keys directly into the clientConfig. This is for - // things like custom configuration of services. - Object.keys(wellknown).forEach((k) => { - if (k === "m.homeserver" || k === "m.identity_server") { - // Only copy selected parts of the config to avoid overwriting - // properties computed by the validation logic above. - const notProps = ["error", "state", "base_url"]; - for (const prop of Object.keys(wellknown[k])) { - if (notProps.includes(prop)) - continue; - // @ts-ignore - ts gets unhappy as we're mixing types here - clientConfig[k][prop] = wellknown[k][prop]; - } - } - else { - // Just copy the whole thing over otherwise - clientConfig[k] = wellknown[k]; - } - }); - // Step 8: Give the config to the caller (finally) - return Promise.resolve(clientConfig); - }); - } - /** - * Attempts to automatically discover client configuration information - * prior to logging in. Such information includes the homeserver URL - * and identity server URL the client would want. Additional details - * may also be discovered, and will be transparently included in the - * response object unaltered. - * @param domain - The homeserver domain to perform discovery - * on. For example, "matrix.org". - * @returns Promise which resolves to the discovered - * configuration, which may include error states. Rejects on unexpected - * failure, not when discovery fails. - */ - static findClientConfig(domain) { - return __awaiter(this, void 0, void 0, function* () { - if (!domain || typeof domain !== "string" || domain.length === 0) { - throw new Error("'domain' must be a string of non-zero length"); - } - // We use a .well-known lookup for all cases. According to the spec, we - // can do other discovery mechanisms if we want such as custom lookups - // however we won't bother with that here (mostly because the spec only - // supports .well-known right now). - // - // By using .well-known, we need to ensure we at least pull out a URL - // for the homeserver. We don't really need an identity server configuration - // but will return one anyways (with state PROMPT) to make development - // easier for clients. If we can't get a homeserver URL, all bets are - // off on the rest of the config and we'll assume it is invalid too. - // We default to an error state to make the first few checks easier to - // write. We'll update the properties of this object over the duration - // of this function. - const clientConfig = { - "m.homeserver": { - state: AutoDiscovery.FAIL_ERROR, - error: AutoDiscovery.ERROR_INVALID, - base_url: null, - }, - "m.identity_server": { - // Technically, we don't have a problem with the identity server - // config at this point. - state: AutoDiscovery.PROMPT, - error: null, - base_url: null, - }, - }; - // Step 1: Actually request the .well-known JSON file and make sure it - // at least has a homeserver definition. - const wellknown = yield this.fetchWellKnownObject(`https://${domain}/.well-known/matrix/client`); - if (!wellknown || wellknown.action !== AutoDiscoveryAction.SUCCESS) { - logger_1.logger.error("No response or error when parsing .well-known"); - if (wellknown.reason) - logger_1.logger.error(wellknown.reason); - if (wellknown.action === AutoDiscoveryAction.IGNORE) { - clientConfig["m.homeserver"] = { - state: AutoDiscovery.PROMPT, - error: null, - base_url: null, - }; - } - else { - // this can only ever be FAIL_PROMPT at this point. - clientConfig["m.homeserver"].state = AutoDiscovery.FAIL_PROMPT; - clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_INVALID; - } - return Promise.resolve(clientConfig); - } - // Step 2: Validate and parse the config - return AutoDiscovery.fromDiscoveryConfig(wellknown.raw); - }); - } - /** - * Gets the raw discovery client configuration for the given domain name. - * Should only be used if there's no validation to be done on the resulting - * object, otherwise use findClientConfig(). - * @param domain - The domain to get the client config for. - * @returns Promise which resolves to the domain's client config. Can - * be an empty object. - */ - static getRawClientConfig(domain) { - return __awaiter(this, void 0, void 0, function* () { - if (!domain || typeof domain !== "string" || domain.length === 0) { - throw new Error("'domain' must be a string of non-zero length"); - } - const response = yield this.fetchWellKnownObject(`https://${domain}/.well-known/matrix/client`); - if (!response) - return {}; - return response.raw || {}; - }); - } - /** - * Sanitizes a given URL to ensure it is either an HTTP or HTTP URL and - * is suitable for the requirements laid out by .well-known auto discovery. - * If valid, the URL will also be stripped of any trailing slashes. - * @param url - The potentially invalid URL to sanitize. - * @returns The sanitized URL or a falsey value if the URL is invalid. - * @internal - */ - static sanitizeWellKnownUrl(url) { - if (!url) - return false; - try { - let parsed; - try { - parsed = new URL(url); - } - catch (e) { - logger_1.logger.error("Could not parse url", e); - } - if (!(parsed === null || parsed === void 0 ? void 0 : parsed.hostname)) - return false; - if (parsed.protocol !== "http:" && parsed.protocol !== "https:") - return false; - const port = parsed.port ? `:${parsed.port}` : ""; - const path = parsed.pathname ? parsed.pathname : ""; - let saferUrl = `${parsed.protocol}//${parsed.hostname}${port}${path}`; - if (saferUrl.endsWith("/")) { - saferUrl = saferUrl.substring(0, saferUrl.length - 1); - } - return saferUrl; - } - catch (e) { - logger_1.logger.error(e); - return false; - } - } - static fetch(resource, options) { - if (this.fetchFn) { - return this.fetchFn(resource, options); - } - return global.fetch(resource, options); - } - static setFetchFn(fetchFn) { - AutoDiscovery.fetchFn = fetchFn; - } - /** - * Fetches a JSON object from a given URL, as expected by all .well-known - * related lookups. If the server gives a 404 then the `action` will be - * IGNORE. If the server returns something that isn't JSON, the `action` - * will be FAIL_PROMPT. For any other failure the `action` will be FAIL_PROMPT. - * - * The returned object will be a result of the call in object form with - * the following properties: - * raw: The JSON object returned by the server. - * action: One of SUCCESS, IGNORE, or FAIL_PROMPT. - * reason: Relatively human-readable description of what went wrong. - * error: The actual Error, if one exists. - * @param url - The URL to fetch a JSON object from. - * @returns Promise which resolves to the returned state. - * @internal - */ - static fetchWellKnownObject(url) { - return __awaiter(this, void 0, void 0, function* () { - let response; - try { - response = yield AutoDiscovery.fetch(url, { - method: http_api_1.Method.Get, - signal: (0, http_api_1.timeoutSignal)(5000), - }); - if (response.status === 404) { - return { - raw: {}, - action: AutoDiscoveryAction.IGNORE, - reason: AutoDiscovery.ERROR_MISSING_WELLKNOWN, - }; - } - if (!response.ok) { - return { - raw: {}, - action: AutoDiscoveryAction.FAIL_PROMPT, - reason: "General failure", - }; - } - } - catch (err) { - const error = err; - let reason = ""; - if (typeof error === "object") { - reason = error === null || error === void 0 ? void 0 : error.message; - } - return { - error, - raw: {}, - action: AutoDiscoveryAction.FAIL_PROMPT, - reason: reason || "General failure", - }; - } - try { - return { - raw: yield response.json(), - action: AutoDiscoveryAction.SUCCESS, - }; - } - catch (err) { - const error = err; - return { - error, - raw: {}, - action: AutoDiscoveryAction.FAIL_PROMPT, - reason: (error === null || error === void 0 ? void 0 : error.name) === "SyntaxError" - ? AutoDiscovery.ERROR_INVALID_JSON - : AutoDiscovery.ERROR_INVALID, - }; - } - }); - } -} -exports.AutoDiscovery = AutoDiscovery; -// Dev note: the constants defined here are related to but not -// exactly the same as those in the spec. This is to hopefully -// translate the meaning of the states in the spec, but also -// support our own if needed. -AutoDiscovery.ERROR_INVALID = AutoDiscoveryError.Invalid; -AutoDiscovery.ERROR_GENERIC_FAILURE = AutoDiscoveryError.GenericFailure; -AutoDiscovery.ERROR_INVALID_HS_BASE_URL = AutoDiscoveryError.InvalidHsBaseUrl; -AutoDiscovery.ERROR_INVALID_HOMESERVER = AutoDiscoveryError.InvalidHomeserver; -AutoDiscovery.ERROR_INVALID_IS_BASE_URL = AutoDiscoveryError.InvalidIsBaseUrl; -AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER = AutoDiscoveryError.InvalidIdentityServer; -AutoDiscovery.ERROR_INVALID_IS = AutoDiscoveryError.InvalidIs; -AutoDiscovery.ERROR_MISSING_WELLKNOWN = AutoDiscoveryError.MissingWellknown; -AutoDiscovery.ERROR_INVALID_JSON = AutoDiscoveryError.InvalidJson; -AutoDiscovery.ALL_ERRORS = Object.keys(AutoDiscoveryError); -/** - * The auto discovery failed. The client is expected to communicate - * the error to the user and refuse logging in. - */ -AutoDiscovery.FAIL_ERROR = AutoDiscoveryAction.FAIL_ERROR; -/** - * The auto discovery failed, however the client may still recover - * from the problem. The client is recommended to that the same - * action it would for PROMPT while also warning the user about - * what went wrong. The client may also treat this the same as - * a FAIL_ERROR state. - */ -AutoDiscovery.FAIL_PROMPT = AutoDiscoveryAction.FAIL_PROMPT; -/** - * The auto discovery didn't fail but did not find anything of - * interest. The client is expected to prompt the user for more - * information, or fail if it prefers. - */ -AutoDiscovery.PROMPT = AutoDiscoveryAction.PROMPT; -/** - * The auto discovery was successful. - */ -AutoDiscovery.SUCCESS = AutoDiscoveryAction.SUCCESS; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./http-api":367,"./logger":374}],320:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const matrixcs = __importStar(require("./matrix")); -if (global.__js_sdk_entrypoint) { - throw new Error("Multiple matrix-js-sdk entrypoints detected!"); -} -global.__js_sdk_entrypoint = true; -// just *accessing* indexedDB throws an exception in firefox with indexeddb disabled. -let indexedDB; -try { - indexedDB = global.indexedDB; -} -catch (e) { } -// if our browser (appears to) support indexeddb, use an indexeddb crypto store. -if (indexedDB) { - matrixcs.setCryptoStoreFactory(() => new matrixcs.IndexedDBCryptoStore(indexedDB, "matrix-js-sdk:crypto")); -} -// We export 3 things to make browserify happy as well as downstream projects. -// It's awkward, but required. -__exportStar(require("./matrix"), exports); -exports.default = matrixcs; // keep export for browserify package deps -global.matrixcs = matrixcs; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./matrix":375}],321:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2015-2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.fixNotificationCountOnDecryption = exports.MatrixClient = exports.ClientEvent = exports.M_AUTHENTICATION = exports.RoomVersionStability = exports.PendingEventOrdering = exports.UNSTABLE_MSC3852_LAST_SEEN_UA = exports.CRYPTO_ENABLED = void 0; -const sync_1 = require("./sync"); -const event_1 = require("./models/event"); -const stub_1 = require("./store/stub"); -const call_1 = require("./webrtc/call"); -const filter_1 = require("./filter"); -const callEventHandler_1 = require("./webrtc/callEventHandler"); -const utils = __importStar(require("./utils")); -const utils_1 = require("./utils"); -const event_timeline_1 = require("./models/event-timeline"); -const pushprocessor_1 = require("./pushprocessor"); -const autodiscovery_1 = require("./autodiscovery"); -const olmlib = __importStar(require("./crypto/olmlib")); -const olmlib_1 = require("./crypto/olmlib"); -const ReEmitter_1 = require("./ReEmitter"); -const RoomList_1 = require("./crypto/RoomList"); -const logger_1 = require("./logger"); -const service_types_1 = require("./service-types"); -const http_api_1 = require("./http-api"); -const crypto_1 = require("./crypto"); -const recoverykey_1 = require("./crypto/recoverykey"); -const key_passphrase_1 = require("./crypto/key_passphrase"); -const user_1 = require("./models/user"); -const content_repo_1 = require("./content-repo"); -const search_result_1 = require("./models/search-result"); -const dehydration_1 = require("./crypto/dehydration"); -const api_1 = require("./crypto/api"); -const ContentHelpers = __importStar(require("./content-helpers")); -const room_1 = require("./models/room"); -const room_member_1 = require("./models/room-member"); -const event_2 = require("./@types/event"); -const partials_1 = require("./@types/partials"); -const event_mapper_1 = require("./event-mapper"); -const randomstring_1 = require("./randomstring"); -const backup_1 = require("./crypto/backup"); -const MSC3089TreeSpace_1 = require("./models/MSC3089TreeSpace"); -const search_1 = require("./@types/search"); -const PushRules_1 = require("./@types/PushRules"); -const groupCall_1 = require("./webrtc/groupCall"); -const mediaHandler_1 = require("./webrtc/mediaHandler"); -const groupCallEventHandler_1 = require("./webrtc/groupCallEventHandler"); -const typed_event_emitter_1 = require("./models/typed-event-emitter"); -const read_receipts_1 = require("./@types/read_receipts"); -const sliding_sync_sdk_1 = require("./sliding-sync-sdk"); -const thread_1 = require("./models/thread"); -const beacon_1 = require("./@types/beacon"); -const NamespacedValue_1 = require("./NamespacedValue"); -const ToDeviceMessageQueue_1 = require("./ToDeviceMessageQueue"); -const invites_ignorer_1 = require("./models/invites-ignorer"); -const feature_1 = require("./feature"); -const constants_1 = require("./rust-crypto/constants"); -const SCROLLBACK_DELAY_MS = 3000; -exports.CRYPTO_ENABLED = (0, crypto_1.isCryptoAvailable)(); -const CAPABILITIES_CACHE_MS = 21600000; // 6 hours - an arbitrary value -const TURN_CHECK_INTERVAL = 10 * 60 * 1000; // poll for turn credentials every 10 minutes -exports.UNSTABLE_MSC3852_LAST_SEEN_UA = new NamespacedValue_1.UnstableValue("last_seen_user_agent", "org.matrix.msc3852.last_seen_user_agent"); -var PendingEventOrdering; -(function (PendingEventOrdering) { - PendingEventOrdering["Chronological"] = "chronological"; - PendingEventOrdering["Detached"] = "detached"; -})(PendingEventOrdering = exports.PendingEventOrdering || (exports.PendingEventOrdering = {})); -var RoomVersionStability; -(function (RoomVersionStability) { - RoomVersionStability["Stable"] = "stable"; - RoomVersionStability["Unstable"] = "unstable"; -})(RoomVersionStability = exports.RoomVersionStability || (exports.RoomVersionStability = {})); -var CrossSigningKeyType; -(function (CrossSigningKeyType) { - CrossSigningKeyType["MasterKey"] = "master_key"; - CrossSigningKeyType["SelfSigningKey"] = "self_signing_key"; - CrossSigningKeyType["UserSigningKey"] = "user_signing_key"; -})(CrossSigningKeyType || (CrossSigningKeyType = {})); -exports.M_AUTHENTICATION = new NamespacedValue_1.UnstableValue("m.authentication", "org.matrix.msc2965.authentication"); -/* eslint-enable camelcase */ -// We're using this constant for methods overloading and inspect whether a variable -// contains an eventId or not. This was required to ensure backwards compatibility -// of methods for threads -// Probably not the most graceful solution but does a good enough job for now -const EVENT_ID_PREFIX = "$"; -var ClientEvent; -(function (ClientEvent) { - ClientEvent["Sync"] = "sync"; - ClientEvent["Event"] = "event"; - ClientEvent["ToDeviceEvent"] = "toDeviceEvent"; - ClientEvent["AccountData"] = "accountData"; - ClientEvent["Room"] = "Room"; - ClientEvent["DeleteRoom"] = "deleteRoom"; - ClientEvent["SyncUnexpectedError"] = "sync.unexpectedError"; - ClientEvent["ClientWellKnown"] = "WellKnown.client"; - ClientEvent["ReceivedVoipEvent"] = "received_voip_event"; - ClientEvent["UndecryptableToDeviceEvent"] = "toDeviceEvent.undecryptable"; - ClientEvent["TurnServers"] = "turnServers"; - ClientEvent["TurnServersError"] = "turnServers.error"; -})(ClientEvent = exports.ClientEvent || (exports.ClientEvent = {})); -const SSO_ACTION_PARAM = new NamespacedValue_1.UnstableValue("action", "org.matrix.msc3824.action"); -/** - * Represents a Matrix Client. Only directly construct this if you want to use - * custom modules. Normally, {@link createClient} should be used - * as it specifies 'sensible' defaults for these modules. - */ -class MatrixClient extends typed_event_emitter_1.TypedEventEmitter { - constructor(opts) { - var _a; - super(); - this.reEmitter = new ReEmitter_1.TypedReEmitter(this); - this.olmVersion = null; // populated after initCrypto - this.usingExternalCrypto = false; - this.clientRunning = false; - this.timelineSupport = false; - this.urlPreviewCache = {}; - this.supportsCallTransfer = false; // XXX: Intended private, used in code. - this.forceTURN = false; // XXX: Intended private, used in code. - this.iceCandidatePoolSize = 0; // XXX: Intended private, used in code. - // Note: these are all `protected` to let downstream consumers make mistakes if they want to. - // We don't technically support this usage, but have reasons to do this. - this.canSupportVoip = false; - this.peekSync = null; - this.isGuestAccount = false; - this.ongoingScrollbacks = {}; - this.notifTimelineSet = null; - this.fallbackICEServerAllowed = false; - this.syncedLeftRooms = false; - this.canSupport = new Map(); - // The pushprocessor caches useful things, so keep one and re-use it - this.pushProcessor = new pushprocessor_1.PushProcessor(this); - this.turnServers = []; - this.turnServersExpiry = 0; - this.txnCtr = 0; - this.mediaHandler = new mediaHandler_1.MediaHandler(this); - this.pendingEventEncryption = new Map(); - this.useE2eForGroupCall = true; - this.startCallEventHandler = () => { - if (this.isInitialSyncComplete()) { - this.callEventHandler.start(); - this.groupCallEventHandler.start(); - this.off(ClientEvent.Sync, this.startCallEventHandler); - } - }; - /** - * Once the client has been initialised, we want to clear notifications we - * know for a fact should be here. - * This issue should also be addressed on synapse's side and is tracked as part - * of https://github.com/matrix-org/synapse/issues/14837 - * - * We consider a room or a thread as fully read if the current user has sent - * the last event in the live timeline of that context and if the read receipt - * we have on record matches. - */ - this.fixupRoomNotifications = () => { - var _a; - if (this.isInitialSyncComplete()) { - const unreadRooms = ((_a = this.getRooms()) !== null && _a !== void 0 ? _a : []).filter((room) => { - return room.getUnreadNotificationCount(room_1.NotificationCountType.Total) > 0; - }); - for (const room of unreadRooms) { - const currentUserId = this.getSafeUserId(); - room.fixupNotifications(currentUserId); - } - this.off(ClientEvent.Sync, this.fixupRoomNotifications); - } - }; - opts.baseUrl = utils.ensureNoTrailingSlash(opts.baseUrl); - opts.idBaseUrl = utils.ensureNoTrailingSlash(opts.idBaseUrl); - this.baseUrl = opts.baseUrl; - this.idBaseUrl = opts.idBaseUrl; - this.identityServer = opts.identityServer; - this.usingExternalCrypto = (_a = opts.usingExternalCrypto) !== null && _a !== void 0 ? _a : false; - this.store = opts.store || new stub_1.StubStore(); - this.deviceId = opts.deviceId || null; - this.sessionId = (0, randomstring_1.randomString)(10); - const userId = opts.userId || null; - this.credentials = { userId }; - this.http = new http_api_1.MatrixHttpApi(this, { - fetchFn: opts.fetchFn, - baseUrl: opts.baseUrl, - idBaseUrl: opts.idBaseUrl, - accessToken: opts.accessToken, - prefix: http_api_1.ClientPrefix.R0, - onlyData: true, - extraParams: opts.queryParams, - localTimeoutMs: opts.localTimeoutMs, - useAuthorizationHeader: opts.useAuthorizationHeader, - }); - if (opts.deviceToImport) { - if (this.deviceId) { - logger_1.logger.warn("not importing device because device ID is provided to " + - "constructor independently of exported data"); - } - else if (this.credentials.userId) { - logger_1.logger.warn("not importing device because user ID is provided to " + - "constructor independently of exported data"); - } - else if (!opts.deviceToImport.deviceId) { - logger_1.logger.warn("not importing device because no device ID in exported data"); - } - else { - this.deviceId = opts.deviceToImport.deviceId; - this.credentials.userId = opts.deviceToImport.userId; - // will be used during async initialization of the crypto - this.exportedOlmDeviceToImport = opts.deviceToImport.olmDevice; - } - } - else if (opts.pickleKey) { - this.pickleKey = opts.pickleKey; - } - this.scheduler = opts.scheduler; - if (this.scheduler) { - this.scheduler.setProcessFunction((eventToSend) => __awaiter(this, void 0, void 0, function* () { - const room = this.getRoom(eventToSend.getRoomId()); - if (eventToSend.status !== event_1.EventStatus.SENDING) { - this.updatePendingEventStatus(room, eventToSend, event_1.EventStatus.SENDING); - } - const res = yield this.sendEventHttpRequest(eventToSend); - if (room) { - // ensure we update pending event before the next scheduler run so that any listeners to event id - // updates on the synchronous event emitter get a chance to run first. - room.updatePendingEvent(eventToSend, event_1.EventStatus.SENT, res.event_id); - } - return res; - })); - } - if ((0, call_1.supportsMatrixCall)()) { - this.callEventHandler = new callEventHandler_1.CallEventHandler(this); - this.groupCallEventHandler = new groupCallEventHandler_1.GroupCallEventHandler(this); - this.canSupportVoip = true; - // Start listening for calls after the initial sync is done - // We do not need to backfill the call event buffer - // with encrypted events that might never get decrypted - this.on(ClientEvent.Sync, this.startCallEventHandler); - } - this.on(ClientEvent.Sync, this.fixupRoomNotifications); - this.timelineSupport = Boolean(opts.timelineSupport); - this.cryptoStore = opts.cryptoStore; - this.verificationMethods = opts.verificationMethods; - this.cryptoCallbacks = opts.cryptoCallbacks || {}; - this.forceTURN = opts.forceTURN || false; - this.iceCandidatePoolSize = opts.iceCandidatePoolSize === undefined ? 0 : opts.iceCandidatePoolSize; - this.supportsCallTransfer = opts.supportsCallTransfer || false; - this.fallbackICEServerAllowed = opts.fallbackICEServerAllowed || false; - this.isVoipWithNoMediaAllowed = opts.isVoipWithNoMediaAllowed || false; - if (opts.useE2eForGroupCall !== undefined) - this.useE2eForGroupCall = opts.useE2eForGroupCall; - // List of which rooms have encryption enabled: separate from crypto because - // we still want to know which rooms are encrypted even if crypto is disabled: - // we don't want to start sending unencrypted events to them. - this.roomList = new RoomList_1.RoomList(this.cryptoStore); - this.roomNameGenerator = opts.roomNameGenerator; - this.toDeviceMessageQueue = new ToDeviceMessageQueue_1.ToDeviceMessageQueue(this); - // The SDK doesn't really provide a clean way for events to recalculate the push - // actions for themselves, so we have to kinda help them out when they are encrypted. - // We do this so that push rules are correctly executed on events in their decrypted - // state, such as highlights when the user's name is mentioned. - this.on(event_1.MatrixEventEvent.Decrypted, (event) => { - fixNotificationCountOnDecryption(this, event); - }); - // Like above, we have to listen for read receipts from ourselves in order to - // correctly handle notification counts on encrypted rooms. - // This fixes https://github.com/vector-im/element-web/issues/9421 - this.on(room_1.RoomEvent.Receipt, (event, room) => { - var _a; - if (room && this.isRoomEncrypted(room.roomId)) { - // Figure out if we've read something or if it's just informational - const content = event.getContent(); - const isSelf = Object.keys(content).filter((eid) => { - for (const [key, value] of Object.entries(content[eid])) { - if (!utils.isSupportedReceiptType(key)) - continue; - if (!value) - continue; - if (Object.keys(value).includes(this.getUserId())) - return true; - } - return false; - }).length > 0; - if (!isSelf) - return; - // Work backwards to determine how many events are unread. We also set - // a limit for how back we'll look to avoid spinning CPU for too long. - // If we hit the limit, we assume the count is unchanged. - const maxHistory = 20; - const events = room.getLiveTimeline().getEvents(); - let highlightCount = 0; - for (let i = events.length - 1; i >= 0; i--) { - if (i === events.length - maxHistory) - return; // limit reached - const event = events[i]; - if (room.hasUserReadEvent(this.getUserId(), event.getId())) { - // If the user has read the event, then the counting is done. - break; - } - const pushActions = this.getPushActionsForEvent(event); - highlightCount += ((_a = pushActions === null || pushActions === void 0 ? void 0 : pushActions.tweaks) === null || _a === void 0 ? void 0 : _a.highlight) ? 1 : 0; - } - // Note: we don't need to handle 'total' notifications because the counts - // will come from the server. - room.setUnreadNotificationCount(room_1.NotificationCountType.Highlight, highlightCount); - } - }); - this.ignoredInvites = new invites_ignorer_1.IgnoredInvites(this); - } - /** - * High level helper method to begin syncing and poll for new events. To listen for these - * events, add a listener for {@link ClientEvent.Event} - * via {@link MatrixClient#on}. Alternatively, listen for specific - * state change events. - * @param opts - Options to apply when syncing. - */ - startClient(opts) { - return __awaiter(this, void 0, void 0, function* () { - if (this.clientRunning) { - // client is already running. - return; - } - this.clientRunning = true; - // backwards compat for when 'opts' was 'historyLen'. - if (typeof opts === "number") { - opts = { - initialSyncLimit: opts, - }; - } - // Create our own user object artificially (instead of waiting for sync) - // so it's always available, even if the user is not in any rooms etc. - const userId = this.getUserId(); - if (userId) { - this.store.storeUser(new user_1.User(userId)); - } - // periodically poll for turn servers if we support voip - if (this.canSupportVoip) { - this.checkTurnServersIntervalID = setInterval(() => { - this.checkTurnServers(); - }, TURN_CHECK_INTERVAL); - // noinspection ES6MissingAwait - this.checkTurnServers(); - } - if (this.syncApi) { - // This shouldn't happen since we thought the client was not running - logger_1.logger.error("Still have sync object whilst not running: stopping old one"); - this.syncApi.stop(); - } - try { - yield this.getVersions(); - // This should be done with `canSupport` - // TODO: https://github.com/vector-im/element-web/issues/23643 - const { threads, list, fwdPagination } = yield this.doesServerSupportThread(); - thread_1.Thread.setServerSideSupport(threads); - thread_1.Thread.setServerSideListSupport(list); - thread_1.Thread.setServerSideFwdPaginationSupport(fwdPagination); - } - catch (e) { - logger_1.logger.error("Can't fetch server versions, continuing to initialise sync, this will be retried later", e); - } - this.clientOpts = opts !== null && opts !== void 0 ? opts : {}; - if (this.clientOpts.slidingSync) { - this.syncApi = new sliding_sync_sdk_1.SlidingSyncSdk(this.clientOpts.slidingSync, this, this.clientOpts, this.buildSyncApiOptions()); - } - else { - this.syncApi = new sync_1.SyncApi(this, this.clientOpts, this.buildSyncApiOptions()); - } - if (this.clientOpts.hasOwnProperty("experimentalThreadSupport")) { - logger_1.logger.warn("`experimentalThreadSupport` has been deprecated, use `threadSupport` instead"); - } - // If `threadSupport` is omitted and the deprecated `experimentalThreadSupport` has been passed - // We should fallback to that value for backwards compatibility purposes - if (!this.clientOpts.hasOwnProperty("threadSupport") && - this.clientOpts.hasOwnProperty("experimentalThreadSupport")) { - this.clientOpts.threadSupport = this.clientOpts.experimentalThreadSupport; - } - this.syncApi.sync(); - if (this.clientOpts.clientWellKnownPollPeriod !== undefined) { - this.clientWellKnownIntervalID = setInterval(() => { - this.fetchClientWellKnown(); - }, 1000 * this.clientOpts.clientWellKnownPollPeriod); - this.fetchClientWellKnown(); - } - this.toDeviceMessageQueue.start(); - }); - } - /** - * Construct a SyncApiOptions for this client, suitable for passing into the SyncApi constructor - */ - buildSyncApiOptions() { - return { - crypto: this.crypto, - cryptoCallbacks: this.cryptoBackend, - canResetEntireTimeline: (roomId) => { - if (!this.canResetTimelineCallback) { - return false; - } - return this.canResetTimelineCallback(roomId); - }, - }; - } - /** - * High level helper method to stop the client from polling and allow a - * clean shutdown. - */ - stopClient() { - var _a, _b, _c, _d, _e; - (_a = this.cryptoBackend) === null || _a === void 0 ? void 0 : _a.stop(); // crypto might have been initialised even if the client wasn't fully started - if (!this.clientRunning) - return; // already stopped - logger_1.logger.log("stopping MatrixClient"); - this.clientRunning = false; - (_b = this.syncApi) === null || _b === void 0 ? void 0 : _b.stop(); - this.syncApi = undefined; - (_c = this.peekSync) === null || _c === void 0 ? void 0 : _c.stopPeeking(); - (_d = this.callEventHandler) === null || _d === void 0 ? void 0 : _d.stop(); - (_e = this.groupCallEventHandler) === null || _e === void 0 ? void 0 : _e.stop(); - this.callEventHandler = undefined; - this.groupCallEventHandler = undefined; - global.clearInterval(this.checkTurnServersIntervalID); - this.checkTurnServersIntervalID = undefined; - if (this.clientWellKnownIntervalID !== undefined) { - global.clearInterval(this.clientWellKnownIntervalID); - } - this.toDeviceMessageQueue.stop(); - } - /** - * Try to rehydrate a device if available. The client must have been - * initialized with a `cryptoCallback.getDehydrationKey` option, and this - * function must be called before initCrypto and startClient are called. - * - * @returns Promise which resolves to undefined if a device could not be dehydrated, or - * to the new device ID if the dehydration was successful. - * @returns Rejects: with an error response. - */ - rehydrateDevice() { - return __awaiter(this, void 0, void 0, function* () { - if (this.crypto) { - throw new Error("Cannot rehydrate device after crypto is initialized"); - } - if (!this.cryptoCallbacks.getDehydrationKey) { - return; - } - const getDeviceResult = yield this.getDehydratedDevice(); - if (!getDeviceResult) { - return; - } - if (!getDeviceResult.device_data || !getDeviceResult.device_id) { - logger_1.logger.info("no dehydrated device found"); - return; - } - const account = new global.Olm.Account(); - try { - const deviceData = getDeviceResult.device_data; - if (deviceData.algorithm !== dehydration_1.DEHYDRATION_ALGORITHM) { - logger_1.logger.warn("Wrong algorithm for dehydrated device"); - return; - } - logger_1.logger.log("unpickling dehydrated device"); - const key = yield this.cryptoCallbacks.getDehydrationKey(deviceData, (k) => { - // copy the key so that it doesn't get clobbered - account.unpickle(new Uint8Array(k), deviceData.account); - }); - account.unpickle(key, deviceData.account); - logger_1.logger.log("unpickled device"); - const rehydrateResult = yield this.http.authedRequest(http_api_1.Method.Post, "/dehydrated_device/claim", undefined, { - device_id: getDeviceResult.device_id, - }, { - prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2", - }); - if (rehydrateResult.success) { - this.deviceId = getDeviceResult.device_id; - logger_1.logger.info("using dehydrated device"); - const pickleKey = this.pickleKey || "DEFAULT_KEY"; - this.exportedOlmDeviceToImport = { - pickledAccount: account.pickle(pickleKey), - sessions: [], - pickleKey: pickleKey, - }; - account.free(); - return this.deviceId; - } - else { - account.free(); - logger_1.logger.info("not using dehydrated device"); - return; - } - } - catch (e) { - account.free(); - logger_1.logger.warn("could not unpickle", e); - } - }); - } - /** - * Get the current dehydrated device, if any - * @returns A promise of an object containing the dehydrated device - */ - getDehydratedDevice() { - return __awaiter(this, void 0, void 0, function* () { - try { - return yield this.http.authedRequest(http_api_1.Method.Get, "/dehydrated_device", undefined, undefined, { - prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2", - }); - } - catch (e) { - logger_1.logger.info("could not get dehydrated device", e); - return; - } - }); - } - /** - * Set the dehydration key. This will also periodically dehydrate devices to - * the server. - * - * @param key - the dehydration key - * @param keyInfo - Information about the key. Primarily for - * information about how to generate the key from a passphrase. - * @param deviceDisplayName - The device display name for the - * dehydrated device. - * @returns A promise that resolves when the dehydrated device is stored. - */ - setDehydrationKey(key, keyInfo, deviceDisplayName) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - logger_1.logger.warn("not dehydrating device if crypto is not enabled"); - return; - } - return this.crypto.dehydrationManager.setKeyAndQueueDehydration(key, keyInfo, deviceDisplayName); - }); - } - /** - * Creates a new dehydrated device (without queuing periodic dehydration) - * @param key - the dehydration key - * @param keyInfo - Information about the key. Primarily for - * information about how to generate the key from a passphrase. - * @param deviceDisplayName - The device display name for the - * dehydrated device. - * @returns the device id of the newly created dehydrated device - */ - createDehydratedDevice(key, keyInfo, deviceDisplayName) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - logger_1.logger.warn("not dehydrating device if crypto is not enabled"); - return; - } - yield this.crypto.dehydrationManager.setKey(key, keyInfo, deviceDisplayName); - return this.crypto.dehydrationManager.dehydrateDevice(); - }); - } - exportDevice() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - logger_1.logger.warn("not exporting device if crypto is not enabled"); - return; - } - return { - userId: this.credentials.userId, - deviceId: this.deviceId, - // XXX: Private member access. - olmDevice: yield this.crypto.olmDevice.export(), - }; - }); - } - /** - * Clear any data out of the persistent stores used by the client. - * - * @returns Promise which resolves when the stores have been cleared. - */ - clearStores() { - if (this.clientRunning) { - throw new Error("Cannot clear stores while client is running"); - } - const promises = []; - promises.push(this.store.deleteAllData()); - if (this.cryptoStore) { - promises.push(this.cryptoStore.deleteAllData()); - } - // delete the stores used by the rust matrix-sdk-crypto, in case they were used - const deleteRustSdkStore = () => __awaiter(this, void 0, void 0, function* () { - let indexedDB; - try { - indexedDB = global.indexedDB; - } - catch (e) { - // No indexeddb support - return; - } - for (const dbname of [ - `${constants_1.RUST_SDK_STORE_PREFIX}::matrix-sdk-crypto`, - `${constants_1.RUST_SDK_STORE_PREFIX}::matrix-sdk-crypto-meta`, - ]) { - const prom = new Promise((resolve, reject) => { - logger_1.logger.info(`Removing IndexedDB instance ${dbname}`); - const req = indexedDB.deleteDatabase(dbname); - req.onsuccess = (_) => { - logger_1.logger.info(`Removed IndexedDB instance ${dbname}`); - resolve(0); - }; - req.onerror = (e) => { - // In private browsing, Firefox has a global.indexedDB, but attempts to delete an indexeddb - // (even a non-existent one) fail with "DOMException: A mutation operation was attempted on a - // database that did not allow mutations." - // - // it seems like the only thing we can really do is ignore the error. - logger_1.logger.warn(`Failed to remove IndexedDB instance ${dbname}:`, e); - resolve(0); - }; - req.onblocked = (e) => { - logger_1.logger.info(`cannot yet remove IndexedDB instance ${dbname}`); - }; - }); - yield prom; - } - }); - promises.push(deleteRustSdkStore()); - return Promise.all(promises).then(); // .then to fix types - } - /** - * Get the user-id of the logged-in user - * - * @returns MXID for the logged-in user, or null if not logged in - */ - getUserId() { - if (this.credentials && this.credentials.userId) { - return this.credentials.userId; - } - return null; - } - /** - * Get the user-id of the logged-in user - * - * @returns MXID for the logged-in user - * @throws Error if not logged in - */ - getSafeUserId() { - const userId = this.getUserId(); - if (!userId) { - throw new Error("Expected logged in user but found none."); - } - return userId; - } - /** - * Get the domain for this client's MXID - * @returns Domain of this MXID - */ - getDomain() { - if (this.credentials && this.credentials.userId) { - return this.credentials.userId.replace(/^.*?:/, ""); - } - return null; - } - /** - * Get the local part of the current user ID e.g. "foo" in "\@foo:bar". - * @returns The user ID localpart or null. - */ - getUserIdLocalpart() { - if (this.credentials && this.credentials.userId) { - return this.credentials.userId.split(":")[0].substring(1); - } - return null; - } - /** - * Get the device ID of this client - * @returns device ID - */ - getDeviceId() { - return this.deviceId; - } - /** - * Get the session ID of this client - * @returns session ID - */ - getSessionId() { - return this.sessionId; - } - /** - * Check if the runtime environment supports VoIP calling. - * @returns True if VoIP is supported. - */ - supportsVoip() { - return this.canSupportVoip; - } - /** - * @returns - */ - getMediaHandler() { - return this.mediaHandler; - } - /** - * Set whether VoIP calls are forced to use only TURN - * candidates. This is the same as the forceTURN option - * when creating the client. - * @param force - True to force use of TURN servers - */ - setForceTURN(force) { - this.forceTURN = force; - } - /** - * Set whether to advertise transfer support to other parties on Matrix calls. - * @param support - True to advertise the 'm.call.transferee' capability - */ - setSupportsCallTransfer(support) { - this.supportsCallTransfer = support; - } - /** - * Returns true if to-device signalling for group calls will be encrypted with Olm. - * If false, it will be sent unencrypted. - * @returns boolean Whether group call signalling will be encrypted - */ - getUseE2eForGroupCall() { - return this.useE2eForGroupCall; - } - /** - * Creates a new call. - * The place*Call methods on the returned call can be used to actually place a call - * - * @param roomId - The room the call is to be placed in. - * @returns the call or null if the browser doesn't support calling. - */ - createCall(roomId) { - return (0, call_1.createNewMatrixCall)(this, roomId); - } - /** - * Creates a new group call and sends the associated state event - * to alert other members that the room now has a group call. - * - * @param roomId - The room the call is to be placed in. - */ - createGroupCall(roomId, type, isPtt, intent, dataChannelsEnabled, dataChannelOptions) { - return __awaiter(this, void 0, void 0, function* () { - if (this.getGroupCallForRoom(roomId)) { - throw new Error(`${roomId} already has an existing group call`); - } - const room = this.getRoom(roomId); - if (!room) { - throw new Error(`Cannot find room ${roomId}`); - } - // Because without Media section a WebRTC connection is not possible, so need a RTCDataChannel to set up a - // no media WebRTC connection anyway. - return new groupCall_1.GroupCall(this, room, type, isPtt, intent, undefined, dataChannelsEnabled || this.isVoipWithNoMediaAllowed, dataChannelOptions, this.isVoipWithNoMediaAllowed).create(); - }); - } - /** - * Wait until an initial state for the given room has been processed by the - * client and the client is aware of any ongoing group calls. Awaiting on - * the promise returned by this method before calling getGroupCallForRoom() - * avoids races where getGroupCallForRoom is called before the state for that - * room has been processed. It does not, however, fix other races, eg. two - * clients both creating a group call at the same time. - * @param roomId - The room ID to wait for - * @returns A promise that resolves once existing group calls in the room - * have been processed. - */ - waitUntilRoomReadyForGroupCalls(roomId) { - return this.groupCallEventHandler.waitUntilRoomReadyForGroupCalls(roomId); - } - /** - * Get an existing group call for the provided room. - * @returns The group call or null if it doesn't already exist. - */ - getGroupCallForRoom(roomId) { - return this.groupCallEventHandler.groupCalls.get(roomId) || null; - } - /** - * Get the current sync state. - * @returns the sync state, which may be null. - * @see MatrixClient#event:"sync" - */ - getSyncState() { - var _a, _b; - return (_b = (_a = this.syncApi) === null || _a === void 0 ? void 0 : _a.getSyncState()) !== null && _b !== void 0 ? _b : null; - } - /** - * Returns the additional data object associated with - * the current sync state, or null if there is no - * such data. - * Sync errors, if available, are put in the 'error' key of - * this object. - */ - getSyncStateData() { - if (!this.syncApi) { - return null; - } - return this.syncApi.getSyncStateData(); - } - /** - * Whether the initial sync has completed. - * @returns True if at least one sync has happened. - */ - isInitialSyncComplete() { - const state = this.getSyncState(); - if (!state) { - return false; - } - return state === sync_1.SyncState.Prepared || state === sync_1.SyncState.Syncing; - } - /** - * Return whether the client is configured for a guest account. - * @returns True if this is a guest access_token (or no token is supplied). - */ - isGuest() { - return this.isGuestAccount; - } - /** - * Set whether this client is a guest account. This method is experimental - * and may change without warning. - * @param guest - True if this is a guest account. - */ - setGuest(guest) { - // EXPERIMENTAL: - // If the token is a macaroon, it should be encoded in it that it is a 'guest' - // access token, which means that the SDK can determine this entirely without - // the dev manually flipping this flag. - this.isGuestAccount = guest; - } - /** - * Return the provided scheduler, if any. - * @returns The scheduler or undefined - */ - getScheduler() { - return this.scheduler; - } - /** - * Retry a backed off syncing request immediately. This should only be used when - * the user explicitly attempts to retry their lost connection. - * Will also retry any outbound to-device messages currently in the queue to be sent - * (retries of regular outgoing events are handled separately, per-event). - * @returns True if this resulted in a request being retried. - */ - retryImmediately() { - var _a, _b; - // don't await for this promise: we just want to kick it off - this.toDeviceMessageQueue.sendQueue(); - return (_b = (_a = this.syncApi) === null || _a === void 0 ? void 0 : _a.retryImmediately()) !== null && _b !== void 0 ? _b : false; - } - /** - * Return the global notification EventTimelineSet, if any - * - * @returns the globl notification EventTimelineSet - */ - getNotifTimelineSet() { - return this.notifTimelineSet; - } - /** - * Set the global notification EventTimelineSet - * - */ - setNotifTimelineSet(set) { - this.notifTimelineSet = set; - } - /** - * Gets the capabilities of the homeserver. Always returns an object of - * capability keys and their options, which may be empty. - * @param fresh - True to ignore any cached values. - * @returns Promise which resolves to the capabilities of the homeserver - * @returns Rejects: with an error response. - */ - getCapabilities(fresh = false) { - const now = new Date().getTime(); - if (this.cachedCapabilities && !fresh) { - if (now < this.cachedCapabilities.expiration) { - logger_1.logger.log("Returning cached capabilities"); - return Promise.resolve(this.cachedCapabilities.capabilities); - } - } - return this.http - .authedRequest(http_api_1.Method.Get, "/capabilities") - .catch((e) => { - // We swallow errors because we need a default object anyhow - logger_1.logger.error(e); - return {}; - }) - .then((r = {}) => { - const capabilities = r["capabilities"] || {}; - // If the capabilities missed the cache, cache it for a shorter amount - // of time to try and refresh them later. - const cacheMs = Object.keys(capabilities).length ? CAPABILITIES_CACHE_MS : 60000 + Math.random() * 5000; - this.cachedCapabilities = { - capabilities, - expiration: now + cacheMs, - }; - logger_1.logger.log("Caching capabilities: ", capabilities); - return capabilities; - }); - } - /** - * Initialise support for end-to-end encryption in this client, using libolm. - * - * You should call this method after creating the matrixclient, but *before* - * calling `startClient`, if you want to support end-to-end encryption. - * - * It will return a Promise which will resolve when the crypto layer has been - * successfully initialised. - */ - initCrypto() { - return __awaiter(this, void 0, void 0, function* () { - if (!(0, crypto_1.isCryptoAvailable)()) { - throw new Error(`End-to-end encryption not supported in this js-sdk build: did ` + - `you remember to load the olm library?`); - } - if (this.cryptoBackend) { - logger_1.logger.warn("Attempt to re-initialise e2e encryption on MatrixClient"); - return; - } - if (!this.cryptoStore) { - // the cryptostore is provided by sdk.createClient, so this shouldn't happen - throw new Error(`Cannot enable encryption: no cryptoStore provided`); - } - logger_1.logger.log("Crypto: Starting up crypto store..."); - yield this.cryptoStore.startup(); - // initialise the list of encrypted rooms (whether or not crypto is enabled) - logger_1.logger.log("Crypto: initialising roomlist..."); - yield this.roomList.init(); - const userId = this.getUserId(); - if (userId === null) { - throw new Error(`Cannot enable encryption on MatrixClient with unknown userId: ` + - `ensure userId is passed in createClient().`); - } - if (this.deviceId === null) { - throw new Error(`Cannot enable encryption on MatrixClient with unknown deviceId: ` + - `ensure deviceId is passed in createClient().`); - } - const crypto = new crypto_1.Crypto(this, userId, this.deviceId, this.store, this.cryptoStore, this.roomList, this.verificationMethods); - this.reEmitter.reEmit(crypto, [ - crypto_1.CryptoEvent.KeyBackupFailed, - crypto_1.CryptoEvent.KeyBackupSessionsRemaining, - crypto_1.CryptoEvent.RoomKeyRequest, - crypto_1.CryptoEvent.RoomKeyRequestCancellation, - crypto_1.CryptoEvent.Warning, - crypto_1.CryptoEvent.DevicesUpdated, - crypto_1.CryptoEvent.WillUpdateDevices, - crypto_1.CryptoEvent.DeviceVerificationChanged, - crypto_1.CryptoEvent.UserTrustStatusChanged, - crypto_1.CryptoEvent.KeysChanged, - ]); - logger_1.logger.log("Crypto: initialising crypto object..."); - yield crypto.init({ - exportedOlmDevice: this.exportedOlmDeviceToImport, - pickleKey: this.pickleKey, - }); - delete this.exportedOlmDeviceToImport; - this.olmVersion = crypto_1.Crypto.getOlmVersion(); - // if crypto initialisation was successful, tell it to attach its event handlers. - crypto.registerEventHandlers(this); - this.cryptoBackend = this.crypto = crypto; - // upload our keys in the background - this.crypto.uploadDeviceKeys().catch((e) => { - // TODO: throwing away this error is a really bad idea. - logger_1.logger.error("Error uploading device keys", e); - }); - }); - } - /** - * Initialise support for end-to-end encryption in this client, using the rust matrix-sdk-crypto. - * - * An alternative to {@link initCrypto}. - * - * *WARNING*: this API is very experimental, should not be used in production, and may change without notice! - * Eventually it will be deprecated and `initCrypto` will do the same thing. - * - * @experimental - * - * @returns a Promise which will resolve when the crypto layer has been - * successfully initialised. - */ - initRustCrypto() { - return __awaiter(this, void 0, void 0, function* () { - if (this.cryptoBackend) { - logger_1.logger.warn("Attempt to re-initialise e2e encryption on MatrixClient"); - return; - } - const userId = this.getUserId(); - if (userId === null) { - throw new Error(`Cannot enable encryption on MatrixClient with unknown userId: ` + - `ensure userId is passed in createClient().`); - } - const deviceId = this.getDeviceId(); - if (deviceId === null) { - throw new Error(`Cannot enable encryption on MatrixClient with unknown deviceId: ` + - `ensure deviceId is passed in createClient().`); - } - // importing rust-crypto will download the webassembly, so we delay it until we know it will be - // needed. - const RustCrypto = yield Promise.resolve().then(() => __importStar(require("./rust-crypto"))); - const rustCrypto = yield RustCrypto.initRustCrypto(this.http, userId, deviceId); - this.cryptoBackend = rustCrypto; - // attach the event listeners needed by RustCrypto - this.on(room_member_1.RoomMemberEvent.Membership, rustCrypto.onRoomMembership.bind(rustCrypto)); - }); - } - /** - * Access the crypto API for this client. - * - * If end-to-end encryption has been enabled for this client (via {@link initCrypto} or {@link initRustCrypto}), - * returns an object giving access to the crypto API. Otherwise, returns `undefined`. - */ - getCrypto() { - return this.cryptoBackend; - } - /** - * Is end-to-end crypto enabled for this client. - * @returns True if end-to-end is enabled. - * @deprecated prefer {@link getCrypto} - */ - isCryptoEnabled() { - return !!this.cryptoBackend; - } - /** - * Get the Ed25519 key for this device - * - * @returns base64-encoded ed25519 key. Null if crypto is - * disabled. - */ - getDeviceEd25519Key() { - var _a, _b; - return (_b = (_a = this.crypto) === null || _a === void 0 ? void 0 : _a.getDeviceEd25519Key()) !== null && _b !== void 0 ? _b : null; - } - /** - * Get the Curve25519 key for this device - * - * @returns base64-encoded curve25519 key. Null if crypto is - * disabled. - */ - getDeviceCurve25519Key() { - var _a, _b; - return (_b = (_a = this.crypto) === null || _a === void 0 ? void 0 : _a.getDeviceCurve25519Key()) !== null && _b !== void 0 ? _b : null; - } - /** - * @deprecated Does nothing. - */ - uploadKeys() { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.warn("MatrixClient.uploadKeys is deprecated"); - }); - } - /** - * Download the keys for a list of users and stores the keys in the session - * store. - * @param userIds - The users to fetch. - * @param forceDownload - Always download the keys even if cached. - * - * @returns A promise which resolves to a map userId-\>deviceId-\>{@link DeviceInfo} - */ - downloadKeys(userIds, forceDownload) { - if (!this.crypto) { - return Promise.reject(new Error("End-to-end encryption disabled")); - } - return this.crypto.downloadKeys(userIds, forceDownload); - } - /** - * Get the stored device keys for a user id - * - * @param userId - the user to list keys for. - * - * @returns list of devices - */ - getStoredDevicesForUser(userId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getStoredDevicesForUser(userId) || []; - } - /** - * Get the stored device key for a user id and device id - * - * @param userId - the user to list keys for. - * @param deviceId - unique identifier for the device - * - * @returns device or null - */ - getStoredDevice(userId, deviceId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getStoredDevice(userId, deviceId) || null; - } - /** - * Mark the given device as verified - * - * @param userId - owner of the device - * @param deviceId - unique identifier for the device or user's - * cross-signing public key ID. - * - * @param verified - whether to mark the device as verified. defaults - * to 'true'. - * - * @returns - * - * @remarks - * Fires {@link CryptoEvent#DeviceVerificationChanged} - */ - setDeviceVerified(userId, deviceId, verified = true) { - const prom = this.setDeviceVerification(userId, deviceId, verified, null, null); - // if one of the user's own devices is being marked as verified / unverified, - // check the key backup status, since whether or not we use this depends on - // whether it has a signature from a verified device - if (userId == this.credentials.userId) { - this.checkKeyBackup(); - } - return prom; - } - /** - * Mark the given device as blocked/unblocked - * - * @param userId - owner of the device - * @param deviceId - unique identifier for the device or user's - * cross-signing public key ID. - * - * @param blocked - whether to mark the device as blocked. defaults - * to 'true'. - * - * @returns - * - * @remarks - * Fires {@link CryptoEvent.DeviceVerificationChanged} - */ - setDeviceBlocked(userId, deviceId, blocked = true) { - return this.setDeviceVerification(userId, deviceId, null, blocked, null); - } - /** - * Mark the given device as known/unknown - * - * @param userId - owner of the device - * @param deviceId - unique identifier for the device or user's - * cross-signing public key ID. - * - * @param known - whether to mark the device as known. defaults - * to 'true'. - * - * @returns - * - * @remarks - * Fires {@link CryptoEvent#DeviceVerificationChanged} - */ - setDeviceKnown(userId, deviceId, known = true) { - return this.setDeviceVerification(userId, deviceId, null, null, known); - } - setDeviceVerification(userId, deviceId, verified, blocked, known) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - yield this.crypto.setDeviceVerification(userId, deviceId, verified, blocked, known); - }); - } - /** - * Request a key verification from another user, using a DM. - * - * @param userId - the user to request verification with - * @param roomId - the room to use for verification - * - * @returns resolves to a VerificationRequest - * when the request has been sent to the other party. - */ - requestVerificationDM(userId, roomId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.requestVerificationDM(userId, roomId); - } - /** - * Finds a DM verification request that is already in progress for the given room id - * - * @param roomId - the room to use for verification - * - * @returns the VerificationRequest that is in progress, if any - */ - findVerificationRequestDMInProgress(roomId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.findVerificationRequestDMInProgress(roomId); - } - /** - * Returns all to-device verification requests that are already in progress for the given user id - * - * @param userId - the ID of the user to query - * - * @returns the VerificationRequests that are in progress - */ - getVerificationRequestsToDeviceInProgress(userId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getVerificationRequestsToDeviceInProgress(userId); - } - /** - * Request a key verification from another user. - * - * @param userId - the user to request verification with - * @param devices - array of device IDs to send requests to. Defaults to - * all devices owned by the user - * - * @returns resolves to a VerificationRequest - * when the request has been sent to the other party. - */ - requestVerification(userId, devices) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.requestVerification(userId, devices); - } - /** - * Begin a key verification. - * - * @param method - the verification method to use - * @param userId - the user to verify keys with - * @param deviceId - the device to verify - * - * @returns a verification object - * @deprecated Use `requestVerification` instead. - */ - beginKeyVerification(method, userId, deviceId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.beginKeyVerification(method, userId, deviceId); - } - checkSecretStorageKey(key, info) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.checkSecretStorageKey(key, info); - } - /** - * Set the global override for whether the client should ever send encrypted - * messages to unverified devices. This provides the default for rooms which - * do not specify a value. - * - * @param value - whether to blacklist all unverified devices by default - * - * @deprecated Prefer direct access to {@link CryptoApi.globalBlacklistUnverifiedDevices}: - * - * ```javascript - * client.getCrypto().globalBlacklistUnverifiedDevices = value; - * ``` - */ - setGlobalBlacklistUnverifiedDevices(value) { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - this.cryptoBackend.globalBlacklistUnverifiedDevices = value; - return value; - } - /** - * @returns whether to blacklist all unverified devices by default - * - * @deprecated Prefer direct access to {@link CryptoApi.globalBlacklistUnverifiedDevices}: - * - * ```javascript - * value = client.getCrypto().globalBlacklistUnverifiedDevices; - * ``` - */ - getGlobalBlacklistUnverifiedDevices() { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - return this.cryptoBackend.globalBlacklistUnverifiedDevices; - } - /** - * Set whether sendMessage in a room with unknown and unverified devices - * should throw an error and not send them message. This has 'Global' for - * symmetry with setGlobalBlacklistUnverifiedDevices but there is currently - * no room-level equivalent for this setting. - * - * This API is currently UNSTABLE and may change or be removed without notice. - * - * @param value - whether error on unknown devices - * - * @deprecated Prefer direct access to {@link CryptoApi.globalBlacklistUnverifiedDevices}: - * - * ```ts - * client.getCrypto().globalBlacklistUnverifiedDevices = value; - * ``` - */ - setGlobalErrorOnUnknownDevices(value) { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - this.cryptoBackend.globalErrorOnUnknownDevices = value; - } - /** - * @returns whether to error on unknown devices - * - * This API is currently UNSTABLE and may change or be removed without notice. - */ - getGlobalErrorOnUnknownDevices() { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - return this.cryptoBackend.globalErrorOnUnknownDevices; - } - /** - * Get the user's cross-signing key ID. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param type - The type of key to get the ID of. One of - * "master", "self_signing", or "user_signing". Defaults to "master". - * - * @returns the key ID - */ - getCrossSigningId(type = api_1.CrossSigningKey.Master) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getCrossSigningId(type); - } - /** - * Get the cross signing information for a given user. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param userId - the user ID to get the cross-signing info for. - * - * @returns the cross signing information for the user. - */ - getStoredCrossSigningForUser(userId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getStoredCrossSigningForUser(userId); - } - /** - * Check whether a given user is trusted. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param userId - The ID of the user to check. - */ - checkUserTrust(userId) { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - return this.cryptoBackend.checkUserTrust(userId); - } - /** - * Check whether a given device is trusted. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param userId - The ID of the user whose devices is to be checked. - * @param deviceId - The ID of the device to check - */ - checkDeviceTrust(userId, deviceId) { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - return this.cryptoBackend.checkDeviceTrust(userId, deviceId); - } - /** - * Check whether one of our own devices is cross-signed by our - * user's stored keys, regardless of whether we trust those keys yet. - * - * @param deviceId - The ID of the device to check - * - * @returns true if the device is cross-signed - */ - checkIfOwnDeviceCrossSigned(deviceId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.checkIfOwnDeviceCrossSigned(deviceId); - } - /** - * Check the copy of our cross-signing key that we have in the device list and - * see if we can get the private key. If so, mark it as trusted. - * @param opts - ICheckOwnCrossSigningTrustOpts object - */ - checkOwnCrossSigningTrust(opts) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.checkOwnCrossSigningTrust(opts); - } - /** - * Checks that a given cross-signing private key matches a given public key. - * This can be used by the getCrossSigningKey callback to verify that the - * private key it is about to supply is the one that was requested. - * @param privateKey - The private key - * @param expectedPublicKey - The public key - * @returns true if the key matches, otherwise false - */ - checkCrossSigningPrivateKey(privateKey, expectedPublicKey) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.checkCrossSigningPrivateKey(privateKey, expectedPublicKey); - } - // deprecated: use requestVerification instead - legacyDeviceVerification(userId, deviceId, method) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.legacyDeviceVerification(userId, deviceId, method); - } - /** - * Perform any background tasks that can be done before a message is ready to - * send, in order to speed up sending of the message. - * @param room - the room the event is in - * - * @deprecated Prefer {@link CryptoApi.prepareToEncrypt | `CryptoApi.prepareToEncrypt`}: - * - * ```javascript - * client.getCrypto().prepareToEncrypt(room); - * ``` - */ - prepareToEncrypt(room) { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - this.cryptoBackend.prepareToEncrypt(room); - } - /** - * Checks if the user has previously published cross-signing keys - * - * This means downloading the devicelist for the user and checking if the list includes - * the cross-signing pseudo-device. - * - * @deprecated Prefer {@link CryptoApi.userHasCrossSigningKeys | `CryptoApi.userHasCrossSigningKeys`}: - * - * ```javascript - * result = client.getCrypto().userHasCrossSigningKeys(); - * ``` - */ - userHasCrossSigningKeys() { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - return this.cryptoBackend.userHasCrossSigningKeys(); - } - /** - * Checks whether cross signing: - * - is enabled on this account and trusted by this device - * - has private keys either cached locally or stored in secret storage - * - * If this function returns false, bootstrapCrossSigning() can be used - * to fix things such that it returns true. That is to say, after - * bootstrapCrossSigning() completes successfully, this function should - * return true. - * @returns True if cross-signing is ready to be used on this device - */ - isCrossSigningReady() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.isCrossSigningReady(); - } - /** - * Bootstrap cross-signing by creating keys if needed. If everything is already - * set up, then no changes are made, so this is safe to run to ensure - * cross-signing is ready for use. - * - * This function: - * - creates new cross-signing keys if they are not found locally cached nor in - * secret storage (if it has been setup) - * - * The cross-signing API is currently UNSTABLE and may change without notice. - */ - bootstrapCrossSigning(opts) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.bootstrapCrossSigning(opts); - } - /** - * Whether to trust a others users signatures of their devices. - * If false, devices will only be considered 'verified' if we have - * verified that device individually (effectively disabling cross-signing). - * - * Default: true - * - * @returns True if trusting cross-signed devices - */ - getCryptoTrustCrossSignedDevices() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getCryptoTrustCrossSignedDevices(); - } - /** - * See getCryptoTrustCrossSignedDevices - * - * @param val - True to trust cross-signed devices - */ - setCryptoTrustCrossSignedDevices(val) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - this.crypto.setCryptoTrustCrossSignedDevices(val); - } - /** - * Counts the number of end to end session keys that are waiting to be backed up - * @returns Promise which resolves to the number of sessions requiring backup - */ - countSessionsNeedingBackup() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.countSessionsNeedingBackup(); - } - /** - * Get information about the encryption of an event - * - * @param event - event to be checked - * @returns The event information. - */ - getEventEncryptionInfo(event) { - if (!this.cryptoBackend) { - throw new Error("End-to-end encryption disabled"); - } - return this.cryptoBackend.getEventEncryptionInfo(event); - } - /** - * Create a recovery key from a user-supplied passphrase. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param password - Passphrase string that can be entered by the user - * when restoring the backup as an alternative to entering the recovery key. - * Optional. - * @returns Object with public key metadata, encoded private - * recovery key which should be disposed of after displaying to the user, - * and raw private key to avoid round tripping if needed. - */ - createRecoveryKeyFromPassphrase(password) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.createRecoveryKeyFromPassphrase(password); - } - /** - * Checks whether secret storage: - * - is enabled on this account - * - is storing cross-signing private keys - * - is storing session backup key (if enabled) - * - * If this function returns false, bootstrapSecretStorage() can be used - * to fix things such that it returns true. That is to say, after - * bootstrapSecretStorage() completes successfully, this function should - * return true. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @returns True if secret storage is ready to be used on this device - */ - isSecretStorageReady() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.isSecretStorageReady(); - } - /** - * Bootstrap Secure Secret Storage if needed by creating a default key. If everything is - * already set up, then no changes are made, so this is safe to run to ensure secret - * storage is ready for use. - * - * This function - * - creates a new Secure Secret Storage key if no default key exists - * - if a key backup exists, it is migrated to store the key in the Secret - * Storage - * - creates a backup if none exists, and one is requested - * - migrates Secure Secret Storage to use the latest algorithm, if an outdated - * algorithm is found - * - */ - bootstrapSecretStorage(opts) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.bootstrapSecretStorage(opts); - } - /** - * Add a key for encrypting secrets. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param algorithm - the algorithm used by the key - * @param opts - the options for the algorithm. The properties used - * depend on the algorithm given. - * @param keyName - the name of the key. If not given, a random name will be generated. - * - * @returns An object with: - * keyId: the ID of the key - * keyInfo: details about the key (iv, mac, passphrase) - */ - addSecretStorageKey(algorithm, opts, keyName) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.addSecretStorageKey(algorithm, opts, keyName); - } - /** - * Check whether we have a key with a given ID. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param keyId - The ID of the key to check - * for. Defaults to the default key ID if not provided. - * @returns Whether we have the key. - */ - hasSecretStorageKey(keyId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.hasSecretStorageKey(keyId); - } - /** - * Store an encrypted secret on the server. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param name - The name of the secret - * @param secret - The secret contents. - * @param keys - The IDs of the keys to use to encrypt the secret or null/undefined - * to use the default (will throw if no default key is set). - */ - storeSecret(name, secret, keys) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.storeSecret(name, secret, keys); - } - /** - * Get a secret from storage. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param name - the name of the secret - * - * @returns the contents of the secret - */ - getSecret(name) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getSecret(name); - } - /** - * Check if a secret is stored on the server. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param name - the name of the secret - * @returns map of key name to key info the secret is encrypted - * with, or null if it is not present or not encrypted with a trusted - * key - */ - isSecretStored(name) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.isSecretStored(name); - } - /** - * Request a secret from another device. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param name - the name of the secret to request - * @param devices - the devices to request the secret from - * - * @returns the secret request object - */ - requestSecret(name, devices) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.requestSecret(name, devices); - } - /** - * Get the current default key ID for encrypting secrets. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @returns The default key ID or null if no default key ID is set - */ - getDefaultSecretStorageKeyId() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.getDefaultSecretStorageKeyId(); - } - /** - * Set the current default key ID for encrypting secrets. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param keyId - The new default key ID - */ - setDefaultSecretStorageKeyId(keyId) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.setDefaultSecretStorageKeyId(keyId); - } - /** - * Checks that a given secret storage private key matches a given public key. - * This can be used by the getSecretStorageKey callback to verify that the - * private key it is about to supply is the one that was requested. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param privateKey - The private key - * @param expectedPublicKey - The public key - * @returns true if the key matches, otherwise false - */ - checkSecretStoragePrivateKey(privateKey, expectedPublicKey) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.checkSecretStoragePrivateKey(privateKey, expectedPublicKey); - } - /** - * Get e2e information on the device that sent an event - * - * @param event - event to be checked - */ - getEventSenderDeviceInfo(event) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - return null; - } - return this.crypto.getEventSenderDeviceInfo(event); - }); - } - /** - * Check if the sender of an event is verified - * - * @param event - event to be checked - * - * @returns true if the sender of this event has been verified using - * {@link MatrixClient#setDeviceVerified}. - */ - isEventSenderVerified(event) { - return __awaiter(this, void 0, void 0, function* () { - const device = yield this.getEventSenderDeviceInfo(event); - if (!device) { - return false; - } - return device.isVerified(); - }); - } - /** - * Get outgoing room key request for this event if there is one. - * @param event - The event to check for - * - * @returns A room key request, or null if there is none - */ - getOutgoingRoomKeyRequest(event) { - if (!this.crypto) { - throw new Error("End-to-End encryption disabled"); - } - const wireContent = event.getWireContent(); - const requestBody = { - session_id: wireContent.session_id, - sender_key: wireContent.sender_key, - algorithm: wireContent.algorithm, - room_id: event.getRoomId(), - }; - if (!requestBody.session_id || !requestBody.sender_key || !requestBody.algorithm || !requestBody.room_id) { - return Promise.resolve(null); - } - return this.crypto.cryptoStore.getOutgoingRoomKeyRequest(requestBody); - } - /** - * Cancel a room key request for this event if one is ongoing and resend the - * request. - * @param event - event of which to cancel and resend the room - * key request. - * @returns A promise that will resolve when the key request is queued - */ - cancelAndResendEventRoomKeyRequest(event) { - if (!this.crypto) { - throw new Error("End-to-End encryption disabled"); - } - return event.cancelAndResendKeyRequest(this.crypto, this.getUserId()); - } - /** - * Enable end-to-end encryption for a room. This does not modify room state. - * Any messages sent before the returned promise resolves will be sent unencrypted. - * @param roomId - The room ID to enable encryption in. - * @param config - The encryption config for the room. - * @returns A promise that will resolve when encryption is set up. - */ - setRoomEncryption(roomId, config) { - if (!this.crypto) { - throw new Error("End-to-End encryption disabled"); - } - return this.crypto.setRoomEncryption(roomId, config); - } - /** - * Whether encryption is enabled for a room. - * @param roomId - the room id to query. - * @returns whether encryption is enabled. - */ - isRoomEncrypted(roomId) { - const room = this.getRoom(roomId); - if (!room) { - // we don't know about this room, so can't determine if it should be - // encrypted. Let's assume not. - return false; - } - // if there is an 'm.room.encryption' event in this room, it should be - // encrypted (independently of whether we actually support encryption) - const ev = room.currentState.getStateEvents(event_2.EventType.RoomEncryption, ""); - if (ev) { - return true; - } - // we don't have an m.room.encrypted event, but that might be because - // the server is hiding it from us. Check the store to see if it was - // previously encrypted. - return this.roomList.isRoomEncrypted(roomId); - } - /** - * Encrypts and sends a given object via Olm to-device messages to a given - * set of devices. - * - * @param userDeviceMap - mapping from userId to deviceInfo - * - * @param payload - fields to include in the encrypted payload - * - * @returns Promise which - * resolves once the message has been encrypted and sent to the given - * userDeviceMap, and returns the `{ contentMap, deviceInfoByDeviceId }` - * of the successfully sent messages. - */ - encryptAndSendToDevices(userDeviceInfoArr, payload) { - if (!this.crypto) { - throw new Error("End-to-End encryption disabled"); - } - return this.crypto.encryptAndSendToDevices(userDeviceInfoArr, payload); - } - /** - * Forces the current outbound group session to be discarded such - * that another one will be created next time an event is sent. - * - * @param roomId - The ID of the room to discard the session for - * - * @deprecated Prefer {@link CryptoApi.forceDiscardSession | `CryptoApi.forceDiscardSession`}: - * - */ - forceDiscardSession(roomId) { - if (!this.cryptoBackend) { - throw new Error("End-to-End encryption disabled"); - } - this.cryptoBackend.forceDiscardSession(roomId); - } - /** - * Get a list containing all of the room keys - * - * This should be encrypted before returning it to the user. - * - * @returns a promise which resolves to a list of session export objects - * - * @deprecated Prefer {@link CryptoApi.exportRoomKeys | `CryptoApi.exportRoomKeys`}: - * - * ```javascript - * sessionData = await client.getCrypto().exportRoomKeys(); - * ``` - */ - exportRoomKeys() { - if (!this.cryptoBackend) { - return Promise.reject(new Error("End-to-end encryption disabled")); - } - return this.cryptoBackend.exportRoomKeys(); - } - /** - * Import a list of room keys previously exported by exportRoomKeys - * - * @param keys - a list of session export objects - * - * @returns a promise which resolves when the keys have been imported - */ - importRoomKeys(keys, opts) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.importRoomKeys(keys, opts); - } - /** - * Force a re-check of the local key backup status against - * what's on the server. - * - * @returns Object with backup info (as returned by - * getKeyBackupVersion) in backupInfo and - * trust information (as returned by isKeyBackupTrusted) - * in trustInfo. - */ - checkKeyBackup() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.backupManager.checkKeyBackup(); - } - /** - * Get information about the current key backup. - * @returns Information object from API or null - */ - getKeyBackupVersion() { - return __awaiter(this, void 0, void 0, function* () { - let res; - try { - res = yield this.http.authedRequest(http_api_1.Method.Get, "/room_keys/version", undefined, undefined, { prefix: http_api_1.ClientPrefix.V3 }); - } - catch (e) { - if (e.errcode === "M_NOT_FOUND") { - return null; - } - else { - throw e; - } - } - backup_1.BackupManager.checkBackupVersion(res); - return res; - }); - } - /** - * @param info - key backup info dict from getKeyBackupVersion() - */ - isKeyBackupTrusted(info) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.backupManager.isKeyBackupTrusted(info); - } - /** - * @returns true if the client is configured to back up keys to - * the server, otherwise false. If we haven't completed a successful check - * of key backup status yet, returns null. - */ - getKeyBackupEnabled() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.backupManager.getKeyBackupEnabled(); - } - /** - * Enable backing up of keys, using data previously returned from - * getKeyBackupVersion. - * - * @param info - Backup information object as returned by getKeyBackupVersion - * @returns Promise which resolves when complete. - */ - enableKeyBackup(info) { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.backupManager.enableKeyBackup(info); - } - /** - * Disable backing up of keys. - */ - disableKeyBackup() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - this.crypto.backupManager.disableKeyBackup(); - } - /** - * Set up the data required to create a new backup version. The backup version - * will not be created and enabled until createKeyBackupVersion is called. - * - * @param password - Passphrase string that can be entered by the user - * when restoring the backup as an alternative to entering the recovery key. - * Optional. - * - * @returns Object that can be passed to createKeyBackupVersion and - * additionally has a 'recovery_key' member with the user-facing recovery key string. - */ - prepareKeyBackupVersion(password, opts = { secureSecretStorage: false }) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - // eslint-disable-next-line camelcase - const { algorithm, auth_data, recovery_key, privateKey } = yield this.crypto.backupManager.prepareKeyBackupVersion(password); - if (opts.secureSecretStorage) { - yield this.storeSecret("m.megolm_backup.v1", (0, olmlib_1.encodeBase64)(privateKey)); - logger_1.logger.info("Key backup private key stored in secret storage"); - } - return { - algorithm, - /* eslint-disable camelcase */ - auth_data, - recovery_key, - /* eslint-enable camelcase */ - }; - }); - } - /** - * Check whether the key backup private key is stored in secret storage. - * @returns map of key name to key info the secret is - * encrypted with, or null if it is not present or not encrypted with a - * trusted key - */ - isKeyBackupKeyStored() { - return Promise.resolve(this.isSecretStored("m.megolm_backup.v1")); - } - /** - * Create a new key backup version and enable it, using the information return - * from prepareKeyBackupVersion. - * - * @param info - Info object from prepareKeyBackupVersion - * @returns Object with 'version' param indicating the version created - */ - createKeyBackupVersion(info) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - yield this.crypto.backupManager.createKeyBackupVersion(info); - const data = { - algorithm: info.algorithm, - auth_data: info.auth_data, - }; - // Sign the backup auth data with the device key for backwards compat with - // older devices with cross-signing. This can probably go away very soon in - // favour of just signing with the cross-singing master key. - // XXX: Private member access - yield this.crypto.signObject(data.auth_data); - if (this.cryptoCallbacks.getCrossSigningKey && - // XXX: Private member access - this.crypto.crossSigningInfo.getId()) { - // now also sign the auth data with the cross-signing master key - // we check for the callback explicitly here because we still want to be able - // to create an un-cross-signed key backup if there is a cross-signing key but - // no callback supplied. - // XXX: Private member access - yield this.crypto.crossSigningInfo.signObject(data.auth_data, "master"); - } - const res = yield this.http.authedRequest(http_api_1.Method.Post, "/room_keys/version", undefined, data, { - prefix: http_api_1.ClientPrefix.V3, - }); - // We could assume everything's okay and enable directly, but this ensures - // we run the same signature verification that will be used for future - // sessions. - yield this.checkKeyBackup(); - if (!this.getKeyBackupEnabled()) { - logger_1.logger.error("Key backup not usable even though we just created it"); - } - return res; - }); - } - deleteKeyBackupVersion(version) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - // If we're currently backing up to this backup... stop. - // (We start using it automatically in createKeyBackupVersion - // so this is symmetrical). - if (this.crypto.backupManager.version) { - this.crypto.backupManager.disableKeyBackup(); - } - const path = utils.encodeUri("/room_keys/version/$version", { - $version: version, - }); - yield this.http.authedRequest(http_api_1.Method.Delete, path, undefined, undefined, { prefix: http_api_1.ClientPrefix.V3 }); - }); - } - makeKeyBackupPath(roomId, sessionId, version) { - let path; - if (sessionId !== undefined) { - path = utils.encodeUri("/room_keys/keys/$roomId/$sessionId", { - $roomId: roomId, - $sessionId: sessionId, - }); - } - else if (roomId !== undefined) { - path = utils.encodeUri("/room_keys/keys/$roomId", { - $roomId: roomId, - }); - } - else { - path = "/room_keys/keys"; - } - const queryData = version === undefined ? undefined : { version }; - return { path, queryData }; - } - sendKeyBackup(roomId, sessionId, version, data) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - const path = this.makeKeyBackupPath(roomId, sessionId, version); - yield this.http.authedRequest(http_api_1.Method.Put, path.path, path.queryData, data, { prefix: http_api_1.ClientPrefix.V3 }); - }); - } - /** - * Marks all group sessions as needing to be backed up and schedules them to - * upload in the background as soon as possible. - */ - scheduleAllGroupSessionsForBackup() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - yield this.crypto.backupManager.scheduleAllGroupSessionsForBackup(); - }); - } - /** - * Marks all group sessions as needing to be backed up without scheduling - * them to upload in the background. - * @returns Promise which resolves to the number of sessions requiring a backup. - */ - flagAllGroupSessionsForBackup() { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - return this.crypto.backupManager.flagAllGroupSessionsForBackup(); - } - isValidRecoveryKey(recoveryKey) { - try { - (0, recoverykey_1.decodeRecoveryKey)(recoveryKey); - return true; - } - catch (e) { - return false; - } - } - /** - * Get the raw key for a key backup from the password - * Used when migrating key backups into SSSS - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param password - Passphrase - * @param backupInfo - Backup metadata from `checkKeyBackup` - * @returns key backup key - */ - keyBackupKeyFromPassword(password, backupInfo) { - return (0, key_passphrase_1.keyFromAuthData)(backupInfo.auth_data, password); - } - /** - * Get the raw key for a key backup from the recovery key - * Used when migrating key backups into SSSS - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param recoveryKey - The recovery key - * @returns key backup key - */ - keyBackupKeyFromRecoveryKey(recoveryKey) { - return (0, recoverykey_1.decodeRecoveryKey)(recoveryKey); - } - restoreKeyBackupWithPassword(password, targetRoomId, targetSessionId, backupInfo, opts) { - return __awaiter(this, void 0, void 0, function* () { - const privKey = yield (0, key_passphrase_1.keyFromAuthData)(backupInfo.auth_data, password); - return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts); - }); - } - /** - * Restore from an existing key backup via a private key stored in secret - * storage. - * - * @param backupInfo - Backup metadata from `checkKeyBackup` - * @param targetRoomId - Room ID to target a specific room. - * Restores all rooms if omitted. - * @param targetSessionId - Session ID to target a specific session. - * Restores all sessions if omitted. - * @param opts - Optional params such as callbacks - * @returns Status of restoration with `total` and `imported` - * key counts. - */ - restoreKeyBackupWithSecretStorage(backupInfo, targetRoomId, targetSessionId, opts) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - const storedKey = yield this.getSecret("m.megolm_backup.v1"); - // ensure that the key is in the right format. If not, fix the key and - // store the fixed version - const fixedKey = (0, crypto_1.fixBackupKey)(storedKey); - if (fixedKey) { - const keys = yield this.crypto.getSecretStorageKey(); - yield this.storeSecret("m.megolm_backup.v1", fixedKey, [keys[0]]); - } - const privKey = (0, olmlib_1.decodeBase64)(fixedKey || storedKey); - return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts); - }); - } - restoreKeyBackupWithRecoveryKey(recoveryKey, targetRoomId, targetSessionId, backupInfo, opts) { - const privKey = (0, recoverykey_1.decodeRecoveryKey)(recoveryKey); - return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts); - } - restoreKeyBackupWithCache(targetRoomId, targetSessionId, backupInfo, opts) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - const privKey = yield this.crypto.getSessionBackupPrivateKey(); - if (!privKey) { - throw new Error("Couldn't get key"); - } - return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts); - }); - } - restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts) { - return __awaiter(this, void 0, void 0, function* () { - const cacheCompleteCallback = opts === null || opts === void 0 ? void 0 : opts.cacheCompleteCallback; - const progressCallback = opts === null || opts === void 0 ? void 0 : opts.progressCallback; - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - let totalKeyCount = 0; - let keys = []; - const path = this.makeKeyBackupPath(targetRoomId, targetSessionId, backupInfo.version); - const algorithm = yield backup_1.BackupManager.makeAlgorithm(backupInfo, () => __awaiter(this, void 0, void 0, function* () { - return privKey; - })); - const untrusted = algorithm.untrusted; - try { - // If the pubkey computed from the private data we've been given - // doesn't match the one in the auth_data, the user has entered - // a different recovery key / the wrong passphrase. - if (!(yield algorithm.keyMatches(privKey))) { - return Promise.reject(new http_api_1.MatrixError({ errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY })); - } - // Cache the key, if possible. - // This is async. - this.crypto - .storeSessionBackupPrivateKey(privKey) - .catch((e) => { - logger_1.logger.warn("Error caching session backup key:", e); - }) - .then(cacheCompleteCallback); - if (progressCallback) { - progressCallback({ - stage: "fetch", - }); - } - const res = yield this.http.authedRequest(http_api_1.Method.Get, path.path, path.queryData, undefined, { prefix: http_api_1.ClientPrefix.V3 }); - if (res.rooms) { - const rooms = res.rooms; - for (const [roomId, roomData] of Object.entries(rooms)) { - if (!roomData.sessions) - continue; - totalKeyCount += Object.keys(roomData.sessions).length; - const roomKeys = yield algorithm.decryptSessions(roomData.sessions); - for (const k of roomKeys) { - k.room_id = roomId; - keys.push(k); - } - } - } - else if (res.sessions) { - const sessions = res.sessions; - totalKeyCount = Object.keys(sessions).length; - keys = yield algorithm.decryptSessions(sessions); - for (const k of keys) { - k.room_id = targetRoomId; - } - } - else { - totalKeyCount = 1; - try { - const [key] = yield algorithm.decryptSessions({ - [targetSessionId]: res, - }); - key.room_id = targetRoomId; - key.session_id = targetSessionId; - keys.push(key); - } - catch (e) { - logger_1.logger.log("Failed to decrypt megolm session from backup", e); - } - } - } - finally { - algorithm.free(); - } - yield this.importRoomKeys(keys, { - progressCallback, - untrusted, - source: "backup", - }); - yield this.checkKeyBackup(); - return { total: totalKeyCount, imported: keys.length }; - }); - } - deleteKeysFromBackup(roomId, sessionId, version) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - const path = this.makeKeyBackupPath(roomId, sessionId, version); - yield this.http.authedRequest(http_api_1.Method.Delete, path.path, path.queryData, undefined, { prefix: http_api_1.ClientPrefix.V3 }); - }); - } - /** - * Share shared-history decryption keys with the given users. - * - * @param roomId - the room for which keys should be shared. - * @param userIds - a list of users to share with. The keys will be sent to - * all of the user's current devices. - */ - sendSharedHistoryKeys(roomId, userIds) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - const roomEncryption = this.roomList.getRoomEncryption(roomId); - if (!roomEncryption) { - // unknown room, or unencrypted room - logger_1.logger.error("Unknown room. Not sharing decryption keys"); - return; - } - const deviceInfos = yield this.crypto.downloadKeys(userIds); - const devicesByUser = new Map(); - for (const [userId, devices] of deviceInfos) { - devicesByUser.set(userId, Array.from(devices.values())); - } - // XXX: Private member access - const alg = this.crypto.getRoomDecryptor(roomId, roomEncryption.algorithm); - if (alg.sendSharedHistoryInboundSessions) { - yield alg.sendSharedHistoryInboundSessions(devicesByUser); - } - else { - logger_1.logger.warn("Algorithm does not support sharing previous keys", roomEncryption.algorithm); - } - }); - } - /** - * Get the config for the media repository. - * @returns Promise which resolves with an object containing the config. - */ - getMediaConfig() { - return this.http.authedRequest(http_api_1.Method.Get, "/config", undefined, undefined, { - prefix: http_api_1.MediaPrefix.R0, - }); - } - /** - * Get the room for the given room ID. - * This function will return a valid room for any room for which a Room event - * has been emitted. Note in particular that other events, eg. RoomState.members - * will be emitted for a room before this function will return the given room. - * @param roomId - The room ID - * @returns The Room or null if it doesn't exist or there is no data store. - */ - getRoom(roomId) { - if (!roomId) { - return null; - } - return this.store.getRoom(roomId); - } - /** - * Retrieve all known rooms. - * @returns A list of rooms, or an empty list if there is no data store. - */ - getRooms() { - return this.store.getRooms(); - } - /** - * Retrieve all rooms that should be displayed to the user - * This is essentially getRooms() with some rooms filtered out, eg. old versions - * of rooms that have been replaced or (in future) other rooms that have been - * marked at the protocol level as not to be displayed to the user. - * - * @param msc3946ProcessDynamicPredecessor - if true, look for an - * m.room.predecessor state event and - * use it if found (MSC3946). - * @returns A list of rooms, or an empty list if there is no data store. - */ - getVisibleRooms(msc3946ProcessDynamicPredecessor = false) { - var _a; - const allRooms = this.store.getRooms(); - const replacedRooms = new Set(); - for (const r of allRooms) { - const predecessor = (_a = r.findPredecessor(msc3946ProcessDynamicPredecessor)) === null || _a === void 0 ? void 0 : _a.roomId; - if (predecessor) { - replacedRooms.add(predecessor); - } - } - return allRooms.filter((r) => { - const tombstone = r.currentState.getStateEvents(event_2.EventType.RoomTombstone, ""); - if (tombstone && replacedRooms.has(r.roomId)) { - return false; - } - return true; - }); - } - /** - * Retrieve a user. - * @param userId - The user ID to retrieve. - * @returns A user or null if there is no data store or the user does - * not exist. - */ - getUser(userId) { - return this.store.getUser(userId); - } - /** - * Retrieve all known users. - * @returns A list of users, or an empty list if there is no data store. - */ - getUsers() { - return this.store.getUsers(); - } - /** - * Set account data event for the current user. - * It will retry the request up to 5 times. - * @param eventType - The event type - * @param content - the contents object for the event - * @returns Promise which resolves: an empty object - * @returns Rejects: with an error response. - */ - setAccountData(eventType, content) { - const path = utils.encodeUri("/user/$userId/account_data/$type", { - $userId: this.credentials.userId, - $type: eventType, - }); - return (0, http_api_1.retryNetworkOperation)(5, () => { - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, content); - }); - } - /** - * Get account data event of given type for the current user. - * @param eventType - The event type - * @returns The contents of the given account data event - */ - getAccountData(eventType) { - return this.store.getAccountData(eventType); - } - /** - * Get account data event of given type for the current user. This variant - * gets account data directly from the homeserver if the local store is not - * ready, which can be useful very early in startup before the initial sync. - * @param eventType - The event type - * @returns Promise which resolves: The contents of the given account data event. - * @returns Rejects: with an error response. - */ - getAccountDataFromServer(eventType) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (this.isInitialSyncComplete()) { - const event = this.store.getAccountData(eventType); - if (!event) { - return null; - } - // The network version below returns just the content, so this branch - // does the same to match. - return event.getContent(); - } - const path = utils.encodeUri("/user/$userId/account_data/$type", { - $userId: this.credentials.userId, - $type: eventType, - }); - try { - return yield this.http.authedRequest(http_api_1.Method.Get, path); - } - catch (e) { - if (((_a = e.data) === null || _a === void 0 ? void 0 : _a.errcode) === "M_NOT_FOUND") { - return null; - } - throw e; - } - }); - } - deleteAccountData(eventType) { - return __awaiter(this, void 0, void 0, function* () { - const msc3391DeleteAccountDataServerSupport = this.canSupport.get(feature_1.Feature.AccountDataDeletion); - // if deletion is not supported overwrite with empty content - if (msc3391DeleteAccountDataServerSupport === feature_1.ServerSupport.Unsupported) { - yield this.setAccountData(eventType, {}); - return; - } - const path = utils.encodeUri("/user/$userId/account_data/$type", { - $userId: this.getSafeUserId(), - $type: eventType, - }); - const options = msc3391DeleteAccountDataServerSupport === feature_1.ServerSupport.Unstable - ? { prefix: "/_matrix/client/unstable/org.matrix.msc3391" } - : undefined; - return yield this.http.authedRequest(http_api_1.Method.Delete, path, undefined, undefined, options); - }); - } - /** - * Gets the users that are ignored by this client - * @returns The array of users that are ignored (empty if none) - */ - getIgnoredUsers() { - const event = this.getAccountData("m.ignored_user_list"); - if (!event || !event.getContent() || !event.getContent()["ignored_users"]) - return []; - return Object.keys(event.getContent()["ignored_users"]); - } - /** - * Sets the users that the current user should ignore. - * @param userIds - the user IDs to ignore - * @returns Promise which resolves: an empty object - * @returns Rejects: with an error response. - */ - setIgnoredUsers(userIds) { - const content = { ignored_users: {} }; - userIds.forEach((u) => { - content.ignored_users[u] = {}; - }); - return this.setAccountData("m.ignored_user_list", content); - } - /** - * Gets whether or not a specific user is being ignored by this client. - * @param userId - the user ID to check - * @returns true if the user is ignored, false otherwise - */ - isUserIgnored(userId) { - return this.getIgnoredUsers().includes(userId); - } - /** - * Join a room. If you have already joined the room, this will no-op. - * @param roomIdOrAlias - The room ID or room alias to join. - * @param opts - Options when joining the room. - * @returns Promise which resolves: Room object. - * @returns Rejects: with an error response. - */ - joinRoom(roomIdOrAlias, opts = {}) { - return __awaiter(this, void 0, void 0, function* () { - if (opts.syncRoom === undefined) { - opts.syncRoom = true; - } - const room = this.getRoom(roomIdOrAlias); - if (room === null || room === void 0 ? void 0 : room.hasMembershipState(this.credentials.userId, "join")) { - return Promise.resolve(room); - } - let signPromise = Promise.resolve(); - if (opts.inviteSignUrl) { - const url = new URL(opts.inviteSignUrl); - url.searchParams.set("mxid", this.credentials.userId); - signPromise = this.http.requestOtherUrl(http_api_1.Method.Post, url); - } - const queryString = {}; - if (opts.viaServers) { - queryString["server_name"] = opts.viaServers; - } - try { - const data = {}; - const signedInviteObj = yield signPromise; - if (signedInviteObj) { - data.third_party_signed = signedInviteObj; - } - const path = utils.encodeUri("/join/$roomid", { $roomid: roomIdOrAlias }); - const res = yield this.http.authedRequest(http_api_1.Method.Post, path, queryString, data); - const roomId = res.room_id; - const syncApi = new sync_1.SyncApi(this, this.clientOpts, this.buildSyncApiOptions()); - const room = syncApi.createRoom(roomId); - if (opts.syncRoom) { - // v2 will do this for us - // return syncApi.syncRoom(room); - } - return room; - } - catch (e) { - throw e; // rethrow for reject - } - }); - } - /** - * Resend an event. Will also retry any to-device messages waiting to be sent. - * @param event - The event to resend. - * @param room - Optional. The room the event is in. Will update the - * timeline entry if provided. - * @returns Promise which resolves: to an ISendEventResponse object - * @returns Rejects: with an error response. - */ - resendEvent(event, room) { - // also kick the to-device queue to retry - this.toDeviceMessageQueue.sendQueue(); - this.updatePendingEventStatus(room, event, event_1.EventStatus.SENDING); - return this.encryptAndSendEvent(room, event); - } - /** - * Cancel a queued or unsent event. - * - * @param event - Event to cancel - * @throws Error if the event is not in QUEUED, NOT_SENT or ENCRYPTING state - */ - cancelPendingEvent(event) { - if (![event_1.EventStatus.QUEUED, event_1.EventStatus.NOT_SENT, event_1.EventStatus.ENCRYPTING].includes(event.status)) { - throw new Error("cannot cancel an event with status " + event.status); - } - // if the event is currently being encrypted then - if (event.status === event_1.EventStatus.ENCRYPTING) { - this.pendingEventEncryption.delete(event.getId()); - } - else if (this.scheduler && event.status === event_1.EventStatus.QUEUED) { - // tell the scheduler to forget about it, if it's queued - this.scheduler.removeEventFromQueue(event); - } - // then tell the room about the change of state, which will remove it - // from the room's list of pending events. - const room = this.getRoom(event.getRoomId()); - this.updatePendingEventStatus(room, event, event_1.EventStatus.CANCELLED); - } - /** - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - setRoomName(roomId, name) { - return this.sendStateEvent(roomId, event_2.EventType.RoomName, { name: name }); - } - /** - * @param htmlTopic - Optional. - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - setRoomTopic(roomId, topic, htmlTopic) { - const content = ContentHelpers.makeTopicContent(topic, htmlTopic); - return this.sendStateEvent(roomId, event_2.EventType.RoomTopic, content); - } - /** - * @returns Promise which resolves: to an object keyed by tagId with objects containing a numeric order field. - * @returns Rejects: with an error response. - */ - getRoomTags(roomId) { - const path = utils.encodeUri("/user/$userId/rooms/$roomId/tags", { - $userId: this.credentials.userId, - $roomId: roomId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * @param tagName - name of room tag to be set - * @param metadata - associated with that tag to be stored - * @returns Promise which resolves: to an empty object - * @returns Rejects: with an error response. - */ - setRoomTag(roomId, tagName, metadata) { - const path = utils.encodeUri("/user/$userId/rooms/$roomId/tags/$tag", { - $userId: this.credentials.userId, - $roomId: roomId, - $tag: tagName, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, metadata); - } - /** - * @param tagName - name of room tag to be removed - * @returns Promise which resolves: to an empty object - * @returns Rejects: with an error response. - */ - deleteRoomTag(roomId, tagName) { - const path = utils.encodeUri("/user/$userId/rooms/$roomId/tags/$tag", { - $userId: this.credentials.userId, - $roomId: roomId, - $tag: tagName, - }); - return this.http.authedRequest(http_api_1.Method.Delete, path); - } - /** - * @param eventType - event type to be set - * @param content - event content - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - setRoomAccountData(roomId, eventType, content) { - const path = utils.encodeUri("/user/$userId/rooms/$roomId/account_data/$type", { - $userId: this.credentials.userId, - $roomId: roomId, - $type: eventType, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, content); - } - /** - * Set a power level to one or multiple users. - * @returns Promise which resolves: to an ISendEventResponse object - * @returns Rejects: with an error response. - */ - setPowerLevel(roomId, userId, powerLevel, event) { - let content = { - users: {}, - }; - if ((event === null || event === void 0 ? void 0 : event.getType()) === event_2.EventType.RoomPowerLevels) { - // take a copy of the content to ensure we don't corrupt - // existing client state with a failed power level change - content = utils.deepCopy(event.getContent()); - } - const users = Array.isArray(userId) ? userId : [userId]; - for (const user of users) { - if (powerLevel == null) { - delete content.users[user]; - } - else { - content.users[user] = powerLevel; - } - } - const path = utils.encodeUri("/rooms/$roomId/state/m.room.power_levels", { - $roomId: roomId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, content); - } - /** - * Create an m.beacon_info event - * @returns - */ - // eslint-disable-next-line @typescript-eslint/naming-convention - unstable_createLiveBeacon(roomId, beaconInfoContent) { - return __awaiter(this, void 0, void 0, function* () { - return this.unstable_setLiveBeacon(roomId, beaconInfoContent); - }); - } - /** - * Upsert a live beacon event - * using a specific m.beacon_info.* event variable type - * @param roomId - string - * @returns - */ - // eslint-disable-next-line @typescript-eslint/naming-convention - unstable_setLiveBeacon(roomId, beaconInfoContent) { - return __awaiter(this, void 0, void 0, function* () { - return this.sendStateEvent(roomId, beacon_1.M_BEACON_INFO.name, beaconInfoContent, this.getUserId()); - }); - } - sendEvent(roomId, threadIdOrEventType, eventTypeOrContent, contentOrTxnId, txnIdOrVoid) { - var _a, _b, _c, _d, _e; - let threadId; - let eventType; - let content; - let txnId; - if (!(threadIdOrEventType === null || threadIdOrEventType === void 0 ? void 0 : threadIdOrEventType.startsWith(EVENT_ID_PREFIX)) && threadIdOrEventType !== null) { - txnId = contentOrTxnId; - content = eventTypeOrContent; - eventType = threadIdOrEventType; - threadId = null; - } - else { - txnId = txnIdOrVoid; - content = contentOrTxnId; - eventType = eventTypeOrContent; - threadId = threadIdOrEventType; - } - // If we expect that an event is part of a thread but is missing the relation - // we need to add it manually, as well as the reply fallback - if (threadId && !((_a = content["m.relates_to"]) === null || _a === void 0 ? void 0 : _a.rel_type)) { - const isReply = !!((_b = content["m.relates_to"]) === null || _b === void 0 ? void 0 : _b["m.in_reply_to"]); - content["m.relates_to"] = Object.assign(Object.assign({}, content["m.relates_to"]), { rel_type: thread_1.THREAD_RELATION_TYPE.name, event_id: threadId, - // Set is_falling_back to true unless this is actually intended to be a reply - is_falling_back: !isReply }); - const thread = (_c = this.getRoom(roomId)) === null || _c === void 0 ? void 0 : _c.getThread(threadId); - if (thread && !isReply) { - content["m.relates_to"]["m.in_reply_to"] = { - event_id: (_e = (_d = thread - .lastReply((ev) => { - return ev.isRelation(thread_1.THREAD_RELATION_TYPE.name) && !ev.status; - })) === null || _d === void 0 ? void 0 : _d.getId()) !== null && _e !== void 0 ? _e : threadId, - }; - } - } - return this.sendCompleteEvent(roomId, threadId, { type: eventType, content }, txnId); - } - /** - * @param eventObject - An object with the partial structure of an event, to which event_id, user_id, room_id and origin_server_ts will be added. - * @param txnId - Optional. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - sendCompleteEvent(roomId, threadId, eventObject, txnId) { - if (!txnId) { - txnId = this.makeTxnId(); - } - // We always construct a MatrixEvent when sending because the store and scheduler use them. - // We'll extract the params back out if it turns out the client has no scheduler or store. - const localEvent = new event_1.MatrixEvent(Object.assign(eventObject, { - event_id: "~" + roomId + ":" + txnId, - user_id: this.credentials.userId, - sender: this.credentials.userId, - room_id: roomId, - origin_server_ts: new Date().getTime(), - })); - const room = this.getRoom(roomId); - const thread = threadId ? room === null || room === void 0 ? void 0 : room.getThread(threadId) : undefined; - if (thread) { - localEvent.setThread(thread); - } - // set up re-emitter for this new event - this is normally the job of EventMapper but we don't use it here - this.reEmitter.reEmit(localEvent, [event_1.MatrixEventEvent.Replaced, event_1.MatrixEventEvent.VisibilityChange]); - room === null || room === void 0 ? void 0 : room.reEmitter.reEmit(localEvent, [event_1.MatrixEventEvent.BeforeRedaction]); - // if this is a relation or redaction of an event - // that hasn't been sent yet (e.g. with a local id starting with a ~) - // then listen for the remote echo of that event so that by the time - // this event does get sent, we have the correct event_id - const targetId = localEvent.getAssociatedId(); - if (targetId === null || targetId === void 0 ? void 0 : targetId.startsWith("~")) { - const target = room === null || room === void 0 ? void 0 : room.getPendingEvents().find((e) => e.getId() === targetId); - target === null || target === void 0 ? void 0 : target.once(event_1.MatrixEventEvent.LocalEventIdReplaced, () => { - localEvent.updateAssociatedId(target.getId()); - }); - } - const type = localEvent.getType(); - logger_1.logger.log(`sendEvent of type ${type} in ${roomId} with txnId ${txnId}`); - localEvent.setTxnId(txnId); - localEvent.setStatus(event_1.EventStatus.SENDING); - // add this event immediately to the local store as 'sending'. - room === null || room === void 0 ? void 0 : room.addPendingEvent(localEvent, txnId); - // addPendingEvent can change the state to NOT_SENT if it believes - // that there's other events that have failed. We won't bother to - // try sending the event if the state has changed as such. - if (localEvent.status === event_1.EventStatus.NOT_SENT) { - return Promise.reject(new Error("Event blocked by other events not yet sent")); - } - return this.encryptAndSendEvent(room, localEvent); - } - /** - * encrypts the event if necessary; adds the event to the queue, or sends it; marks the event as sent/unsent - * @returns returns a promise which resolves with the result of the send request - */ - encryptAndSendEvent(room, event) { - let cancelled = false; - // Add an extra Promise.resolve() to turn synchronous exceptions into promise rejections, - // so that we can handle synchronous and asynchronous exceptions with the - // same code path. - return Promise.resolve() - .then(() => { - const encryptionPromise = this.encryptEventIfNeeded(event, room !== null && room !== void 0 ? room : undefined); - if (!encryptionPromise) - return null; // doesn't need encryption - this.pendingEventEncryption.set(event.getId(), encryptionPromise); - this.updatePendingEventStatus(room, event, event_1.EventStatus.ENCRYPTING); - return encryptionPromise.then(() => { - if (!this.pendingEventEncryption.has(event.getId())) { - // cancelled via MatrixClient::cancelPendingEvent - cancelled = true; - return; - } - this.updatePendingEventStatus(room, event, event_1.EventStatus.SENDING); - }); - }) - .then(() => { - if (cancelled) - return {}; - let promise = null; - if (this.scheduler) { - // if this returns a promise then the scheduler has control now and will - // resolve/reject when it is done. Internally, the scheduler will invoke - // processFn which is set to this._sendEventHttpRequest so the same code - // path is executed regardless. - promise = this.scheduler.queueEvent(event); - if (promise && this.scheduler.getQueueForEvent(event).length > 1) { - // event is processed FIFO so if the length is 2 or more we know - // this event is stuck behind an earlier event. - this.updatePendingEventStatus(room, event, event_1.EventStatus.QUEUED); - } - } - if (!promise) { - promise = this.sendEventHttpRequest(event); - if (room) { - promise = promise.then((res) => { - room.updatePendingEvent(event, event_1.EventStatus.SENT, res["event_id"]); - return res; - }); - } - } - return promise; - }) - .catch((err) => { - logger_1.logger.error("Error sending event", err.stack || err); - try { - // set the error on the event before we update the status: - // updating the status emits the event, so the state should be - // consistent at that point. - event.error = err; - this.updatePendingEventStatus(room, event, event_1.EventStatus.NOT_SENT); - } - catch (e) { - logger_1.logger.error("Exception in error handler!", e.stack || err); - } - if (err instanceof http_api_1.MatrixError) { - err.event = event; - } - throw err; - }); - } - encryptEventIfNeeded(event, room) { - if (event.isEncrypted()) { - // this event has already been encrypted; this happens if the - // encryption step succeeded, but the send step failed on the first - // attempt. - return null; - } - if (event.isRedaction()) { - // Redactions do not support encryption in the spec at this time, - // whilst it mostly worked in some clients, it wasn't compliant. - return null; - } - if (!room || !this.isRoomEncrypted(event.getRoomId())) { - return null; - } - if (!this.cryptoBackend && this.usingExternalCrypto) { - // The client has opted to allow sending messages to encrypted - // rooms even if the room is encrypted, and we haven't setup - // crypto. This is useful for users of matrix-org/pantalaimon - return null; - } - if (event.getType() === event_2.EventType.Reaction) { - // For reactions, there is a very little gained by encrypting the entire - // event, as relation data is already kept in the clear. Event - // encryption for a reaction effectively only obscures the event type, - // but the purpose is still obvious from the relation data, so nothing - // is really gained. It also causes quite a few problems, such as: - // * triggers notifications via default push rules - // * prevents server-side bundling for reactions - // The reaction key / content / emoji value does warrant encrypting, but - // this will be handled separately by encrypting just this value. - // See https://github.com/matrix-org/matrix-doc/pull/1849#pullrequestreview-248763642 - return null; - } - if (!this.cryptoBackend) { - throw new Error("This room is configured to use encryption, but your client does not support encryption."); - } - return this.cryptoBackend.encryptEvent(event, room); - } - /** - * Returns the eventType that should be used taking encryption into account - * for a given eventType. - * @param roomId - the room for the events `eventType` relates to - * @param eventType - the event type - * @returns the event type taking encryption into account - */ - getEncryptedIfNeededEventType(roomId, eventType) { - if (eventType === event_2.EventType.Reaction) - return eventType; - return this.isRoomEncrypted(roomId) ? event_2.EventType.RoomMessageEncrypted : eventType; - } - updatePendingEventStatus(room, event, newStatus) { - if (room) { - room.updatePendingEvent(event, newStatus); - } - else { - event.setStatus(newStatus); - } - } - sendEventHttpRequest(event) { - let txnId = event.getTxnId(); - if (!txnId) { - txnId = this.makeTxnId(); - event.setTxnId(txnId); - } - const pathParams = { - $roomId: event.getRoomId(), - $eventType: event.getWireType(), - $stateKey: event.getStateKey(), - $txnId: txnId, - }; - let path; - if (event.isState()) { - let pathTemplate = "/rooms/$roomId/state/$eventType"; - if (event.getStateKey() && event.getStateKey().length > 0) { - pathTemplate = "/rooms/$roomId/state/$eventType/$stateKey"; - } - path = utils.encodeUri(pathTemplate, pathParams); - } - else if (event.isRedaction()) { - const pathTemplate = `/rooms/$roomId/redact/$redactsEventId/$txnId`; - path = utils.encodeUri(pathTemplate, Object.assign({ $redactsEventId: event.event.redacts }, pathParams)); - } - else { - path = utils.encodeUri("/rooms/$roomId/send/$eventType/$txnId", pathParams); - } - return this.http - .authedRequest(http_api_1.Method.Put, path, undefined, event.getWireContent()) - .then((res) => { - logger_1.logger.log(`Event sent to ${event.getRoomId()} with event id ${res.event_id}`); - return res; - }); - } - redactEvent(roomId, threadId, eventId, txnId, opts) { - if (!(eventId === null || eventId === void 0 ? void 0 : eventId.startsWith(EVENT_ID_PREFIX))) { - opts = txnId; - txnId = eventId; - eventId = threadId; - threadId = null; - } - const reason = opts === null || opts === void 0 ? void 0 : opts.reason; - if ((opts === null || opts === void 0 ? void 0 : opts.with_relations) && - this.canSupport.get(feature_1.Feature.RelationBasedRedactions) === feature_1.ServerSupport.Unsupported) { - throw new Error("Server does not support relation based redactions " + - `roomId ${roomId} eventId ${eventId} txnId: ${txnId} threadId ${threadId}`); - } - const withRelations = (opts === null || opts === void 0 ? void 0 : opts.with_relations) - ? { - [this.canSupport.get(feature_1.Feature.RelationBasedRedactions) === feature_1.ServerSupport.Stable - ? event_2.MSC3912_RELATION_BASED_REDACTIONS_PROP.stable - : event_2.MSC3912_RELATION_BASED_REDACTIONS_PROP.unstable]: opts === null || opts === void 0 ? void 0 : opts.with_relations, - } - : {}; - return this.sendCompleteEvent(roomId, threadId, { - type: event_2.EventType.RoomRedaction, - content: Object.assign(Object.assign({}, withRelations), { reason }), - redacts: eventId, - }, txnId); - } - sendMessage(roomId, threadId, content, txnId) { - if (typeof threadId !== "string" && threadId !== null) { - txnId = content; - content = threadId; - threadId = null; - } - const eventType = event_2.EventType.RoomMessage; - const sendContent = content; - return this.sendEvent(roomId, threadId, eventType, sendContent, txnId); - } - sendTextMessage(roomId, threadId, body, txnId) { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - txnId = body; - body = threadId; - threadId = null; - } - const content = ContentHelpers.makeTextMessage(body); - return this.sendMessage(roomId, threadId, content, txnId); - } - sendNotice(roomId, threadId, body, txnId) { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - txnId = body; - body = threadId; - threadId = null; - } - const content = ContentHelpers.makeNotice(body); - return this.sendMessage(roomId, threadId, content, txnId); - } - sendEmoteMessage(roomId, threadId, body, txnId) { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - txnId = body; - body = threadId; - threadId = null; - } - const content = ContentHelpers.makeEmoteMessage(body); - return this.sendMessage(roomId, threadId, content, txnId); - } - sendImageMessage(roomId, threadId, url, info, text = "Image") { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - text = info || "Image"; - info = url; - url = threadId; - threadId = null; - } - const content = { - msgtype: event_2.MsgType.Image, - url: url, - info: info, - body: text, - }; - return this.sendMessage(roomId, threadId, content); - } - sendStickerMessage(roomId, threadId, url, info, text = "Sticker") { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - text = info || "Sticker"; - info = url; - url = threadId; - threadId = null; - } - const content = { - url: url, - info: info, - body: text, - }; - return this.sendEvent(roomId, threadId, event_2.EventType.Sticker, content); - } - sendHtmlMessage(roomId, threadId, body, htmlBody) { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - htmlBody = body; - body = threadId; - threadId = null; - } - const content = ContentHelpers.makeHtmlMessage(body, htmlBody); - return this.sendMessage(roomId, threadId, content); - } - sendHtmlNotice(roomId, threadId, body, htmlBody) { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - htmlBody = body; - body = threadId; - threadId = null; - } - const content = ContentHelpers.makeHtmlNotice(body, htmlBody); - return this.sendMessage(roomId, threadId, content); - } - sendHtmlEmote(roomId, threadId, body, htmlBody) { - if (!(threadId === null || threadId === void 0 ? void 0 : threadId.startsWith(EVENT_ID_PREFIX)) && threadId !== null) { - htmlBody = body; - body = threadId; - threadId = null; - } - const content = ContentHelpers.makeHtmlEmote(body, htmlBody); - return this.sendMessage(roomId, threadId, content); - } - /** - * Send a receipt. - * @param event - The event being acknowledged - * @param receiptType - The kind of receipt e.g. "m.read". Other than - * ReceiptType.Read are experimental! - * @param body - Additional content to send alongside the receipt. - * @param unthreaded - An unthreaded receipt will clear room+thread notifications - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - sendReceipt(event, receiptType, body, unthreaded = false) { - return __awaiter(this, void 0, void 0, function* () { - if (this.isGuest()) { - return Promise.resolve({}); // guests cannot send receipts so don't bother. - } - const path = utils.encodeUri("/rooms/$roomId/receipt/$receiptType/$eventId", { - $roomId: event.getRoomId(), - $receiptType: receiptType, - $eventId: event.getId(), - }); - if (!unthreaded) { - const isThread = !!event.threadRootId; - body = Object.assign(Object.assign({}, body), { thread_id: isThread ? event.threadRootId : read_receipts_1.MAIN_ROOM_TIMELINE }); - } - const promise = this.http.authedRequest(http_api_1.Method.Post, path, undefined, body || {}); - const room = this.getRoom(event.getRoomId()); - if (room && this.credentials.userId) { - room.addLocalEchoReceipt(this.credentials.userId, event, receiptType); - } - return promise; - }); - } - /** - * Send a read receipt. - * @param event - The event that has been read. - * @param receiptType - other than ReceiptType.Read are experimental! Optional. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - sendReadReceipt(event, receiptType = read_receipts_1.ReceiptType.Read, unthreaded = false) { - return __awaiter(this, void 0, void 0, function* () { - if (!event) - return; - const eventId = event.getId(); - const room = this.getRoom(event.getRoomId()); - if (room === null || room === void 0 ? void 0 : room.hasPendingEvent(eventId)) { - throw new Error(`Cannot set read receipt to a pending event (${eventId})`); - } - return this.sendReceipt(event, receiptType, {}, unthreaded); - }); - } - /** - * Set a marker to indicate the point in a room before which the user has read every - * event. This can be retrieved from room account data (the event type is `m.fully_read`) - * and displayed as a horizontal line in the timeline that is visually distinct to the - * position of the user's own read receipt. - * @param roomId - ID of the room that has been read - * @param rmEventId - ID of the event that has been read - * @param rrEvent - the event tracked by the read receipt. This is here for - * convenience because the RR and the RM are commonly updated at the same time as each - * other. The local echo of this receipt will be done if set. Optional. - * @param rpEvent - the m.read.private read receipt event for when we don't - * want other users to see the read receipts. This is experimental. Optional. - * @returns Promise which resolves: the empty object, `{}`. - */ - setRoomReadMarkers(roomId, rmEventId, rrEvent, rpEvent) { - return __awaiter(this, void 0, void 0, function* () { - const room = this.getRoom(roomId); - if (room && room.hasPendingEvent(rmEventId)) { - throw new Error(`Cannot set read marker to a pending event (${rmEventId})`); - } - // Add the optional RR update, do local echo like `sendReceipt` - let rrEventId; - if (rrEvent) { - rrEventId = rrEvent.getId(); - if (room === null || room === void 0 ? void 0 : room.hasPendingEvent(rrEventId)) { - throw new Error(`Cannot set read receipt to a pending event (${rrEventId})`); - } - room === null || room === void 0 ? void 0 : room.addLocalEchoReceipt(this.credentials.userId, rrEvent, read_receipts_1.ReceiptType.Read); - } - // Add the optional private RR update, do local echo like `sendReceipt` - let rpEventId; - if (rpEvent) { - rpEventId = rpEvent.getId(); - if (room === null || room === void 0 ? void 0 : room.hasPendingEvent(rpEventId)) { - throw new Error(`Cannot set read receipt to a pending event (${rpEventId})`); - } - room === null || room === void 0 ? void 0 : room.addLocalEchoReceipt(this.credentials.userId, rpEvent, read_receipts_1.ReceiptType.ReadPrivate); - } - return yield this.setRoomReadMarkersHttpRequest(roomId, rmEventId, rrEventId, rpEventId); - }); - } - /** - * Get a preview of the given URL as of (roughly) the given point in time, - * described as an object with OpenGraph keys and associated values. - * Attributes may be synthesized where actual OG metadata is lacking. - * Caches results to prevent hammering the server. - * @param url - The URL to get preview data for - * @param ts - The preferred point in time that the preview should - * describe (ms since epoch). The preview returned will either be the most - * recent one preceding this timestamp if available, or failing that the next - * most recent available preview. - * @returns Promise which resolves: Object of OG metadata. - * @returns Rejects: with an error response. - * May return synthesized attributes if the URL lacked OG meta. - */ - getUrlPreview(url, ts) { - // bucket the timestamp to the nearest minute to prevent excessive spam to the server - // Surely 60-second accuracy is enough for anyone. - ts = Math.floor(ts / 60000) * 60000; - const parsed = new URL(url); - parsed.hash = ""; // strip the hash as it won't affect the preview - url = parsed.toString(); - const key = ts + "_" + url; - // If there's already a request in flight (or we've handled it), return that instead. - const cachedPreview = this.urlPreviewCache[key]; - if (cachedPreview) { - return cachedPreview; - } - const resp = this.http.authedRequest(http_api_1.Method.Get, "/preview_url", { - url, - ts: ts.toString(), - }, undefined, { - prefix: http_api_1.MediaPrefix.R0, - }); - // TODO: Expire the URL preview cache sometimes - this.urlPreviewCache[key] = resp; - return resp; - } - /** - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - sendTyping(roomId, isTyping, timeoutMs) { - if (this.isGuest()) { - return Promise.resolve({}); // guests cannot send typing notifications so don't bother. - } - const path = utils.encodeUri("/rooms/$roomId/typing/$userId", { - $roomId: roomId, - $userId: this.getUserId(), - }); - const data = { - typing: isTyping, - }; - if (isTyping) { - data.timeout = timeoutMs ? timeoutMs : 20000; - } - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, data); - } - /** - * Determines the history of room upgrades for a given room, as far as the - * client can see. Returns an array of Rooms where the first entry is the - * oldest and the last entry is the newest (likely current) room. If the - * provided room is not found, this returns an empty list. This works in - * both directions, looking for older and newer rooms of the given room. - * @param roomId - The room ID to search from - * @param verifyLinks - If true, the function will only return rooms - * which can be proven to be linked. For example, rooms which have a create - * event pointing to an old room which the client is not aware of or doesn't - * have a matching tombstone would not be returned. - * @param msc3946ProcessDynamicPredecessor - if true, look for - * m.room.predecessor state events as well as create events, and prefer - * predecessor events where they exist (MSC3946). - * @returns An array of rooms representing the upgrade - * history. - */ - getRoomUpgradeHistory(roomId, verifyLinks = false, msc3946ProcessDynamicPredecessor = false) { - const currentRoom = this.getRoom(roomId); - if (!currentRoom) - return []; - const before = this.findPredecessorRooms(currentRoom, verifyLinks, msc3946ProcessDynamicPredecessor); - const after = this.findSuccessorRooms(currentRoom, verifyLinks, msc3946ProcessDynamicPredecessor); - return [...before, currentRoom, ...after]; - } - findPredecessorRooms(room, verifyLinks, msc3946ProcessDynamicPredecessor) { - var _a, _b; - const ret = []; - // Work backwards from newer to older rooms - let predecessorRoomId = (_a = room.findPredecessor(msc3946ProcessDynamicPredecessor)) === null || _a === void 0 ? void 0 : _a.roomId; - while (predecessorRoomId !== null) { - const predecessorRoom = this.getRoom(predecessorRoomId); - if (predecessorRoom === null) { - break; - } - if (verifyLinks) { - const tombstone = predecessorRoom.currentState.getStateEvents(event_2.EventType.RoomTombstone, ""); - if (!tombstone || tombstone.getContent()["replacement_room"] !== room.roomId) { - break; - } - } - // Insert at the front because we're working backwards from the currentRoom - ret.splice(0, 0, predecessorRoom); - room = predecessorRoom; - predecessorRoomId = (_b = room.findPredecessor(msc3946ProcessDynamicPredecessor)) === null || _b === void 0 ? void 0 : _b.roomId; - } - return ret; - } - findSuccessorRooms(room, verifyLinks, msc3946ProcessDynamicPredecessor) { - var _a; - const ret = []; - // Work forwards, looking at tombstone events - let tombstoneEvent = room.currentState.getStateEvents(event_2.EventType.RoomTombstone, ""); - while (tombstoneEvent) { - const successorRoom = this.getRoom(tombstoneEvent.getContent()["replacement_room"]); - if (!successorRoom) - break; // end of the chain - if (successorRoom.roomId === room.roomId) - break; // Tombstone is referencing its own room - if (verifyLinks) { - const predecessorRoomId = (_a = successorRoom.findPredecessor(msc3946ProcessDynamicPredecessor)) === null || _a === void 0 ? void 0 : _a.roomId; - if (!predecessorRoomId || predecessorRoomId !== room.roomId) { - break; - } - } - // Push to the end because we're looking forwards - ret.push(successorRoom); - const roomIds = new Set(ret.map((ref) => ref.roomId)); - if (roomIds.size < ret.length) { - // The last room added to the list introduced a previous roomId - // To avoid recursion, return the last rooms - 1 - return ret.slice(0, ret.length - 1); - } - // Set the current room to the reference room so we know where we're at - room = successorRoom; - tombstoneEvent = room.currentState.getStateEvents(event_2.EventType.RoomTombstone, ""); - } - return ret; - } - /** - * @param reason - Optional. - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - invite(roomId, userId, reason) { - return this.membershipChange(roomId, userId, "invite", reason); - } - /** - * Invite a user to a room based on their email address. - * @param roomId - The room to invite the user to. - * @param email - The email address to invite. - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - inviteByEmail(roomId, email) { - return this.inviteByThreePid(roomId, "email", email); - } - /** - * Invite a user to a room based on a third-party identifier. - * @param roomId - The room to invite the user to. - * @param medium - The medium to invite the user e.g. "email". - * @param address - The address for the specified medium. - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - inviteByThreePid(roomId, medium, address) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const path = utils.encodeUri("/rooms/$roomId/invite", { $roomId: roomId }); - const identityServerUrl = this.getIdentityServerUrl(true); - if (!identityServerUrl) { - return Promise.reject(new http_api_1.MatrixError({ - error: "No supplied identity server URL", - errcode: "ORG.MATRIX.JSSDK_MISSING_PARAM", - })); - } - const params = { - id_server: identityServerUrl, - medium: medium, - address: address, - }; - if (((_a = this.identityServer) === null || _a === void 0 ? void 0 : _a.getAccessToken) && (yield this.doesServerAcceptIdentityAccessToken())) { - const identityAccessToken = yield this.identityServer.getAccessToken(); - if (identityAccessToken) { - params["id_access_token"] = identityAccessToken; - } - } - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, params); - }); - } - /** - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - leave(roomId) { - return this.membershipChange(roomId, undefined, "leave"); - } - /** - * Leaves all rooms in the chain of room upgrades based on the given room. By - * default, this will leave all the previous and upgraded rooms, including the - * given room. To only leave the given room and any previous rooms, keeping the - * upgraded (modern) rooms untouched supply `false` to `includeFuture`. - * @param roomId - The room ID to start leaving at - * @param includeFuture - If true, the whole chain (past and future) of - * upgraded rooms will be left. - * @returns Promise which resolves when completed with an object keyed - * by room ID and value of the error encountered when leaving or null. - */ - leaveRoomChain(roomId, includeFuture = true) { - const upgradeHistory = this.getRoomUpgradeHistory(roomId); - let eligibleToLeave = upgradeHistory; - if (!includeFuture) { - eligibleToLeave = []; - for (const room of upgradeHistory) { - eligibleToLeave.push(room); - if (room.roomId === roomId) { - break; - } - } - } - const populationResults = {}; - const promises = []; - const doLeave = (roomId) => { - return this.leave(roomId) - .then(() => { - delete populationResults[roomId]; - }) - .catch((err) => { - // suppress error - populationResults[roomId] = err; - }); - }; - for (const room of eligibleToLeave) { - promises.push(doLeave(room.roomId)); - } - return Promise.all(promises).then(() => populationResults); - } - /** - * @param reason - Optional. - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - ban(roomId, userId, reason) { - return this.membershipChange(roomId, userId, "ban", reason); - } - /** - * @param deleteRoom - True to delete the room from the store on success. - * Default: true. - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - forget(roomId, deleteRoom = true) { - const promise = this.membershipChange(roomId, undefined, "forget"); - if (!deleteRoom) { - return promise; - } - return promise.then((response) => { - this.store.removeRoom(roomId); - this.emit(ClientEvent.DeleteRoom, roomId); - return response; - }); - } - /** - * @returns Promise which resolves: Object (currently empty) - * @returns Rejects: with an error response. - */ - unban(roomId, userId) { - // unbanning != set their state to leave: this used to be - // the case, but was then changed so that leaving was always - // a revoking of privilege, otherwise two people racing to - // kick / ban someone could end up banning and then un-banning - // them. - const path = utils.encodeUri("/rooms/$roomId/unban", { - $roomId: roomId, - }); - const data = { - user_id: userId, - }; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data); - } - /** - * @param reason - Optional. - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - kick(roomId, userId, reason) { - const path = utils.encodeUri("/rooms/$roomId/kick", { - $roomId: roomId, - }); - const data = { - user_id: userId, - reason: reason, - }; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data); - } - membershipChange(roomId, userId, membership, reason) { - // API returns an empty object - const path = utils.encodeUri("/rooms/$room_id/$membership", { - $room_id: roomId, - $membership: membership, - }); - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, { - user_id: userId, - reason: reason, - }); - } - /** - * Obtain a dict of actions which should be performed for this event according - * to the push rules for this user. Caches the dict on the event. - * @param event - The event to get push actions for. - * @param forceRecalculate - forces to recalculate actions for an event - * Useful when an event just got decrypted - * @returns A dict of actions to perform. - */ - getPushActionsForEvent(event, forceRecalculate = false) { - if (!event.getPushActions() || forceRecalculate) { - event.setPushActions(this.pushProcessor.actionsForEvent(event)); - } - return event.getPushActions(); - } - setProfileInfo(info, data) { - const path = utils.encodeUri("/profile/$userId/$info", { - $userId: this.credentials.userId, - $info: info, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, data); - } - /** - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - setDisplayName(name) { - return __awaiter(this, void 0, void 0, function* () { - const prom = yield this.setProfileInfo("displayname", { displayname: name }); - // XXX: synthesise a profile update for ourselves because Synapse is broken and won't - const user = this.getUser(this.getUserId()); - if (user) { - user.displayName = name; - user.emit(user_1.UserEvent.DisplayName, user.events.presence, user); - } - return prom; - }); - } - /** - * @returns Promise which resolves: `{}` an empty object. - * @returns Rejects: with an error response. - */ - setAvatarUrl(url) { - return __awaiter(this, void 0, void 0, function* () { - const prom = yield this.setProfileInfo("avatar_url", { avatar_url: url }); - // XXX: synthesise a profile update for ourselves because Synapse is broken and won't - const user = this.getUser(this.getUserId()); - if (user) { - user.avatarUrl = url; - user.emit(user_1.UserEvent.AvatarUrl, user.events.presence, user); - } - return prom; - }); - } - /** - * Turn an MXC URL into an HTTP one. This method is experimental and - * may change. - * @param mxcUrl - The MXC URL - * @param width - The desired width of the thumbnail. - * @param height - The desired height of the thumbnail. - * @param resizeMethod - The thumbnail resize method to use, either - * "crop" or "scale". - * @param allowDirectLinks - If true, return any non-mxc URLs - * directly. Fetching such URLs will leak information about the user to - * anyone they share a room with. If false, will return null for such URLs. - * @returns the avatar URL or null. - */ - mxcUrlToHttp(mxcUrl, width, height, resizeMethod, allowDirectLinks) { - return (0, content_repo_1.getHttpUriForMxc)(this.baseUrl, mxcUrl, width, height, resizeMethod, allowDirectLinks); - } - /** - * @param opts - Options to apply - * @returns Promise which resolves - * @returns Rejects: with an error response. - * @throws If 'presence' isn't a valid presence enum value. - */ - setPresence(opts) { - return __awaiter(this, void 0, void 0, function* () { - const path = utils.encodeUri("/presence/$userId/status", { - $userId: this.credentials.userId, - }); - const validStates = ["offline", "online", "unavailable"]; - if (validStates.indexOf(opts.presence) === -1) { - throw new Error("Bad presence value: " + opts.presence); - } - yield this.http.authedRequest(http_api_1.Method.Put, path, undefined, opts); - }); - } - /** - * @param userId - The user to get presence for - * @returns Promise which resolves: The presence state for this user. - * @returns Rejects: with an error response. - */ - getPresence(userId) { - const path = utils.encodeUri("/presence/$userId/status", { - $userId: userId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * Retrieve older messages from the given room and put them in the timeline. - * - * If this is called multiple times whilst a request is ongoing, the same - * Promise will be returned. If there was a problem requesting scrollback, there - * will be a small delay before another request can be made (to prevent tight-looping - * when there is no connection). - * - * @param room - The room to get older messages in. - * @param limit - Optional. The maximum number of previous events to - * pull in. Default: 30. - * @returns Promise which resolves: Room. If you are at the beginning - * of the timeline, `Room.oldState.paginationToken` will be - * `null`. - * @returns Rejects: with an error response. - */ - scrollback(room, limit = 30) { - let timeToWaitMs = 0; - let info = this.ongoingScrollbacks[room.roomId] || {}; - if (info.promise) { - return info.promise; - } - else if (info.errorTs) { - const timeWaitedMs = Date.now() - info.errorTs; - timeToWaitMs = Math.max(SCROLLBACK_DELAY_MS - timeWaitedMs, 0); - } - if (room.oldState.paginationToken === null) { - return Promise.resolve(room); // already at the start. - } - // attempt to grab more events from the store first - const numAdded = this.store.scrollback(room, limit).length; - if (numAdded === limit) { - // store contained everything we needed. - return Promise.resolve(room); - } - // reduce the required number of events appropriately - limit = limit - numAdded; - const promise = new Promise((resolve, reject) => { - // wait for a time before doing this request - // (which may be 0 in order not to special case the code paths) - (0, utils_1.sleep)(timeToWaitMs) - .then(() => { - return this.createMessagesRequest(room.roomId, room.oldState.paginationToken, limit, event_timeline_1.Direction.Backward); - }) - .then((res) => { - var _a, _b; - const matrixEvents = res.chunk.map(this.getEventMapper()); - if (res.state) { - const stateEvents = res.state.map(this.getEventMapper()); - room.currentState.setUnknownStateEvents(stateEvents); - } - const [timelineEvents, threadedEvents] = room.partitionThreadedEvents(matrixEvents); - this.processAggregatedTimelineEvents(room, timelineEvents); - room.addEventsToTimeline(timelineEvents, true, room.getLiveTimeline()); - this.processThreadEvents(room, threadedEvents, true); - room.oldState.paginationToken = (_a = res.end) !== null && _a !== void 0 ? _a : null; - if (res.chunk.length === 0) { - room.oldState.paginationToken = null; - } - this.store.storeEvents(room, matrixEvents, (_b = res.end) !== null && _b !== void 0 ? _b : null, true); - delete this.ongoingScrollbacks[room.roomId]; - resolve(room); - }) - .catch((err) => { - this.ongoingScrollbacks[room.roomId] = { - errorTs: Date.now(), - }; - reject(err); - }); - }); - info = { promise }; - this.ongoingScrollbacks[room.roomId] = info; - return promise; - } - getEventMapper(options) { - return (0, event_mapper_1.eventMapperFor)(this, options || {}); - } - /** - * Get an EventTimeline for the given event - * - *

If the EventTimelineSet object already has the given event in its store, the - * corresponding timeline will be returned. Otherwise, a /context request is - * made, and used to construct an EventTimeline. - * If the event does not belong to this EventTimelineSet then undefined will be returned. - * - * @param timelineSet - The timelineSet to look for the event in, must be bound to a room - * @param eventId - The ID of the event to look for - * - * @returns Promise which resolves: - * {@link EventTimeline} including the given event - */ - getEventTimeline(timelineSet, eventId) { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - // don't allow any timeline support unless it's been enabled. - if (!this.timelineSupport) { - throw new Error("timeline support is disabled. Set the 'timelineSupport'" + - " parameter to true when creating MatrixClient to enable it."); - } - if (!(timelineSet === null || timelineSet === void 0 ? void 0 : timelineSet.room)) { - throw new Error("getEventTimeline only supports room timelines"); - } - if (timelineSet.getTimelineForEvent(eventId)) { - return timelineSet.getTimelineForEvent(eventId); - } - if (timelineSet.thread && this.supportsThreads()) { - return this.getThreadTimeline(timelineSet, eventId); - } - const path = utils.encodeUri("/rooms/$roomId/context/$eventId", { - $roomId: timelineSet.room.roomId, - $eventId: eventId, - }); - let params = undefined; - if ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.lazyLoadMembers) { - params = { filter: JSON.stringify(filter_1.Filter.LAZY_LOADING_MESSAGES_FILTER) }; - } - // TODO: we should implement a backoff (as per scrollback()) to deal more nicely with HTTP errors. - const res = yield this.http.authedRequest(http_api_1.Method.Get, path, params); - if (!res.event) { - throw new Error("'event' not in '/context' result - homeserver too old?"); - } - // by the time the request completes, the event might have ended up in the timeline. - if (timelineSet.getTimelineForEvent(eventId)) { - return timelineSet.getTimelineForEvent(eventId); - } - const mapper = this.getEventMapper(); - const event = mapper(res.event); - if (event.isRelation(thread_1.THREAD_RELATION_TYPE.name)) { - logger_1.logger.warn("Tried loading a regular timeline at the position of a thread event"); - return undefined; - } - const events = [ - // Order events from most recent to oldest (reverse-chronological). - // We start with the last event, since that's the point at which we have known state. - // events_after is already backwards; events_before is forwards. - ...res.events_after.reverse().map(mapper), - event, - ...res.events_before.map(mapper), - ]; - // Here we handle non-thread timelines only, but still process any thread events to populate thread summaries. - let timeline = timelineSet.getTimelineForEvent(events[0].getId()); - if (timeline) { - timeline.getState(event_timeline_1.EventTimeline.BACKWARDS).setUnknownStateEvents(res.state.map(mapper)); - } - else { - timeline = timelineSet.addTimeline(); - timeline.initialiseState(res.state.map(mapper)); - timeline.getState(event_timeline_1.EventTimeline.FORWARDS).paginationToken = res.end; - } - const [timelineEvents, threadedEvents] = timelineSet.room.partitionThreadedEvents(events); - timelineSet.addEventsToTimeline(timelineEvents, true, timeline, res.start); - // The target event is not in a thread but process the contextual events, so we can show any threads around it. - this.processThreadEvents(timelineSet.room, threadedEvents, true); - this.processAggregatedTimelineEvents(timelineSet.room, timelineEvents); - // There is no guarantee that the event ended up in "timeline" (we might have switched to a neighbouring - // timeline) - so check the room's index again. On the other hand, there's no guarantee the event ended up - // anywhere, if it was later redacted, so we just return the timeline we first thought of. - return ((_d = (_b = timelineSet.getTimelineForEvent(eventId)) !== null && _b !== void 0 ? _b : (_c = timelineSet.room.findThreadForEvent(event)) === null || _c === void 0 ? void 0 : _c.liveTimeline) !== null && _d !== void 0 ? _d : timeline); - }); - } - getThreadTimeline(timelineSet, eventId) { - var _a, _b, _c, _d, _e, _f, _g, _h; - return __awaiter(this, void 0, void 0, function* () { - if (!this.supportsThreads()) { - throw new Error("could not get thread timeline: no client support"); - } - if (!timelineSet.room) { - throw new Error("could not get thread timeline: not a room timeline"); - } - if (!timelineSet.thread) { - throw new Error("could not get thread timeline: not a thread timeline"); - } - const path = utils.encodeUri("/rooms/$roomId/context/$eventId", { - $roomId: timelineSet.room.roomId, - $eventId: eventId, - }); - const params = { - limit: "0", - }; - if ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.lazyLoadMembers) { - params.filter = JSON.stringify(filter_1.Filter.LAZY_LOADING_MESSAGES_FILTER); - } - // TODO: we should implement a backoff (as per scrollback()) to deal more nicely with HTTP errors. - const res = yield this.http.authedRequest(http_api_1.Method.Get, path, params); - const mapper = this.getEventMapper(); - const event = mapper(res.event); - if (!timelineSet.canContain(event)) { - return undefined; - } - if (thread_1.Thread.hasServerSideSupport) { - if (thread_1.Thread.hasServerSideFwdPaginationSupport) { - if (!timelineSet.thread) { - throw new Error("could not get thread timeline: not a thread timeline"); - } - const thread = timelineSet.thread; - const resOlder = yield this.fetchRelations(timelineSet.room.roomId, thread.id, thread_1.THREAD_RELATION_TYPE.name, null, { dir: event_timeline_1.Direction.Backward, from: res.start }); - const resNewer = yield this.fetchRelations(timelineSet.room.roomId, thread.id, thread_1.THREAD_RELATION_TYPE.name, null, { dir: event_timeline_1.Direction.Forward, from: res.end }); - const events = [ - // Order events from most recent to oldest (reverse-chronological). - // We start with the last event, since that's the point at which we have known state. - // events_after is already backwards; events_before is forwards. - ...resNewer.chunk.reverse().map(mapper), - event, - ...resOlder.chunk.map(mapper), - ]; - for (const event of events) { - yield ((_b = timelineSet.thread) === null || _b === void 0 ? void 0 : _b.processEvent(event)); - } - // Here we handle non-thread timelines only, but still process any thread events to populate thread summaries. - let timeline = timelineSet.getTimelineForEvent(event.getId()); - if (timeline) { - timeline.getState(event_timeline_1.EventTimeline.BACKWARDS).setUnknownStateEvents(res.state.map(mapper)); - } - else { - timeline = timelineSet.addTimeline(); - timeline.initialiseState(res.state.map(mapper)); - } - timelineSet.addEventsToTimeline(events, true, timeline, resNewer.next_batch); - if (!resOlder.next_batch) { - const originalEvent = yield this.fetchRoomEvent(timelineSet.room.roomId, thread.id); - timelineSet.addEventsToTimeline([mapper(originalEvent)], true, timeline, null); - } - timeline.setPaginationToken((_c = resOlder.next_batch) !== null && _c !== void 0 ? _c : null, event_timeline_1.Direction.Backward); - timeline.setPaginationToken((_d = resNewer.next_batch) !== null && _d !== void 0 ? _d : null, event_timeline_1.Direction.Forward); - this.processAggregatedTimelineEvents(timelineSet.room, events); - // There is no guarantee that the event ended up in "timeline" (we might have switched to a neighbouring - // timeline) - so check the room's index again. On the other hand, there's no guarantee the event ended up - // anywhere, if it was later redacted, so we just return the timeline we first thought of. - return (_e = timelineSet.getTimelineForEvent(eventId)) !== null && _e !== void 0 ? _e : timeline; - } - else { - // Where the event is a thread reply (not a root) and running in MSC-enabled mode the Thread timeline only - // functions contiguously, so we have to jump through some hoops to get our target event in it. - // XXX: workaround for https://github.com/vector-im/element-meta/issues/150 - const thread = timelineSet.thread; - const resOlder = yield this.fetchRelations(timelineSet.room.roomId, thread.id, thread_1.THREAD_RELATION_TYPE.name, null, { dir: event_timeline_1.Direction.Backward, from: res.start }); - const eventsNewer = []; - let nextBatch = res.end; - while (nextBatch) { - const resNewer = yield this.fetchRelations(timelineSet.room.roomId, thread.id, thread_1.THREAD_RELATION_TYPE.name, null, { dir: event_timeline_1.Direction.Forward, from: nextBatch }); - nextBatch = (_f = resNewer.next_batch) !== null && _f !== void 0 ? _f : null; - eventsNewer.push(...resNewer.chunk); - } - const events = [ - // Order events from most recent to oldest (reverse-chronological). - // We start with the last event, since that's the point at which we have known state. - // events_after is already backwards; events_before is forwards. - ...eventsNewer.reverse().map(mapper), - event, - ...resOlder.chunk.map(mapper), - ]; - for (const event of events) { - yield ((_g = timelineSet.thread) === null || _g === void 0 ? void 0 : _g.processEvent(event)); - } - // Here we handle non-thread timelines only, but still process any thread events to populate thread - // summaries. - const timeline = timelineSet.getLiveTimeline(); - timeline.getState(event_timeline_1.EventTimeline.BACKWARDS).setUnknownStateEvents(res.state.map(mapper)); - timelineSet.addEventsToTimeline(events, true, timeline, null); - if (!resOlder.next_batch) { - const originalEvent = yield this.fetchRoomEvent(timelineSet.room.roomId, thread.id); - timelineSet.addEventsToTimeline([mapper(originalEvent)], true, timeline, null); - } - timeline.setPaginationToken((_h = resOlder.next_batch) !== null && _h !== void 0 ? _h : null, event_timeline_1.Direction.Backward); - timeline.setPaginationToken(null, event_timeline_1.Direction.Forward); - this.processAggregatedTimelineEvents(timelineSet.room, events); - return timeline; - } - } - }); - } - /** - * Get an EventTimeline for the latest events in the room. This will just - * call `/messages` to get the latest message in the room, then use - * `client.getEventTimeline(...)` to construct a new timeline from it. - * - * @param timelineSet - The timelineSet to find or add the timeline to - * - * @returns Promise which resolves: - * {@link EventTimeline} timeline with the latest events in the room - */ - getLatestTimeline(timelineSet) { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - // don't allow any timeline support unless it's been enabled. - if (!this.timelineSupport) { - throw new Error("timeline support is disabled. Set the 'timelineSupport'" + - " parameter to true when creating MatrixClient to enable it."); - } - if (!timelineSet.room) { - throw new Error("getLatestTimeline only supports room timelines"); - } - let event; - if (timelineSet.threadListType !== null) { - const res = yield this.createThreadListMessagesRequest(timelineSet.room.roomId, null, 1, event_timeline_1.Direction.Backward, timelineSet.threadListType, timelineSet.getFilter()); - event = (_a = res.chunk) === null || _a === void 0 ? void 0 : _a[0]; - } - else if (timelineSet.thread && thread_1.Thread.hasServerSideSupport) { - const res = yield this.fetchRelations(timelineSet.room.roomId, timelineSet.thread.id, thread_1.THREAD_RELATION_TYPE.name, null, { dir: event_timeline_1.Direction.Backward, limit: 1 }); - event = (_b = res.chunk) === null || _b === void 0 ? void 0 : _b[0]; - } - else { - const messagesPath = utils.encodeUri("/rooms/$roomId/messages", { - $roomId: timelineSet.room.roomId, - }); - const params = { - dir: "b", - }; - if ((_c = this.clientOpts) === null || _c === void 0 ? void 0 : _c.lazyLoadMembers) { - params.filter = JSON.stringify(filter_1.Filter.LAZY_LOADING_MESSAGES_FILTER); - } - const res = yield this.http.authedRequest(http_api_1.Method.Get, messagesPath, params); - event = (_d = res.chunk) === null || _d === void 0 ? void 0 : _d[0]; - } - if (!event) { - throw new Error("No message returned when trying to construct getLatestTimeline"); - } - return this.getEventTimeline(timelineSet, event.event_id); - }); - } - /** - * Makes a request to /messages with the appropriate lazy loading filter set. - * XXX: if we do get rid of scrollback (as it's not used at the moment), - * we could inline this method again in paginateEventTimeline as that would - * then be the only call-site - * @param limit - the maximum amount of events the retrieve - * @param dir - 'f' or 'b' - * @param timelineFilter - the timeline filter to pass - */ - // XXX: Intended private, used in code. - createMessagesRequest(roomId, fromToken, limit = 30, dir, timelineFilter) { - var _a, _b; - const path = utils.encodeUri("/rooms/$roomId/messages", { $roomId: roomId }); - const params = { - limit: limit.toString(), - dir: dir, - }; - if (fromToken) { - params.from = fromToken; - } - let filter = null; - if ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.lazyLoadMembers) { - // create a shallow copy of LAZY_LOADING_MESSAGES_FILTER, - // so the timelineFilter doesn't get written into it below - filter = Object.assign({}, filter_1.Filter.LAZY_LOADING_MESSAGES_FILTER); - } - if (timelineFilter) { - // XXX: it's horrific that /messages' filter parameter doesn't match - // /sync's one - see https://matrix.org/jira/browse/SPEC-451 - filter = filter || {}; - Object.assign(filter, (_b = timelineFilter.getRoomTimelineFilterComponent()) === null || _b === void 0 ? void 0 : _b.toJSON()); - } - if (filter) { - params.filter = JSON.stringify(filter); - } - return this.http.authedRequest(http_api_1.Method.Get, path, params); - } - /** - * Makes a request to /messages with the appropriate lazy loading filter set. - * XXX: if we do get rid of scrollback (as it's not used at the moment), - * we could inline this method again in paginateEventTimeline as that would - * then be the only call-site - * @param limit - the maximum amount of events the retrieve - * @param dir - 'f' or 'b' - * @param timelineFilter - the timeline filter to pass - */ - // XXX: Intended private, used by room.fetchRoomThreads - createThreadListMessagesRequest(roomId, fromToken, limit = 30, dir = event_timeline_1.Direction.Backward, threadListType = thread_1.ThreadFilterType.All, timelineFilter) { - var _a, _b; - const path = utils.encodeUri("/rooms/$roomId/threads", { $roomId: roomId }); - const params = { - limit: limit.toString(), - dir: dir, - include: (0, thread_1.threadFilterTypeToFilter)(threadListType), - }; - if (fromToken) { - params.from = fromToken; - } - let filter = {}; - if ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.lazyLoadMembers) { - // create a shallow copy of LAZY_LOADING_MESSAGES_FILTER, - // so the timelineFilter doesn't get written into it below - filter = Object.assign({}, filter_1.Filter.LAZY_LOADING_MESSAGES_FILTER); - } - if (timelineFilter) { - // XXX: it's horrific that /messages' filter parameter doesn't match - // /sync's one - see https://matrix.org/jira/browse/SPEC-451 - filter = Object.assign(Object.assign({}, filter), (_b = timelineFilter.getRoomTimelineFilterComponent()) === null || _b === void 0 ? void 0 : _b.toJSON()); - } - if (Object.keys(filter).length) { - params.filter = JSON.stringify(filter); - } - const opts = { - prefix: thread_1.Thread.hasServerSideListSupport === thread_1.FeatureSupport.Stable - ? "/_matrix/client/v1" - : "/_matrix/client/unstable/org.matrix.msc3856", - }; - return this.http - .authedRequest(http_api_1.Method.Get, path, params, undefined, opts) - .then((res) => { - var _a; - return (Object.assign(Object.assign({}, res), { chunk: (_a = res.chunk) === null || _a === void 0 ? void 0 : _a.reverse(), start: res.prev_batch, end: res.next_batch })); - }); - } - /** - * Take an EventTimeline, and back/forward-fill results. - * - * @param eventTimeline - timeline object to be updated - * - * @returns Promise which resolves to a boolean: false if there are no - * events and we reached either end of the timeline; else true. - */ - paginateEventTimeline(eventTimeline, opts) { - var _a, _b, _c; - const isNotifTimeline = eventTimeline.getTimelineSet() === this.notifTimelineSet; - const room = this.getRoom(eventTimeline.getRoomId()); - const threadListType = eventTimeline.getTimelineSet().threadListType; - const thread = eventTimeline.getTimelineSet().thread; - // TODO: we should implement a backoff (as per scrollback()) to deal more - // nicely with HTTP errors. - opts = opts || {}; - const backwards = opts.backwards || false; - if (isNotifTimeline) { - if (!backwards) { - throw new Error("paginateNotifTimeline can only paginate backwards"); - } - } - const dir = backwards ? event_timeline_1.EventTimeline.BACKWARDS : event_timeline_1.EventTimeline.FORWARDS; - const token = eventTimeline.getPaginationToken(dir); - const pendingRequest = eventTimeline.paginationRequests[dir]; - if (pendingRequest) { - // already a request in progress - return the existing promise - return pendingRequest; - } - let path; - let params; - let promise; - if (isNotifTimeline) { - path = "/notifications"; - params = { - limit: ((_a = opts.limit) !== null && _a !== void 0 ? _a : 30).toString(), - only: "highlight", - }; - if (token && token !== "end") { - params.from = token; - } - promise = this.http - .authedRequest(http_api_1.Method.Get, path, params) - .then((res) => __awaiter(this, void 0, void 0, function* () { - const token = res.next_token; - const matrixEvents = []; - res.notifications = res.notifications.filter(utils_1.noUnsafeEventProps); - for (let i = 0; i < res.notifications.length; i++) { - const notification = res.notifications[i]; - const event = this.getEventMapper()(notification.event); - event.setPushActions(pushprocessor_1.PushProcessor.actionListToActionsObject(notification.actions)); - event.event.room_id = notification.room_id; // XXX: gutwrenching - matrixEvents[i] = event; - } - // No need to partition events for threads here, everything lives - // in the notification timeline set - const timelineSet = eventTimeline.getTimelineSet(); - timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token); - this.processAggregatedTimelineEvents(timelineSet.room, matrixEvents); - // if we've hit the end of the timeline, we need to stop trying to - // paginate. We need to keep the 'forwards' token though, to make sure - // we can recover from gappy syncs. - if (backwards && !res.next_token) { - eventTimeline.setPaginationToken(null, dir); - } - return Boolean(res.next_token); - })) - .finally(() => { - eventTimeline.paginationRequests[dir] = null; - }); - eventTimeline.paginationRequests[dir] = promise; - } - else if (threadListType !== null) { - if (!room) { - throw new Error("Unknown room " + eventTimeline.getRoomId()); - } - if (!thread_1.Thread.hasServerSideFwdPaginationSupport && dir === event_timeline_1.Direction.Forward) { - throw new Error("Cannot paginate threads forwards without server-side support for MSC 3715"); - } - promise = this.createThreadListMessagesRequest(eventTimeline.getRoomId(), token, opts.limit, dir, threadListType, eventTimeline.getFilter()) - .then((res) => { - if (res.state) { - const roomState = eventTimeline.getState(dir); - const stateEvents = res.state.filter(utils_1.noUnsafeEventProps).map(this.getEventMapper()); - roomState.setUnknownStateEvents(stateEvents); - } - const token = res.end; - const matrixEvents = res.chunk.filter(utils_1.noUnsafeEventProps).map(this.getEventMapper()); - const timelineSet = eventTimeline.getTimelineSet(); - timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token); - this.processAggregatedTimelineEvents(room, matrixEvents); - this.processThreadRoots(room, matrixEvents, backwards); - // if we've hit the end of the timeline, we need to stop trying to - // paginate. We need to keep the 'forwards' token though, to make sure - // we can recover from gappy syncs. - if (backwards && res.end == res.start) { - eventTimeline.setPaginationToken(null, dir); - } - return res.end !== res.start; - }) - .finally(() => { - eventTimeline.paginationRequests[dir] = null; - }); - eventTimeline.paginationRequests[dir] = promise; - } - else if (thread) { - const room = this.getRoom((_b = eventTimeline.getRoomId()) !== null && _b !== void 0 ? _b : undefined); - if (!room) { - throw new Error("Unknown room " + eventTimeline.getRoomId()); - } - promise = this.fetchRelations((_c = eventTimeline.getRoomId()) !== null && _c !== void 0 ? _c : "", thread.id, thread_1.THREAD_RELATION_TYPE.name, null, { - dir, - limit: opts.limit, - from: token !== null && token !== void 0 ? token : undefined, - }) - .then((res) => __awaiter(this, void 0, void 0, function* () { - var _d; - const mapper = this.getEventMapper(); - const matrixEvents = res.chunk.filter(utils_1.noUnsafeEventProps).map(mapper); - // Process latest events first - for (const event of matrixEvents.slice().reverse()) { - yield (thread === null || thread === void 0 ? void 0 : thread.processEvent(event)); - const sender = event.getSender(); - if (!backwards || (thread === null || thread === void 0 ? void 0 : thread.getEventReadUpTo(sender)) === null) { - room.addLocalEchoReceipt(sender, event, read_receipts_1.ReceiptType.Read); - } - } - const newToken = res.next_batch; - const timelineSet = eventTimeline.getTimelineSet(); - timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, newToken !== null && newToken !== void 0 ? newToken : null); - if (!newToken && backwards) { - const originalEvent = yield this.fetchRoomEvent((_d = eventTimeline.getRoomId()) !== null && _d !== void 0 ? _d : "", thread.id); - timelineSet.addEventsToTimeline([mapper(originalEvent)], true, eventTimeline, null); - } - this.processAggregatedTimelineEvents(timelineSet.room, matrixEvents); - // if we've hit the end of the timeline, we need to stop trying to - // paginate. We need to keep the 'forwards' token though, to make sure - // we can recover from gappy syncs. - if (backwards && !newToken) { - eventTimeline.setPaginationToken(null, dir); - } - return Boolean(newToken); - })) - .finally(() => { - eventTimeline.paginationRequests[dir] = null; - }); - eventTimeline.paginationRequests[dir] = promise; - } - else { - if (!room) { - throw new Error("Unknown room " + eventTimeline.getRoomId()); - } - promise = this.createMessagesRequest(eventTimeline.getRoomId(), token, opts.limit, dir, eventTimeline.getFilter()) - .then((res) => { - if (res.state) { - const roomState = eventTimeline.getState(dir); - const stateEvents = res.state.filter(utils_1.noUnsafeEventProps).map(this.getEventMapper()); - roomState.setUnknownStateEvents(stateEvents); - } - const token = res.end; - const matrixEvents = res.chunk.filter(utils_1.noUnsafeEventProps).map(this.getEventMapper()); - const timelineSet = eventTimeline.getTimelineSet(); - const [timelineEvents] = room.partitionThreadedEvents(matrixEvents); - timelineSet.addEventsToTimeline(timelineEvents, backwards, eventTimeline, token); - this.processAggregatedTimelineEvents(room, timelineEvents); - this.processThreadRoots(room, timelineEvents.filter((it) => it.getServerAggregatedRelation(thread_1.THREAD_RELATION_TYPE.name)), false); - const atEnd = res.end === undefined || res.end === res.start; - // if we've hit the end of the timeline, we need to stop trying to - // paginate. We need to keep the 'forwards' token though, to make sure - // we can recover from gappy syncs. - if (backwards && atEnd) { - eventTimeline.setPaginationToken(null, dir); - } - return !atEnd; - }) - .finally(() => { - eventTimeline.paginationRequests[dir] = null; - }); - eventTimeline.paginationRequests[dir] = promise; - } - return promise; - } - /** - * Reset the notifTimelineSet entirely, paginating in some historical notifs as - * a starting point for subsequent pagination. - */ - resetNotifTimelineSet() { - if (!this.notifTimelineSet) { - return; - } - // FIXME: This thing is a total hack, and results in duplicate events being - // added to the timeline both from /sync and /notifications, and lots of - // slow and wasteful processing and pagination. The correct solution is to - // extend /messages or /search or something to filter on notifications. - // use the fictitious token 'end'. in practice we would ideally give it - // the oldest backwards pagination token from /sync, but /sync doesn't - // know about /notifications, so we have no choice but to start paginating - // from the current point in time. This may well overlap with historical - // notifs which are then inserted into the timeline by /sync responses. - this.notifTimelineSet.resetLiveTimeline("end"); - // we could try to paginate a single event at this point in order to get - // a more valid pagination token, but it just ends up with an out of order - // timeline. given what a mess this is and given we're going to have duplicate - // events anyway, just leave it with the dummy token for now. - /* - this.paginateNotifTimeline(this._notifTimelineSet.getLiveTimeline(), { - backwards: true, - limit: 1 - }); - */ - } - /** - * Peek into a room and receive updates about the room. This only works if the - * history visibility for the room is world_readable. - * @param roomId - The room to attempt to peek into. - * @returns Promise which resolves: Room object - * @returns Rejects: with an error response. - */ - peekInRoom(roomId) { - var _a; - (_a = this.peekSync) === null || _a === void 0 ? void 0 : _a.stopPeeking(); - this.peekSync = new sync_1.SyncApi(this, this.clientOpts, this.buildSyncApiOptions()); - return this.peekSync.peek(roomId); - } - /** - * Stop any ongoing room peeking. - */ - stopPeeking() { - if (this.peekSync) { - this.peekSync.stopPeeking(); - this.peekSync = null; - } - } - /** - * Set r/w flags for guest access in a room. - * @param roomId - The room to configure guest access in. - * @param opts - Options - * @returns Promise which resolves - * @returns Rejects: with an error response. - */ - setGuestAccess(roomId, opts) { - const writePromise = this.sendStateEvent(roomId, event_2.EventType.RoomGuestAccess, { - guest_access: opts.allowJoin ? "can_join" : "forbidden", - }, ""); - let readPromise = Promise.resolve(undefined); - if (opts.allowRead) { - readPromise = this.sendStateEvent(roomId, event_2.EventType.RoomHistoryVisibility, { - history_visibility: "world_readable", - }, ""); - } - return Promise.all([readPromise, writePromise]).then(); // .then() to hide results for contract - } - /** - * Requests an email verification token for the purposes of registration. - * This API requests a token from the homeserver. - * The doesServerRequireIdServerParam() method can be used to determine if - * the server requires the id_server parameter to be provided. - * - * Parameters and return value are as for requestEmailToken - - * @param email - As requestEmailToken - * @param clientSecret - As requestEmailToken - * @param sendAttempt - As requestEmailToken - * @param nextLink - As requestEmailToken - * @returns Promise which resolves: As requestEmailToken - */ - requestRegisterEmailToken(email, clientSecret, sendAttempt, nextLink) { - return this.requestTokenFromEndpoint("/register/email/requestToken", { - email: email, - client_secret: clientSecret, - send_attempt: sendAttempt, - next_link: nextLink, - }); - } - /** - * Requests a text message verification token for the purposes of registration. - * This API requests a token from the homeserver. - * The doesServerRequireIdServerParam() method can be used to determine if - * the server requires the id_server parameter to be provided. - * - * @param phoneCountry - The ISO 3166-1 alpha-2 code for the country in which - * phoneNumber should be parsed relative to. - * @param phoneNumber - The phone number, in national or international format - * @param clientSecret - As requestEmailToken - * @param sendAttempt - As requestEmailToken - * @param nextLink - As requestEmailToken - * @returns Promise which resolves: As requestEmailToken - */ - requestRegisterMsisdnToken(phoneCountry, phoneNumber, clientSecret, sendAttempt, nextLink) { - return this.requestTokenFromEndpoint("/register/msisdn/requestToken", { - country: phoneCountry, - phone_number: phoneNumber, - client_secret: clientSecret, - send_attempt: sendAttempt, - next_link: nextLink, - }); - } - /** - * Requests an email verification token for the purposes of adding a - * third party identifier to an account. - * This API requests a token from the homeserver. - * The doesServerRequireIdServerParam() method can be used to determine if - * the server requires the id_server parameter to be provided. - * If an account with the given email address already exists and is - * associated with an account other than the one the user is authed as, - * it will either send an email to the address informing them of this - * or return M_THREEPID_IN_USE (which one is up to the homeserver). - * - * @param email - As requestEmailToken - * @param clientSecret - As requestEmailToken - * @param sendAttempt - As requestEmailToken - * @param nextLink - As requestEmailToken - * @returns Promise which resolves: As requestEmailToken - */ - requestAdd3pidEmailToken(email, clientSecret, sendAttempt, nextLink) { - return this.requestTokenFromEndpoint("/account/3pid/email/requestToken", { - email: email, - client_secret: clientSecret, - send_attempt: sendAttempt, - next_link: nextLink, - }); - } - /** - * Requests a text message verification token for the purposes of adding a - * third party identifier to an account. - * This API proxies the identity server /validate/email/requestToken API, - * adding specific behaviour for the addition of phone numbers to an - * account, as requestAdd3pidEmailToken. - * - * @param phoneCountry - As requestRegisterMsisdnToken - * @param phoneNumber - As requestRegisterMsisdnToken - * @param clientSecret - As requestEmailToken - * @param sendAttempt - As requestEmailToken - * @param nextLink - As requestEmailToken - * @returns Promise which resolves: As requestEmailToken - */ - requestAdd3pidMsisdnToken(phoneCountry, phoneNumber, clientSecret, sendAttempt, nextLink) { - return this.requestTokenFromEndpoint("/account/3pid/msisdn/requestToken", { - country: phoneCountry, - phone_number: phoneNumber, - client_secret: clientSecret, - send_attempt: sendAttempt, - next_link: nextLink, - }); - } - /** - * Requests an email verification token for the purposes of resetting - * the password on an account. - * This API proxies the identity server /validate/email/requestToken API, - * adding specific behaviour for the password resetting. Specifically, - * if no account with the given email address exists, it may either - * return M_THREEPID_NOT_FOUND or send an email - * to the address informing them of this (which one is up to the homeserver). - * - * requestEmailToken calls the equivalent API directly on the identity server, - * therefore bypassing the password reset specific logic. - * - * @param email - As requestEmailToken - * @param clientSecret - As requestEmailToken - * @param sendAttempt - As requestEmailToken - * @param nextLink - As requestEmailToken - * @returns Promise which resolves: As requestEmailToken - */ - requestPasswordEmailToken(email, clientSecret, sendAttempt, nextLink) { - return this.requestTokenFromEndpoint("/account/password/email/requestToken", { - email: email, - client_secret: clientSecret, - send_attempt: sendAttempt, - next_link: nextLink, - }); - } - /** - * Requests a text message verification token for the purposes of resetting - * the password on an account. - * This API proxies the identity server /validate/email/requestToken API, - * adding specific behaviour for the password resetting, as requestPasswordEmailToken. - * - * @param phoneCountry - As requestRegisterMsisdnToken - * @param phoneNumber - As requestRegisterMsisdnToken - * @param clientSecret - As requestEmailToken - * @param sendAttempt - As requestEmailToken - * @param nextLink - As requestEmailToken - * @returns Promise which resolves: As requestEmailToken - */ - requestPasswordMsisdnToken(phoneCountry, phoneNumber, clientSecret, sendAttempt, nextLink) { - return this.requestTokenFromEndpoint("/account/password/msisdn/requestToken", { - country: phoneCountry, - phone_number: phoneNumber, - client_secret: clientSecret, - send_attempt: sendAttempt, - next_link: nextLink, - }); - } - /** - * Internal utility function for requesting validation tokens from usage-specific - * requestToken endpoints. - * - * @param endpoint - The endpoint to send the request to - * @param params - Parameters for the POST request - * @returns Promise which resolves: As requestEmailToken - */ - requestTokenFromEndpoint(endpoint, params) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const postParams = Object.assign({}, params); - // If the HS supports separate add and bind, then requestToken endpoints - // don't need an IS as they are all validated by the HS directly. - if (!(yield this.doesServerSupportSeparateAddAndBind()) && this.idBaseUrl) { - const idServerUrl = new URL(this.idBaseUrl); - postParams.id_server = idServerUrl.host; - if (((_a = this.identityServer) === null || _a === void 0 ? void 0 : _a.getAccessToken) && (yield this.doesServerAcceptIdentityAccessToken())) { - const identityAccessToken = yield this.identityServer.getAccessToken(); - if (identityAccessToken) { - postParams.id_access_token = identityAccessToken; - } - } - } - return this.http.request(http_api_1.Method.Post, endpoint, undefined, postParams); - }); - } - /** - * Get the room-kind push rule associated with a room. - * @param scope - "global" or device-specific. - * @param roomId - the id of the room. - * @returns the rule or undefined. - */ - getRoomPushRule(scope, roomId) { - var _a, _b; - // There can be only room-kind push rule per room - // and its id is the room id. - if (this.pushRules) { - return (_b = (_a = this.pushRules[scope]) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.find((rule) => rule.rule_id === roomId); - } - else { - throw new Error("SyncApi.sync() must be done before accessing to push rules."); - } - } - /** - * Set a room-kind muting push rule in a room. - * The operation also updates MatrixClient.pushRules at the end. - * @param scope - "global" or device-specific. - * @param roomId - the id of the room. - * @param mute - the mute state. - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - setRoomMutePushRule(scope, roomId, mute) { - let promise; - let hasDontNotifyRule = false; - // Get the existing room-kind push rule if any - const roomPushRule = this.getRoomPushRule(scope, roomId); - if (roomPushRule === null || roomPushRule === void 0 ? void 0 : roomPushRule.actions.includes(PushRules_1.PushRuleActionName.DontNotify)) { - hasDontNotifyRule = true; - } - if (!mute) { - // Remove the rule only if it is a muting rule - if (hasDontNotifyRule) { - promise = this.deletePushRule(scope, PushRules_1.PushRuleKind.RoomSpecific, roomPushRule.rule_id); - } - } - else { - if (!roomPushRule) { - promise = this.addPushRule(scope, PushRules_1.PushRuleKind.RoomSpecific, roomId, { - actions: [PushRules_1.PushRuleActionName.DontNotify], - }); - } - else if (!hasDontNotifyRule) { - // Remove the existing one before setting the mute push rule - // This is a workaround to SYN-590 (Push rule update fails) - const deferred = utils.defer(); - this.deletePushRule(scope, PushRules_1.PushRuleKind.RoomSpecific, roomPushRule.rule_id) - .then(() => { - this.addPushRule(scope, PushRules_1.PushRuleKind.RoomSpecific, roomId, { - actions: [PushRules_1.PushRuleActionName.DontNotify], - }) - .then(() => { - deferred.resolve(); - }) - .catch((err) => { - deferred.reject(err); - }); - }) - .catch((err) => { - deferred.reject(err); - }); - promise = deferred.promise; - } - } - if (promise) { - return new Promise((resolve, reject) => { - // Update this.pushRules when the operation completes - promise - .then(() => { - this.getPushRules() - .then((result) => { - this.pushRules = result; - resolve(); - }) - .catch((err) => { - reject(err); - }); - }) - .catch((err) => { - // Update it even if the previous operation fails. This can help the - // app to recover when push settings has been modified from another client - this.getPushRules() - .then((result) => { - this.pushRules = result; - reject(err); - }) - .catch((err2) => { - reject(err); - }); - }); - }); - } - } - searchMessageText(opts) { - const roomEvents = { - search_term: opts.query, - }; - if ("keys" in opts) { - roomEvents.keys = opts.keys; - } - return this.search({ - body: { - search_categories: { - room_events: roomEvents, - }, - }, - }); - } - /** - * Perform a server-side search for room events. - * - * The returned promise resolves to an object containing the fields: - * - * * count: estimate of the number of results - * * next_batch: token for back-pagination; if undefined, there are no more results - * * highlights: a list of words to highlight from the stemming algorithm - * * results: a list of results - * - * Each entry in the results list is a SearchResult. - * - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - searchRoomEvents(opts) { - // TODO: support search groups - const body = { - search_categories: { - room_events: { - search_term: opts.term, - filter: opts.filter, - order_by: search_1.SearchOrderBy.Recent, - event_context: { - before_limit: 1, - after_limit: 1, - include_profile: true, - }, - }, - }, - }; - const searchResults = { - _query: body, - results: [], - highlights: [], - }; - return this.search({ body: body }).then((res) => this.processRoomEventsSearch(searchResults, res)); - } - /** - * Take a result from an earlier searchRoomEvents call, and backfill results. - * - * @param searchResults - the results object to be updated - * @returns Promise which resolves: updated result object - * @returns Rejects: with an error response. - */ - backPaginateRoomEventsSearch(searchResults) { - // TODO: we should implement a backoff (as per scrollback()) to deal more - // nicely with HTTP errors. - if (!searchResults.next_batch) { - return Promise.reject(new Error("Cannot backpaginate event search any further")); - } - if (searchResults.pendingRequest) { - // already a request in progress - return the existing promise - return searchResults.pendingRequest; - } - const searchOpts = { - body: searchResults._query, - next_batch: searchResults.next_batch, - }; - const promise = this.search(searchOpts, searchResults.abortSignal) - .then((res) => this.processRoomEventsSearch(searchResults, res)) - .finally(() => { - searchResults.pendingRequest = undefined; - }); - searchResults.pendingRequest = promise; - return promise; - } - /** - * helper for searchRoomEvents and backPaginateRoomEventsSearch. Processes the - * response from the API call and updates the searchResults - * - * @returns searchResults - * @internal - */ - // XXX: Intended private, used in code - processRoomEventsSearch(searchResults, response) { - var _a, _b; - const roomEvents = response.search_categories.room_events; - searchResults.count = roomEvents.count; - searchResults.next_batch = roomEvents.next_batch; - // combine the highlight list with our existing list; - const highlights = new Set(roomEvents.highlights); - searchResults.highlights.forEach((hl) => { - highlights.add(hl); - }); - // turn it back into a list. - searchResults.highlights = Array.from(highlights); - const mapper = this.getEventMapper(); - // append the new results to our existing results - const resultsLength = (_b = (_a = roomEvents.results) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0; - for (let i = 0; i < resultsLength; i++) { - const sr = search_result_1.SearchResult.fromJson(roomEvents.results[i], mapper); - const room = this.getRoom(sr.context.getEvent().getRoomId()); - if (room) { - // Copy over a known event sender if we can - for (const ev of sr.context.getTimeline()) { - const sender = room.getMember(ev.getSender()); - if (!ev.sender && sender) - ev.sender = sender; - } - } - searchResults.results.push(sr); - } - return searchResults; - } - /** - * Populate the store with rooms the user has left. - * @returns Promise which resolves: TODO - Resolved when the rooms have - * been added to the data store. - * @returns Rejects: with an error response. - */ - syncLeftRooms() { - // Guard against multiple calls whilst ongoing and multiple calls post success - if (this.syncedLeftRooms) { - return Promise.resolve([]); // don't call syncRooms again if it succeeded. - } - if (this.syncLeftRoomsPromise) { - return this.syncLeftRoomsPromise; // return the ongoing request - } - const syncApi = new sync_1.SyncApi(this, this.clientOpts, this.buildSyncApiOptions()); - this.syncLeftRoomsPromise = syncApi.syncLeftRooms(); - // cleanup locks - this.syncLeftRoomsPromise - .then(() => { - logger_1.logger.log("Marking success of sync left room request"); - this.syncedLeftRooms = true; // flip the bit on success - }) - .finally(() => { - this.syncLeftRoomsPromise = undefined; // cleanup ongoing request state - }); - return this.syncLeftRoomsPromise; - } - /** - * Create a new filter. - * @param content - The HTTP body for the request - * @returns Promise which resolves to a Filter object. - * @returns Rejects: with an error response. - */ - createFilter(content) { - const path = utils.encodeUri("/user/$userId/filter", { - $userId: this.credentials.userId, - }); - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, content).then((response) => { - // persist the filter - const filter = filter_1.Filter.fromJson(this.credentials.userId, response.filter_id, content); - this.store.storeFilter(filter); - return filter; - }); - } - /** - * Retrieve a filter. - * @param userId - The user ID of the filter owner - * @param filterId - The filter ID to retrieve - * @param allowCached - True to allow cached filters to be returned. - * Default: True. - * @returns Promise which resolves: a Filter object - * @returns Rejects: with an error response. - */ - getFilter(userId, filterId, allowCached) { - if (allowCached) { - const filter = this.store.getFilter(userId, filterId); - if (filter) { - return Promise.resolve(filter); - } - } - const path = utils.encodeUri("/user/$userId/filter/$filterId", { - $userId: userId, - $filterId: filterId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path).then((response) => { - // persist the filter - const filter = filter_1.Filter.fromJson(userId, filterId, response); - this.store.storeFilter(filter); - return filter; - }); - } - /** - * @returns Filter ID - */ - getOrCreateFilter(filterName, filter) { - return __awaiter(this, void 0, void 0, function* () { - const filterId = this.store.getFilterIdByName(filterName); - let existingId; - if (filterId) { - // check that the existing filter matches our expectations - try { - const existingFilter = yield this.getFilter(this.credentials.userId, filterId, true); - if (existingFilter) { - const oldDef = existingFilter.getDefinition(); - const newDef = filter.getDefinition(); - if (utils.deepCompare(oldDef, newDef)) { - // super, just use that. - // debuglog("Using existing filter ID %s: %s", filterId, - // JSON.stringify(oldDef)); - existingId = filterId; - } - } - } - catch (error) { - // Synapse currently returns the following when the filter cannot be found: - // { - // errcode: "M_UNKNOWN", - // name: "M_UNKNOWN", - // message: "No row found", - // } - if (error.errcode !== "M_UNKNOWN" && error.errcode !== "M_NOT_FOUND") { - throw error; - } - } - // if the filter doesn't exist anymore on the server, remove from store - if (!existingId) { - this.store.setFilterIdByName(filterName, undefined); - } - } - if (existingId) { - return existingId; - } - // create a new filter - const createdFilter = yield this.createFilter(filter.getDefinition()); - this.store.setFilterIdByName(filterName, createdFilter.filterId); - return createdFilter.filterId; - }); - } - /** - * Gets a bearer token from the homeserver that the user can - * present to a third party in order to prove their ownership - * of the Matrix account they are logged into. - * @returns Promise which resolves: Token object - * @returns Rejects: with an error response. - */ - getOpenIdToken() { - const path = utils.encodeUri("/user/$userId/openid/request_token", { - $userId: this.credentials.userId, - }); - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, {}); - } - /** - * @returns Promise which resolves: ITurnServerResponse object - * @returns Rejects: with an error response. - */ - turnServer() { - return this.http.authedRequest(http_api_1.Method.Get, "/voip/turnServer"); - } - /** - * Get the TURN servers for this homeserver. - * @returns The servers or an empty list. - */ - getTurnServers() { - return this.turnServers || []; - } - /** - * Get the unix timestamp (in milliseconds) at which the current - * TURN credentials (from getTurnServers) expire - * @returns The expiry timestamp in milliseconds - */ - getTurnServersExpiry() { - return this.turnServersExpiry; - } - get pollingTurnServers() { - return this.checkTurnServersIntervalID !== undefined; - } - // XXX: Intended private, used in code. - checkTurnServers() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.canSupportVoip) { - return; - } - let credentialsGood = false; - const remainingTime = this.turnServersExpiry - Date.now(); - if (remainingTime > TURN_CHECK_INTERVAL) { - logger_1.logger.debug("TURN creds are valid for another " + remainingTime + " ms: not fetching new ones."); - credentialsGood = true; - } - else { - logger_1.logger.debug("Fetching new TURN credentials"); - try { - const res = yield this.turnServer(); - if (res.uris) { - logger_1.logger.log("Got TURN URIs: " + res.uris + " refresh in " + res.ttl + " secs"); - // map the response to a format that can be fed to RTCPeerConnection - const servers = { - urls: res.uris, - username: res.username, - credential: res.password, - }; - this.turnServers = [servers]; - // The TTL is in seconds but we work in ms - this.turnServersExpiry = Date.now() + res.ttl * 1000; - credentialsGood = true; - this.emit(ClientEvent.TurnServers, this.turnServers); - } - } - catch (err) { - logger_1.logger.error("Failed to get TURN URIs", err); - if (err.httpStatus === 403) { - // We got a 403, so there's no point in looping forever. - logger_1.logger.info("TURN access unavailable for this account: stopping credentials checks"); - if (this.checkTurnServersIntervalID !== null) - global.clearInterval(this.checkTurnServersIntervalID); - this.checkTurnServersIntervalID = undefined; - this.emit(ClientEvent.TurnServersError, err, true); // fatal - } - else { - // otherwise, if we failed for whatever reason, try again the next time we're called. - this.emit(ClientEvent.TurnServersError, err, false); // non-fatal - } - } - } - return credentialsGood; - }); - } - /** - * Set whether to allow a fallback ICE server should be used for negotiating a - * WebRTC connection if the homeserver doesn't provide any servers. Defaults to - * false. - * - */ - setFallbackICEServerAllowed(allow) { - this.fallbackICEServerAllowed = allow; - } - /** - * Get whether to allow a fallback ICE server should be used for negotiating a - * WebRTC connection if the homeserver doesn't provide any servers. Defaults to - * false. - * - * @returns - */ - isFallbackICEServerAllowed() { - return this.fallbackICEServerAllowed; - } - /** - * Determines if the current user is an administrator of the Synapse homeserver. - * Returns false if untrue or the homeserver does not appear to be a Synapse - * homeserver. This function is implementation specific and may change - * as a result. - * @returns true if the user appears to be a Synapse administrator. - */ - isSynapseAdministrator() { - const path = utils.encodeUri("/_synapse/admin/v1/users/$userId/admin", { $userId: this.getUserId() }); - return this.http - .authedRequest(http_api_1.Method.Get, path, undefined, undefined, { prefix: "" }) - .then((r) => r.admin); // pull out the specific boolean we want - } - /** - * Performs a whois lookup on a user using Synapse's administrator API. - * This function is implementation specific and may change as a - * result. - * @param userId - the User ID to look up. - * @returns the whois response - see Synapse docs for information. - */ - whoisSynapseUser(userId) { - const path = utils.encodeUri("/_synapse/admin/v1/whois/$userId", { $userId: userId }); - return this.http.authedRequest(http_api_1.Method.Get, path, undefined, undefined, { prefix: "" }); - } - /** - * Deactivates a user using Synapse's administrator API. This - * function is implementation specific and may change as a result. - * @param userId - the User ID to deactivate. - * @returns the deactivate response - see Synapse docs for information. - */ - deactivateSynapseUser(userId) { - const path = utils.encodeUri("/_synapse/admin/v1/deactivate/$userId", { $userId: userId }); - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, undefined, { prefix: "" }); - } - fetchClientWellKnown() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // `getRawClientConfig` does not throw or reject on network errors, instead - // it absorbs errors and returns `{}`. - this.clientWellKnownPromise = autodiscovery_1.AutoDiscovery.getRawClientConfig((_a = this.getDomain()) !== null && _a !== void 0 ? _a : undefined); - this.clientWellKnown = yield this.clientWellKnownPromise; - this.emit(ClientEvent.ClientWellKnown, this.clientWellKnown); - }); - } - getClientWellKnown() { - return this.clientWellKnown; - } - waitForClientWellKnown() { - if (!this.clientRunning) { - throw new Error("Client is not running"); - } - return this.clientWellKnownPromise; - } - /** - * store client options with boolean/string/numeric values - * to know in the next session what flags the sync data was - * created with (e.g. lazy loading) - * @param opts - the complete set of client options - * @returns for store operation - */ - storeClientOptions() { - // XXX: Intended private, used in code - const primTypes = ["boolean", "string", "number"]; - const serializableOpts = Object.entries(this.clientOpts) - .filter(([key, value]) => { - return primTypes.includes(typeof value); - }) - .reduce((obj, [key, value]) => { - obj[key] = value; - return obj; - }, {}); - return this.store.storeClientOptions(serializableOpts); - } - /** - * Gets a set of room IDs in common with another user - * @param userId - The userId to check. - * @returns Promise which resolves to a set of rooms - * @returns Rejects: with an error response. - */ - // eslint-disable-next-line - _unstable_getSharedRooms(userId) { - return __awaiter(this, void 0, void 0, function* () { - const sharedRoomsSupport = yield this.doesServerSupportUnstableFeature("uk.half-shot.msc2666"); - const mutualRoomsSupport = yield this.doesServerSupportUnstableFeature("uk.half-shot.msc2666.mutual_rooms"); - if (!sharedRoomsSupport && !mutualRoomsSupport) { - throw Error("Server does not support mutual_rooms API"); - } - const path = utils.encodeUri(`/uk.half-shot.msc2666/user/${mutualRoomsSupport ? "mutual_rooms" : "shared_rooms"}/$userId`, { $userId: userId }); - const res = yield this.http.authedRequest(http_api_1.Method.Get, path, undefined, undefined, { - prefix: http_api_1.ClientPrefix.Unstable, - }); - return res.joined; - }); - } - /** - * Get the API versions supported by the server, along with any - * unstable APIs it supports - * @returns The server /versions response - */ - getVersions() { - return __awaiter(this, void 0, void 0, function* () { - if (this.serverVersionsPromise) { - return this.serverVersionsPromise; - } - this.serverVersionsPromise = this.http - .request(http_api_1.Method.Get, "/_matrix/client/versions", undefined, // queryParams - undefined, // data - { - prefix: "", - }) - .catch((e) => { - // Need to unset this if it fails, otherwise we'll never retry - this.serverVersionsPromise = undefined; - // but rethrow the exception to anything that was waiting - throw e; - }); - const serverVersions = yield this.serverVersionsPromise; - this.canSupport = yield (0, feature_1.buildFeatureSupportMap)(serverVersions); - return this.serverVersionsPromise; - }); - } - /** - * Check if a particular spec version is supported by the server. - * @param version - The spec version (such as "r0.5.0") to check for. - * @returns Whether it is supported - */ - isVersionSupported(version) { - return __awaiter(this, void 0, void 0, function* () { - const { versions } = yield this.getVersions(); - return versions && versions.includes(version); - }); - } - /** - * Query the server to see if it supports members lazy loading - * @returns true if server supports lazy loading - */ - doesServerSupportLazyLoading() { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.getVersions(); - if (!response) - return false; - const versions = response["versions"]; - const unstableFeatures = response["unstable_features"]; - return ((versions && versions.includes("r0.5.0")) || (unstableFeatures && unstableFeatures["m.lazy_load_members"])); - }); - } - /** - * Query the server to see if the `id_server` parameter is required - * when registering with an 3pid, adding a 3pid or resetting password. - * @returns true if id_server parameter is required - */ - doesServerRequireIdServerParam() { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.getVersions(); - if (!response) - return true; - const versions = response["versions"]; - // Supporting r0.6.0 is the same as having the flag set to false - if (versions && versions.includes("r0.6.0")) { - return false; - } - const unstableFeatures = response["unstable_features"]; - if (!unstableFeatures) - return true; - if (unstableFeatures["m.require_identity_server"] === undefined) { - return true; - } - else { - return unstableFeatures["m.require_identity_server"]; - } - }); - } - /** - * Query the server to see if the `id_access_token` parameter can be safely - * passed to the homeserver. Some homeservers may trigger errors if they are not - * prepared for the new parameter. - * @returns true if id_access_token can be sent - */ - doesServerAcceptIdentityAccessToken() { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.getVersions(); - if (!response) - return false; - const versions = response["versions"]; - const unstableFeatures = response["unstable_features"]; - return (versions && versions.includes("r0.6.0")) || (unstableFeatures && unstableFeatures["m.id_access_token"]); - }); - } - /** - * Query the server to see if it supports separate 3PID add and bind functions. - * This affects the sequence of API calls clients should use for these operations, - * so it's helpful to be able to check for support. - * @returns true if separate functions are supported - */ - doesServerSupportSeparateAddAndBind() { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.getVersions(); - if (!response) - return false; - const versions = response["versions"]; - const unstableFeatures = response["unstable_features"]; - return (versions === null || versions === void 0 ? void 0 : versions.includes("r0.6.0")) || (unstableFeatures === null || unstableFeatures === void 0 ? void 0 : unstableFeatures["m.separate_add_and_bind"]); - }); - } - /** - * Query the server to see if it lists support for an unstable feature - * in the /versions response - * @param feature - the feature name - * @returns true if the feature is supported - */ - doesServerSupportUnstableFeature(feature) { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.getVersions(); - if (!response) - return false; - const unstableFeatures = response["unstable_features"]; - return unstableFeatures && !!unstableFeatures[feature]; - }); - } - /** - * Query the server to see if it is forcing encryption to be enabled for - * a given room preset, based on the /versions response. - * @param presetName - The name of the preset to check. - * @returns true if the server is forcing encryption - * for the preset. - */ - doesServerForceEncryptionForPreset(presetName) { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.getVersions(); - if (!response) - return false; - const unstableFeatures = response["unstable_features"]; - // The preset name in the versions response will be without the _chat suffix. - const versionsPresetName = presetName.includes("_chat") - ? presetName.substring(0, presetName.indexOf("_chat")) - : presetName; - return unstableFeatures && !!unstableFeatures[`io.element.e2ee_forced.${versionsPresetName}`]; - }); - } - doesServerSupportThread() { - return __awaiter(this, void 0, void 0, function* () { - if (yield this.isVersionSupported("v1.4")) { - return { - threads: thread_1.FeatureSupport.Stable, - list: thread_1.FeatureSupport.Stable, - fwdPagination: thread_1.FeatureSupport.Stable, - }; - } - try { - const [threadUnstable, threadStable, listUnstable, listStable, fwdPaginationUnstable, fwdPaginationStable] = yield Promise.all([ - this.doesServerSupportUnstableFeature("org.matrix.msc3440"), - this.doesServerSupportUnstableFeature("org.matrix.msc3440.stable"), - this.doesServerSupportUnstableFeature("org.matrix.msc3856"), - this.doesServerSupportUnstableFeature("org.matrix.msc3856.stable"), - this.doesServerSupportUnstableFeature("org.matrix.msc3715"), - this.doesServerSupportUnstableFeature("org.matrix.msc3715.stable"), - ]); - return { - threads: (0, thread_1.determineFeatureSupport)(threadStable, threadUnstable), - list: (0, thread_1.determineFeatureSupport)(listStable, listUnstable), - fwdPagination: (0, thread_1.determineFeatureSupport)(fwdPaginationStable, fwdPaginationUnstable), - }; - } - catch (e) { - return { - threads: thread_1.FeatureSupport.None, - list: thread_1.FeatureSupport.None, - fwdPagination: thread_1.FeatureSupport.None, - }; - } - }); - } - /** - * Query the server to see if it supports the MSC2457 `logout_devices` parameter when setting password - * @returns true if server supports the `logout_devices` parameter - */ - doesServerSupportLogoutDevices() { - return this.isVersionSupported("r0.6.1"); - } - /** - * Get if lazy loading members is being used. - * @returns Whether or not members are lazy loaded by this client - */ - hasLazyLoadMembersEnabled() { - var _a; - return !!((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.lazyLoadMembers); - } - /** - * Set a function which is called when /sync returns a 'limited' response. - * It is called with a room ID and returns a boolean. It should return 'true' if the SDK - * can SAFELY remove events from this room. It may not be safe to remove events if there - * are other references to the timelines for this room, e.g because the client is - * actively viewing events in this room. - * Default: returns false. - * @param cb - The callback which will be invoked. - */ - setCanResetTimelineCallback(cb) { - this.canResetTimelineCallback = cb; - } - /** - * Get the callback set via `setCanResetTimelineCallback`. - * @returns The callback or null - */ - getCanResetTimelineCallback() { - return this.canResetTimelineCallback; - } - /** - * Returns relations for a given event. Handles encryption transparently, - * with the caveat that the amount of events returned might be 0, even though you get a nextBatch. - * When the returned promise resolves, all messages should have finished trying to decrypt. - * @param roomId - the room of the event - * @param eventId - the id of the event - * @param relationType - the rel_type of the relations requested - * @param eventType - the event type of the relations requested - * @param opts - options with optional values for the request. - * @returns an object with `events` as `MatrixEvent[]` and optionally `nextBatch` if more relations are available. - */ - relations(roomId, eventId, relationType, eventType, opts = { dir: event_timeline_1.Direction.Backward }) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - const fetchedEventType = eventType ? this.getEncryptedIfNeededEventType(roomId, eventType) : null; - const [eventResult, result] = yield Promise.all([ - this.fetchRoomEvent(roomId, eventId), - this.fetchRelations(roomId, eventId, relationType, fetchedEventType, opts), - ]); - const mapper = this.getEventMapper(); - const originalEvent = eventResult ? mapper(eventResult) : undefined; - let events = result.chunk.map(mapper); - if (fetchedEventType === event_2.EventType.RoomMessageEncrypted) { - const allEvents = originalEvent ? events.concat(originalEvent) : events; - yield Promise.all(allEvents.map((e) => this.decryptEventIfNeeded(e))); - if (eventType !== null) { - events = events.filter((e) => e.getType() === eventType); - } - } - if (originalEvent && relationType === event_2.RelationType.Replace) { - events = events.filter((e) => e.getSender() === originalEvent.getSender()); - } - return { - originalEvent: originalEvent !== null && originalEvent !== void 0 ? originalEvent : null, - events, - nextBatch: (_a = result.next_batch) !== null && _a !== void 0 ? _a : null, - prevBatch: (_b = result.prev_batch) !== null && _b !== void 0 ? _b : null, - }; - }); - } - /** - * The app may wish to see if we have a key cached without - * triggering a user interaction. - */ - getCrossSigningCacheCallbacks() { - var _a; - // XXX: Private member access - return (_a = this.crypto) === null || _a === void 0 ? void 0 : _a.crossSigningInfo.getCacheCallbacks(); - } - /** - * Generates a random string suitable for use as a client secret. This - * method is experimental and may change. - * @returns A new client secret - */ - generateClientSecret() { - return (0, randomstring_1.randomString)(32); - } - /** - * Attempts to decrypt an event - * @param event - The event to decrypt - * @returns A decryption promise - */ - decryptEventIfNeeded(event, options) { - if (event.shouldAttemptDecryption() && this.isCryptoEnabled()) { - event.attemptDecryption(this.cryptoBackend, options); - } - if (event.isBeingDecrypted()) { - return event.getDecryptionPromise(); - } - else { - return Promise.resolve(); - } - } - termsUrlForService(serviceType, baseUrl) { - switch (serviceType) { - case service_types_1.SERVICE_TYPES.IS: - return this.http.getUrl("/terms", undefined, http_api_1.IdentityPrefix.V2, baseUrl); - case service_types_1.SERVICE_TYPES.IM: - return this.http.getUrl("/terms", undefined, "/_matrix/integrations/v1", baseUrl); - default: - throw new Error("Unsupported service type"); - } - } - /** - * Get the Homeserver URL of this client - * @returns Homeserver URL of this client - */ - getHomeserverUrl() { - return this.baseUrl; - } - /** - * Get the identity server URL of this client - * @param stripProto - whether or not to strip the protocol from the URL - * @returns Identity server URL of this client - */ - getIdentityServerUrl(stripProto = false) { - var _a, _b; - if (stripProto && (((_a = this.idBaseUrl) === null || _a === void 0 ? void 0 : _a.startsWith("http://")) || ((_b = this.idBaseUrl) === null || _b === void 0 ? void 0 : _b.startsWith("https://")))) { - return this.idBaseUrl.split("://")[1]; - } - return this.idBaseUrl; - } - /** - * Set the identity server URL of this client - * @param url - New identity server URL - */ - setIdentityServerUrl(url) { - this.idBaseUrl = utils.ensureNoTrailingSlash(url); - this.http.setIdBaseUrl(this.idBaseUrl); - } - /** - * Get the access token associated with this account. - * @returns The access_token or null - */ - getAccessToken() { - return this.http.opts.accessToken || null; - } - /** - * Set the access token associated with this account. - * @param token - The new access token. - */ - setAccessToken(token) { - this.http.opts.accessToken = token; - } - /** - * @returns true if there is a valid access_token for this client. - */ - isLoggedIn() { - return this.http.opts.accessToken !== undefined; - } - /** - * Make up a new transaction id - * - * @returns a new, unique, transaction id - */ - makeTxnId() { - return "m" + new Date().getTime() + "." + this.txnCtr++; - } - /** - * Check whether a username is available prior to registration. An error response - * indicates an invalid/unavailable username. - * @param username - The username to check the availability of. - * @returns Promise which resolves: to boolean of whether the username is available. - */ - isUsernameAvailable(username) { - return this.http - .authedRequest(http_api_1.Method.Get, "/register/available", { username }) - .then((response) => { - return response.available; - }) - .catch((response) => { - if (response.errcode === "M_USER_IN_USE") { - return false; - } - return Promise.reject(response); - }); - } - /** - * @param bindThreepids - Set key 'email' to true to bind any email - * threepid uses during registration in the identity server. Set 'msisdn' to - * true to bind msisdn. - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - register(username, password, sessionId, auth, bindThreepids, guestAccessToken, inhibitLogin) { - // backwards compat - if (bindThreepids === true) { - bindThreepids = { email: true }; - } - else if (bindThreepids === null || bindThreepids === undefined || bindThreepids === false) { - bindThreepids = {}; - } - if (sessionId) { - auth.session = sessionId; - } - const params = { - auth: auth, - refresh_token: true, // always ask for a refresh token - does nothing if unsupported - }; - if (username !== undefined && username !== null) { - params.username = username; - } - if (password !== undefined && password !== null) { - params.password = password; - } - if (bindThreepids.email) { - params.bind_email = true; - } - if (bindThreepids.msisdn) { - params.bind_msisdn = true; - } - if (guestAccessToken !== undefined && guestAccessToken !== null) { - params.guest_access_token = guestAccessToken; - } - if (inhibitLogin !== undefined && inhibitLogin !== null) { - params.inhibit_login = inhibitLogin; - } - // Temporary parameter added to make the register endpoint advertise - // msisdn flows. This exists because there are clients that break - // when given stages they don't recognise. This parameter will cease - // to be necessary once these old clients are gone. - // Only send it if we send any params at all (the password param is - // mandatory, so if we send any params, we'll send the password param) - if (password !== undefined && password !== null) { - params.x_show_msisdn = true; - } - return this.registerRequest(params); - } - /** - * Register a guest account. - * This method returns the auth info needed to create a new authenticated client, - * Remember to call `setGuest(true)` on the (guest-)authenticated client, e.g: - * ```javascript - * const tmpClient = await sdk.createClient(MATRIX_INSTANCE); - * const { user_id, device_id, access_token } = tmpClient.registerGuest(); - * const client = createClient({ - * baseUrl: MATRIX_INSTANCE, - * accessToken: access_token, - * userId: user_id, - * deviceId: device_id, - * }) - * client.setGuest(true); - * ``` - * - * @param body - JSON HTTP body to provide. - * @returns Promise which resolves: JSON object that contains: - * `{ user_id, device_id, access_token, home_server }` - * @returns Rejects: with an error response. - */ - registerGuest({ body } = {}) { - // TODO: Types - return this.registerRequest(body || {}, "guest"); - } - /** - * @param data - parameters for registration request - * @param kind - type of user to register. may be "guest" - * @returns Promise which resolves: to the /register response - * @returns Rejects: with an error response. - */ - registerRequest(data, kind) { - const params = {}; - if (kind) { - params.kind = kind; - } - return this.http.request(http_api_1.Method.Post, "/register", params, data); - } - /** - * Refreshes an access token using a provided refresh token. The refresh token - * must be valid for the current access token known to the client instance. - * - * Note that this function will not cause a logout if the token is deemed - * unknown by the server - the caller is responsible for managing logout - * actions on error. - * @param refreshToken - The refresh token. - * @returns Promise which resolves to the new token. - * @returns Rejects with an error response. - */ - refreshToken(refreshToken) { - return this.http.authedRequest(http_api_1.Method.Post, "/refresh", undefined, { refresh_token: refreshToken }, { - prefix: http_api_1.ClientPrefix.V1, - inhibitLogoutEmit: true, // we don't want to cause logout loops - }); - } - /** - * @returns Promise which resolves to the available login flows - * @returns Rejects: with an error response. - */ - loginFlows() { - return this.http.request(http_api_1.Method.Get, "/login"); - } - /** - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - login(loginType, data) { - // TODO: Types - const loginData = { - type: loginType, - }; - // merge data into loginData - Object.assign(loginData, data); - return this.http - .authedRequest(http_api_1.Method.Post, "/login", undefined, loginData) - .then((response) => { - if (response.access_token && response.user_id) { - this.http.opts.accessToken = response.access_token; - this.credentials = { - userId: response.user_id, - }; - } - return response; - }); - } - /** - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - loginWithPassword(user, password) { - // TODO: Types - return this.login("m.login.password", { - user: user, - password: password, - }); - } - /** - * @param relayState - URL Callback after SAML2 Authentication - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - loginWithSAML2(relayState) { - // TODO: Types - return this.login("m.login.saml2", { - relay_state: relayState, - }); - } - /** - * @param redirectUrl - The URL to redirect to after the HS - * authenticates with CAS. - * @returns The HS URL to hit to begin the CAS login process. - */ - getCasLoginUrl(redirectUrl) { - return this.getSsoLoginUrl(redirectUrl, "cas"); - } - /** - * @param redirectUrl - The URL to redirect to after the HS - * authenticates with the SSO. - * @param loginType - The type of SSO login we are doing (sso or cas). - * Defaults to 'sso'. - * @param idpId - The ID of the Identity Provider being targeted, optional. - * @param action - the SSO flow to indicate to the IdP, optional. - * @returns The HS URL to hit to begin the SSO login process. - */ - getSsoLoginUrl(redirectUrl, loginType = "sso", idpId, action) { - let url = "/login/" + loginType + "/redirect"; - if (idpId) { - url += "/" + idpId; - } - const params = { - redirectUrl, - [SSO_ACTION_PARAM.unstable]: action, - }; - return this.http.getUrl(url, params, http_api_1.ClientPrefix.R0).href; - } - /** - * @param token - Login token previously received from homeserver - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - loginWithToken(token) { - // TODO: Types - return this.login("m.login.token", { - token: token, - }); - } - /** - * Logs out the current session. - * Obviously, further calls that require authorisation should fail after this - * method is called. The state of the MatrixClient object is not affected: - * it is up to the caller to either reset or destroy the MatrixClient after - * this method succeeds. - * @param stopClient - whether to stop the client before calling /logout to prevent invalid token errors. - * @returns Promise which resolves: On success, the empty object `{}` - */ - logout(stopClient = false) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - if ((_b = (_a = this.crypto) === null || _a === void 0 ? void 0 : _a.backupManager) === null || _b === void 0 ? void 0 : _b.getKeyBackupEnabled()) { - try { - while ((yield this.crypto.backupManager.backupPendingKeys(200)) > 0) - ; - } - catch (err) { - logger_1.logger.error("Key backup request failed when logging out. Some keys may be missing from backup", err); - } - } - if (stopClient) { - this.stopClient(); - this.http.abort(); - } - return this.http.authedRequest(http_api_1.Method.Post, "/logout"); - }); - } - /** - * Deactivates the logged-in account. - * Obviously, further calls that require authorisation should fail after this - * method is called. The state of the MatrixClient object is not affected: - * it is up to the caller to either reset or destroy the MatrixClient after - * this method succeeds. - * @param auth - Optional. Auth data to supply for User-Interactive auth. - * @param erase - Optional. If set, send as `erase` attribute in the - * JSON request body, indicating whether the account should be erased. Defaults - * to false. - * @returns Promise which resolves: On success, the empty object - */ - deactivateAccount(auth, erase) { - const body = {}; - if (auth) { - body.auth = auth; - } - if (erase !== undefined) { - body.erase = erase; - } - return this.http.authedRequest(http_api_1.Method.Post, "/account/deactivate", undefined, body); - } - /** - * Make a request for an `m.login.token` to be issued as per - * [MSC3882](https://github.com/matrix-org/matrix-spec-proposals/pull/3882). - * The server may require User-Interactive auth. - * Note that this is UNSTABLE and subject to breaking changes without notice. - * @param auth - Optional. Auth data to supply for User-Interactive auth. - * @returns Promise which resolves: On success, the token response - * or UIA auth data. - */ - requestLoginToken(auth) { - const body = { auth }; - return this.http.authedRequest(http_api_1.Method.Post, "/org.matrix.msc3882/login/token", undefined, // no query params - body, { prefix: http_api_1.ClientPrefix.Unstable }); - } - /** - * Get the fallback URL to use for unknown interactive-auth stages. - * - * @param loginType - the type of stage being attempted - * @param authSessionId - the auth session ID provided by the homeserver - * - * @returns HS URL to hit to for the fallback interface - */ - getFallbackAuthUrl(loginType, authSessionId) { - const path = utils.encodeUri("/auth/$loginType/fallback/web", { - $loginType: loginType, - }); - return this.http.getUrl(path, { - session: authSessionId, - }, http_api_1.ClientPrefix.R0).href; - } - /** - * Create a new room. - * @param options - a list of options to pass to the /createRoom API. - * @returns Promise which resolves: `{room_id: {string}}` - * @returns Rejects: with an error response. - */ - createRoom(options) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // eslint-disable-line camelcase - // some valid options include: room_alias_name, visibility, invite - // inject the id_access_token if inviting 3rd party addresses - const invitesNeedingToken = (options.invite_3pid || []).filter((i) => !i.id_access_token); - if (invitesNeedingToken.length > 0 && - ((_a = this.identityServer) === null || _a === void 0 ? void 0 : _a.getAccessToken) && - (yield this.doesServerAcceptIdentityAccessToken())) { - const identityAccessToken = yield this.identityServer.getAccessToken(); - if (identityAccessToken) { - for (const invite of invitesNeedingToken) { - invite.id_access_token = identityAccessToken; - } - } - } - return this.http.authedRequest(http_api_1.Method.Post, "/createRoom", undefined, options); - }); - } - /** - * Fetches relations for a given event - * @param roomId - the room of the event - * @param eventId - the id of the event - * @param relationType - the rel_type of the relations requested - * @param eventType - the event type of the relations requested - * @param opts - options with optional values for the request. - * @returns the response, with chunk, prev_batch and, next_batch. - */ - fetchRelations(roomId, eventId, relationType, eventType, opts = { dir: event_timeline_1.Direction.Backward }) { - let params = opts; - if (thread_1.Thread.hasServerSideFwdPaginationSupport === thread_1.FeatureSupport.Experimental) { - params = (0, utils_1.replaceParam)("dir", "org.matrix.msc3715.dir", params); - } - const queryString = utils.encodeParams(params); - let templatedUrl = "/rooms/$roomId/relations/$eventId"; - if (relationType !== null) { - templatedUrl += "/$relationType"; - if (eventType !== null) { - templatedUrl += "/$eventType"; - } - } - else if (eventType !== null) { - logger_1.logger.warn(`eventType: ${eventType} ignored when fetching - relations as relationType is null`); - eventType = null; - } - const path = utils.encodeUri(templatedUrl + "?" + queryString, { - $roomId: roomId, - $eventId: eventId, - $relationType: relationType, - $eventType: eventType, - }); - return this.http.authedRequest(http_api_1.Method.Get, path, undefined, undefined, { - prefix: http_api_1.ClientPrefix.V1, - }); - } - /** - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - roomState(roomId) { - const path = utils.encodeUri("/rooms/$roomId/state", { $roomId: roomId }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * Get an event in a room by its event id. - * - * @returns Promise which resolves to an object containing the event. - * @returns Rejects: with an error response. - */ - fetchRoomEvent(roomId, eventId) { - const path = utils.encodeUri("/rooms/$roomId/event/$eventId", { - $roomId: roomId, - $eventId: eventId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * @param includeMembership - the membership type to include in the response - * @param excludeMembership - the membership type to exclude from the response - * @param atEventId - the id of the event for which moment in the timeline the members should be returned for - * @returns Promise which resolves: dictionary of userid to profile information - * @returns Rejects: with an error response. - */ - members(roomId, includeMembership, excludeMembership, atEventId) { - const queryParams = {}; - if (includeMembership) { - queryParams.membership = includeMembership; - } - if (excludeMembership) { - queryParams.not_membership = excludeMembership; - } - if (atEventId) { - queryParams.at = atEventId; - } - const queryString = utils.encodeParams(queryParams); - const path = utils.encodeUri("/rooms/$roomId/members?" + queryString, { $roomId: roomId }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * Upgrades a room to a new protocol version - * @param newVersion - The target version to upgrade to - * @returns Promise which resolves: Object with key 'replacement_room' - * @returns Rejects: with an error response. - */ - upgradeRoom(roomId, newVersion) { - // eslint-disable-line camelcase - const path = utils.encodeUri("/rooms/$roomId/upgrade", { $roomId: roomId }); - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, { new_version: newVersion }); - } - /** - * Retrieve a state event. - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - getStateEvent(roomId, eventType, stateKey) { - const pathParams = { - $roomId: roomId, - $eventType: eventType, - $stateKey: stateKey, - }; - let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams); - if (stateKey !== undefined) { - path = utils.encodeUri(path + "/$stateKey", pathParams); - } - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * @param opts - Options for the request function. - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - sendStateEvent(roomId, eventType, content, stateKey = "", opts = {}) { - const pathParams = { - $roomId: roomId, - $eventType: eventType, - $stateKey: stateKey, - }; - let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams); - if (stateKey !== undefined) { - path = utils.encodeUri(path + "/$stateKey", pathParams); - } - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, content, opts); - } - /** - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - roomInitialSync(roomId, limit) { - var _a; - const path = utils.encodeUri("/rooms/$roomId/initialSync", { $roomId: roomId }); - return this.http.authedRequest(http_api_1.Method.Get, path, { limit: (_a = limit === null || limit === void 0 ? void 0 : limit.toString()) !== null && _a !== void 0 ? _a : "30" }); - } - /** - * Set a marker to indicate the point in a room before which the user has read every - * event. This can be retrieved from room account data (the event type is `m.fully_read`) - * and displayed as a horizontal line in the timeline that is visually distinct to the - * position of the user's own read receipt. - * @param roomId - ID of the room that has been read - * @param rmEventId - ID of the event that has been read - * @param rrEventId - ID of the event tracked by the read receipt. This is here - * for convenience because the RR and the RM are commonly updated at the same time as - * each other. Optional. - * @param rpEventId - rpEvent the m.read.private read receipt event for when we - * don't want other users to see the read receipts. This is experimental. Optional. - * @returns Promise which resolves: the empty object, `{}`. - */ - setRoomReadMarkersHttpRequest(roomId, rmEventId, rrEventId, rpEventId) { - return __awaiter(this, void 0, void 0, function* () { - const path = utils.encodeUri("/rooms/$roomId/read_markers", { - $roomId: roomId, - }); - const content = { - [read_receipts_1.ReceiptType.FullyRead]: rmEventId, - [read_receipts_1.ReceiptType.Read]: rrEventId, - }; - if ((yield this.doesServerSupportUnstableFeature("org.matrix.msc2285.stable")) || - (yield this.isVersionSupported("v1.4"))) { - content[read_receipts_1.ReceiptType.ReadPrivate] = rpEventId; - } - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, content); - }); - } - /** - * @returns Promise which resolves: A list of the user's current rooms - * @returns Rejects: with an error response. - */ - getJoinedRooms() { - const path = utils.encodeUri("/joined_rooms", {}); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * Retrieve membership info. for a room. - * @param roomId - ID of the room to get membership for - * @returns Promise which resolves: A list of currently joined users - * and their profile data. - * @returns Rejects: with an error response. - */ - getJoinedRoomMembers(roomId) { - const path = utils.encodeUri("/rooms/$roomId/joined_members", { - $roomId: roomId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * @param options - Options for this request - * @param server - The remote server to query for the room list. - * Optional. If unspecified, get the local home - * server's public room list. - * @param limit - Maximum number of entries to return - * @param since - Token to paginate from - * @returns Promise which resolves: IPublicRoomsResponse - * @returns Rejects: with an error response. - */ - publicRooms(_a = {}) { - var { server, limit, since } = _a, options = __rest(_a, ["server", "limit", "since"]); - const queryParams = { server, limit, since }; - if (Object.keys(options).length === 0) { - return this.http.authedRequest(http_api_1.Method.Get, "/publicRooms", queryParams); - } - else { - return this.http.authedRequest(http_api_1.Method.Post, "/publicRooms", queryParams, options); - } - } - /** - * Create an alias to room ID mapping. - * @param alias - The room alias to create. - * @param roomId - The room ID to link the alias to. - * @returns Promise which resolves: an empty object `{}` - * @returns Rejects: with an error response. - */ - createAlias(alias, roomId) { - const path = utils.encodeUri("/directory/room/$alias", { - $alias: alias, - }); - const data = { - room_id: roomId, - }; - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, data); - } - /** - * Delete an alias to room ID mapping. This alias must be on your local server, - * and you must have sufficient access to do this operation. - * @param alias - The room alias to delete. - * @returns Promise which resolves: an empty object `{}`. - * @returns Rejects: with an error response. - */ - deleteAlias(alias) { - const path = utils.encodeUri("/directory/room/$alias", { - $alias: alias, - }); - return this.http.authedRequest(http_api_1.Method.Delete, path); - } - /** - * Gets the local aliases for the room. Note: this includes all local aliases, unlike the - * curated list from the m.room.canonical_alias state event. - * @param roomId - The room ID to get local aliases for. - * @returns Promise which resolves: an object with an `aliases` property, containing an array of local aliases - * @returns Rejects: with an error response. - */ - getLocalAliases(roomId) { - const path = utils.encodeUri("/rooms/$roomId/aliases", { $roomId: roomId }); - const prefix = http_api_1.ClientPrefix.V3; - return this.http.authedRequest(http_api_1.Method.Get, path, undefined, undefined, { prefix }); - } - /** - * Get room info for the given alias. - * @param alias - The room alias to resolve. - * @returns Promise which resolves: Object with room_id and servers. - * @returns Rejects: with an error response. - */ - getRoomIdForAlias(alias) { - // eslint-disable-line camelcase - // TODO: deprecate this or resolveRoomAlias - const path = utils.encodeUri("/directory/room/$alias", { - $alias: alias, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * @returns Promise which resolves: Object with room_id and servers. - * @returns Rejects: with an error response. - */ - // eslint-disable-next-line camelcase - resolveRoomAlias(roomAlias) { - // TODO: deprecate this or getRoomIdForAlias - const path = utils.encodeUri("/directory/room/$alias", { $alias: roomAlias }); - return this.http.request(http_api_1.Method.Get, path); - } - /** - * Get the visibility of a room in the current HS's room directory - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - getRoomDirectoryVisibility(roomId) { - const path = utils.encodeUri("/directory/list/room/$roomId", { - $roomId: roomId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * Set the visbility of a room in the current HS's room directory - * @param visibility - "public" to make the room visible - * in the public directory, or "private" to make - * it invisible. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - setRoomDirectoryVisibility(roomId, visibility) { - const path = utils.encodeUri("/directory/list/room/$roomId", { - $roomId: roomId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, { visibility }); - } - /** - * Set the visbility of a room bridged to a 3rd party network in - * the current HS's room directory. - * @param networkId - the network ID of the 3rd party - * instance under which this room is published under. - * @param visibility - "public" to make the room visible - * in the public directory, or "private" to make - * it invisible. - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - setRoomDirectoryVisibilityAppService(networkId, roomId, visibility) { - // TODO: Types - const path = utils.encodeUri("/directory/list/appservice/$networkId/$roomId", { - $networkId: networkId, - $roomId: roomId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, { visibility: visibility }); - } - /** - * Query the user directory with a term matching user IDs, display names and domains. - * @param term - the term with which to search. - * @param limit - the maximum number of results to return. The server will - * apply a limit if unspecified. - * @returns Promise which resolves: an array of results. - */ - searchUserDirectory({ term, limit }) { - const body = { - search_term: term, - }; - if (limit !== undefined) { - body.limit = limit; - } - return this.http.authedRequest(http_api_1.Method.Post, "/user_directory/search", undefined, body); - } - /** - * Upload a file to the media repository on the homeserver. - * - * @param file - The object to upload. On a browser, something that - * can be sent to XMLHttpRequest.send (typically a File). Under node.js, - * a a Buffer, String or ReadStream. - * - * @param opts - options object - * - * @returns Promise which resolves to response object, as - * determined by this.opts.onlyData, opts.rawResponse, and - * opts.onlyContentUri. Rejects with an error (usually a MatrixError). - */ - uploadContent(file, opts) { - return this.http.uploadContent(file, opts); - } - /** - * Cancel a file upload in progress - * @param upload - The object returned from uploadContent - * @returns true if canceled, otherwise false - */ - cancelUpload(upload) { - return this.http.cancelUpload(upload); - } - /** - * Get a list of all file uploads in progress - * @returns Array of objects representing current uploads. - * Currently in progress is element 0. Keys: - * - promise: The promise associated with the upload - * - loaded: Number of bytes uploaded - * - total: Total number of bytes to upload - */ - getCurrentUploads() { - return this.http.getCurrentUploads(); - } - /** - * @param info - The kind of info to retrieve (e.g. 'displayname', - * 'avatar_url'). - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - */ - getProfileInfo(userId, info) { - const path = info - ? utils.encodeUri("/profile/$userId/$info", { $userId: userId, $info: info }) - : utils.encodeUri("/profile/$userId", { $userId: userId }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * @returns Promise which resolves to a list of the user's threepids. - * @returns Rejects: with an error response. - */ - getThreePids() { - return this.http.authedRequest(http_api_1.Method.Get, "/account/3pid"); - } - /** - * Add a 3PID to your homeserver account and optionally bind it to an identity - * server as well. An identity server is required as part of the `creds` object. - * - * This API is deprecated, and you should instead use `addThreePidOnly` - * for homeservers that support it. - * - * @returns Promise which resolves: on success - * @returns Rejects: with an error response. - */ - addThreePid(creds, bind) { - // TODO: Types - const path = "/account/3pid"; - const data = { - threePidCreds: creds, - bind: bind, - }; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data); - } - /** - * Add a 3PID to your homeserver account. This API does not use an identity - * server, as the homeserver is expected to handle 3PID ownership validation. - * - * You can check whether a homeserver supports this API via - * `doesServerSupportSeparateAddAndBind`. - * - * @param data - A object with 3PID validation data from having called - * `account/3pid//requestToken` on the homeserver. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - addThreePidOnly(data) { - return __awaiter(this, void 0, void 0, function* () { - const path = "/account/3pid/add"; - const prefix = (yield this.isVersionSupported("r0.6.0")) ? http_api_1.ClientPrefix.R0 : http_api_1.ClientPrefix.Unstable; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data, { prefix }); - }); - } - /** - * Bind a 3PID for discovery onto an identity server via the homeserver. The - * identity server handles 3PID ownership validation and the homeserver records - * the new binding to track where all 3PIDs for the account are bound. - * - * You can check whether a homeserver supports this API via - * `doesServerSupportSeparateAddAndBind`. - * - * @param data - A object with 3PID validation data from having called - * `validate//requestToken` on the identity server. It should also - * contain `id_server` and `id_access_token` fields as well. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - bindThreePid(data) { - return __awaiter(this, void 0, void 0, function* () { - const path = "/account/3pid/bind"; - const prefix = (yield this.isVersionSupported("r0.6.0")) ? http_api_1.ClientPrefix.R0 : http_api_1.ClientPrefix.Unstable; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data, { prefix }); - }); - } - /** - * Unbind a 3PID for discovery on an identity server via the homeserver. The - * homeserver removes its record of the binding to keep an updated record of - * where all 3PIDs for the account are bound. - * - * @param medium - The threepid medium (eg. 'email') - * @param address - The threepid address (eg. 'bob\@example.com') - * this must be as returned by getThreePids. - * @returns Promise which resolves: on success - * @returns Rejects: with an error response. - */ - unbindThreePid(medium, address) { - return __awaiter(this, void 0, void 0, function* () { - const path = "/account/3pid/unbind"; - const data = { - medium, - address, - id_server: this.getIdentityServerUrl(true), - }; - const prefix = (yield this.isVersionSupported("r0.6.0")) ? http_api_1.ClientPrefix.R0 : http_api_1.ClientPrefix.Unstable; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data, { prefix }); - }); - } - /** - * @param medium - The threepid medium (eg. 'email') - * @param address - The threepid address (eg. 'bob\@example.com') - * this must be as returned by getThreePids. - * @returns Promise which resolves: The server response on success - * (generally the empty JSON object) - * @returns Rejects: with an error response. - */ - deleteThreePid(medium, address) { - const path = "/account/3pid/delete"; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, { medium, address }); - } - /** - * Make a request to change your password. - * @param newPassword - The new desired password. - * @param logoutDevices - Should all sessions be logged out after the password change. Defaults to true. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - setPassword(authDict, newPassword, logoutDevices) { - const path = "/account/password"; - const data = { - auth: authDict, - new_password: newPassword, - logout_devices: logoutDevices, - }; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, data); - } - /** - * Gets all devices recorded for the logged-in user - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - getDevices() { - return this.http.authedRequest(http_api_1.Method.Get, "/devices"); - } - /** - * Gets specific device details for the logged-in user - * @param deviceId - device to query - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - getDevice(deviceId) { - const path = utils.encodeUri("/devices/$device_id", { - $device_id: deviceId, - }); - return this.http.authedRequest(http_api_1.Method.Get, path); - } - /** - * Update the given device - * - * @param deviceId - device to update - * @param body - body of request - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - // eslint-disable-next-line camelcase - setDeviceDetails(deviceId, body) { - const path = utils.encodeUri("/devices/$device_id", { - $device_id: deviceId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, body); - } - /** - * Delete the given device - * - * @param deviceId - device to delete - * @param auth - Optional. Auth data to supply for User-Interactive auth. - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - deleteDevice(deviceId, auth) { - const path = utils.encodeUri("/devices/$device_id", { - $device_id: deviceId, - }); - const body = {}; - if (auth) { - body.auth = auth; - } - return this.http.authedRequest(http_api_1.Method.Delete, path, undefined, body); - } - /** - * Delete multiple device - * - * @param devices - IDs of the devices to delete - * @param auth - Optional. Auth data to supply for User-Interactive auth. - * @returns Promise which resolves: result object - * @returns Rejects: with an error response. - */ - deleteMultipleDevices(devices, auth) { - const body = { devices }; - if (auth) { - body.auth = auth; - } - const path = "/delete_devices"; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, body); - } - /** - * Gets all pushers registered for the logged-in user - * - * @returns Promise which resolves: Array of objects representing pushers - * @returns Rejects: with an error response. - */ - getPushers() { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.http.authedRequest(http_api_1.Method.Get, "/pushers"); - // Migration path for clients that connect to a homeserver that does not support - // MSC3881 yet, see https://github.com/matrix-org/matrix-spec-proposals/blob/kerry/remote-push-toggle/proposals/3881-remote-push-notification-toggling.md#migration - if (!(yield this.doesServerSupportUnstableFeature("org.matrix.msc3881"))) { - response.pushers = response.pushers.map((pusher) => { - if (!pusher.hasOwnProperty(event_2.PUSHER_ENABLED.name)) { - pusher[event_2.PUSHER_ENABLED.name] = true; - } - return pusher; - }); - } - return response; - }); - } - /** - * Adds a new pusher or updates an existing pusher - * - * @param pusher - Object representing a pusher - * @returns Promise which resolves: Empty json object on success - * @returns Rejects: with an error response. - */ - setPusher(pusher) { - const path = "/pushers/set"; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, pusher); - } - /** - * Persists local notification settings - * @returns Promise which resolves: an empty object - * @returns Rejects: with an error response. - */ - setLocalNotificationSettings(deviceId, notificationSettings) { - const key = `${event_2.LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${deviceId}`; - return this.setAccountData(key, notificationSettings); - } - /** - * Get the push rules for the account from the server. - * @returns Promise which resolves to the push rules. - * @returns Rejects: with an error response. - */ - getPushRules() { - return this.http.authedRequest(http_api_1.Method.Get, "/pushrules/").then((rules) => { - this.setPushRules(rules); - return this.pushRules; - }); - } - /** - * Update the push rules for the account. This should be called whenever - * updated push rules are available. - */ - setPushRules(rules) { - // Fix-up defaults, if applicable. - this.pushRules = pushprocessor_1.PushProcessor.rewriteDefaultRules(rules, this.getUserId()); - // Pre-calculate any necessary caches. - this.pushProcessor.updateCachedPushRuleKeys(this.pushRules); - } - /** - * @returns Promise which resolves: an empty object `{}` - * @returns Rejects: with an error response. - */ - addPushRule(scope, kind, ruleId, body) { - // NB. Scope not uri encoded because devices need the '/' - const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", { - $kind: kind, - $ruleId: ruleId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, body); - } - /** - * @returns Promise which resolves: an empty object `{}` - * @returns Rejects: with an error response. - */ - deletePushRule(scope, kind, ruleId) { - // NB. Scope not uri encoded because devices need the '/' - const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", { - $kind: kind, - $ruleId: ruleId, - }); - return this.http.authedRequest(http_api_1.Method.Delete, path); - } - /** - * Enable or disable a push notification rule. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - setPushRuleEnabled(scope, kind, ruleId, enabled) { - const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/enabled", { - $kind: kind, - $ruleId: ruleId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, { enabled: enabled }); - } - /** - * Set the actions for a push notification rule. - * @returns Promise which resolves: to an empty object `{}` - * @returns Rejects: with an error response. - */ - setPushRuleActions(scope, kind, ruleId, actions) { - const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/actions", { - $kind: kind, - $ruleId: ruleId, - }); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, { actions: actions }); - } - /** - * Perform a server-side search. - * @param next_batch - the batch token to pass in the query string - * @param body - the JSON object to pass to the request body. - * @param abortSignal - optional signal used to cancel the http request. - * @returns Promise which resolves to the search response object. - * @returns Rejects: with an error response. - */ - search({ body, next_batch: nextBatch }, abortSignal) { - const queryParams = {}; - if (nextBatch) { - queryParams.next_batch = nextBatch; - } - return this.http.authedRequest(http_api_1.Method.Post, "/search", queryParams, body, { abortSignal }); - } - /** - * Upload keys - * - * @param content - body of upload request - * - * @param opts - this method no longer takes any opts, - * used to take opts.device_id but this was not removed from the spec as a redundant parameter - * - * @returns Promise which resolves: result object. Rejects: with - * an error response ({@link MatrixError}). - */ - uploadKeysRequest(content, opts) { - return this.http.authedRequest(http_api_1.Method.Post, "/keys/upload", undefined, content); - } - uploadKeySignatures(content) { - return this.http.authedRequest(http_api_1.Method.Post, "/keys/signatures/upload", undefined, content, { - prefix: http_api_1.ClientPrefix.V3, - }); - } - /** - * Download device keys - * - * @param userIds - list of users to get keys for - * - * @param token - sync token to pass in the query request, to help - * the HS give the most recent results - * - * @returns Promise which resolves: result object. Rejects: with - * an error response ({@link MatrixError}). - */ - downloadKeysForUsers(userIds, { token } = {}) { - const content = { - device_keys: {}, - }; - if (token !== undefined) { - content.token = token; - } - userIds.forEach((u) => { - content.device_keys[u] = []; - }); - return this.http.authedRequest(http_api_1.Method.Post, "/keys/query", undefined, content); - } - /** - * Claim one-time keys - * - * @param devices - a list of [userId, deviceId] pairs - * - * @param keyAlgorithm - desired key type - * - * @param timeout - the time (in milliseconds) to wait for keys from remote - * servers - * - * @returns Promise which resolves: result object. Rejects: with - * an error response ({@link MatrixError}). - */ - claimOneTimeKeys(devices, keyAlgorithm = "signed_curve25519", timeout) { - const queries = {}; - if (keyAlgorithm === undefined) { - keyAlgorithm = "signed_curve25519"; - } - for (const [userId, deviceId] of devices) { - const query = queries[userId] || {}; - queries[userId] = query; - query[deviceId] = keyAlgorithm; - } - const content = { one_time_keys: queries }; - if (timeout) { - content.timeout = timeout; - } - const path = "/keys/claim"; - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, content); - } - /** - * Ask the server for a list of users who have changed their device lists - * between a pair of sync tokens - * - * - * @returns Promise which resolves: result object. Rejects: with - * an error response ({@link MatrixError}). - */ - getKeyChanges(oldToken, newToken) { - const qps = { - from: oldToken, - to: newToken, - }; - return this.http.authedRequest(http_api_1.Method.Get, "/keys/changes", qps); - } - uploadDeviceSigningKeys(auth, keys) { - // API returns empty object - const data = Object.assign({}, keys); - if (auth) - Object.assign(data, { auth }); - return this.http.authedRequest(http_api_1.Method.Post, "/keys/device_signing/upload", undefined, data, { - prefix: http_api_1.ClientPrefix.Unstable, - }); - } - /** - * Register with an identity server using the OpenID token from the user's - * Homeserver, which can be retrieved via - * {@link MatrixClient#getOpenIdToken}. - * - * Note that the `/account/register` endpoint (as well as IS authentication in - * general) was added as part of the v2 API version. - * - * @returns Promise which resolves: with object containing an Identity - * Server access token. - * @returns Rejects: with an error response. - */ - registerWithIdentityServer(hsOpenIdToken) { - if (!this.idBaseUrl) { - throw new Error("No identity server base URL set"); - } - const uri = this.http.getUrl("/account/register", undefined, http_api_1.IdentityPrefix.V2, this.idBaseUrl); - return this.http.requestOtherUrl(http_api_1.Method.Post, uri, hsOpenIdToken); - } - /** - * Requests an email verification token directly from an identity server. - * - * This API is used as part of binding an email for discovery on an identity - * server. The validation data that results should be passed to the - * `bindThreePid` method to complete the binding process. - * - * @param email - The email address to request a token for - * @param clientSecret - A secret binary string generated by the client. - * It is recommended this be around 16 ASCII characters. - * @param sendAttempt - If an identity server sees a duplicate request - * with the same sendAttempt, it will not send another email. - * To request another email to be sent, use a larger value for - * the sendAttempt param as was used in the previous request. - * @param nextLink - Optional If specified, the client will be redirected - * to this link after validation. - * @param identityAccessToken - The `access_token` field of the identity - * server `/account/register` response (see {@link registerWithIdentityServer}). - * - * @returns Promise which resolves: TODO - * @returns Rejects: with an error response. - * @throws Error if no identity server is set - */ - requestEmailToken(email, clientSecret, sendAttempt, nextLink, identityAccessToken) { - const params = { - client_secret: clientSecret, - email: email, - send_attempt: sendAttempt === null || sendAttempt === void 0 ? void 0 : sendAttempt.toString(), - }; - if (nextLink) { - params.next_link = nextLink; - } - return this.http.idServerRequest(http_api_1.Method.Post, "/validate/email/requestToken", params, http_api_1.IdentityPrefix.V2, identityAccessToken); - } - /** - * Requests a MSISDN verification token directly from an identity server. - * - * This API is used as part of binding a MSISDN for discovery on an identity - * server. The validation data that results should be passed to the - * `bindThreePid` method to complete the binding process. - * - * @param phoneCountry - The ISO 3166-1 alpha-2 code for the country in - * which phoneNumber should be parsed relative to. - * @param phoneNumber - The phone number, in national or international - * format - * @param clientSecret - A secret binary string generated by the client. - * It is recommended this be around 16 ASCII characters. - * @param sendAttempt - If an identity server sees a duplicate request - * with the same sendAttempt, it will not send another SMS. - * To request another SMS to be sent, use a larger value for - * the sendAttempt param as was used in the previous request. - * @param nextLink - Optional If specified, the client will be redirected - * to this link after validation. - * @param identityAccessToken - The `access_token` field of the Identity - * Server `/account/register` response (see {@link registerWithIdentityServer}). - * - * @returns Promise which resolves to an object with a sid string - * @returns Rejects: with an error response. - * @throws Error if no identity server is set - */ - requestMsisdnToken(phoneCountry, phoneNumber, clientSecret, sendAttempt, nextLink, identityAccessToken) { - const params = { - client_secret: clientSecret, - country: phoneCountry, - phone_number: phoneNumber, - send_attempt: sendAttempt === null || sendAttempt === void 0 ? void 0 : sendAttempt.toString(), - }; - if (nextLink) { - params.next_link = nextLink; - } - return this.http.idServerRequest(http_api_1.Method.Post, "/validate/msisdn/requestToken", params, http_api_1.IdentityPrefix.V2, identityAccessToken); - } - /** - * Submits a MSISDN token to the identity server - * - * This is used when submitting the code sent by SMS to a phone number. - * The identity server has an equivalent API for email but the js-sdk does - * not expose this, since email is normally validated by the user clicking - * a link rather than entering a code. - * - * @param sid - The sid given in the response to requestToken - * @param clientSecret - A secret binary string generated by the client. - * This must be the same value submitted in the requestToken call. - * @param msisdnToken - The MSISDN token, as enetered by the user. - * @param identityAccessToken - The `access_token` field of the Identity - * Server `/account/register` response (see {@link registerWithIdentityServer}). - * - * @returns Promise which resolves: Object, currently with no parameters. - * @returns Rejects: with an error response. - * @throws Error if No identity server is set - */ - submitMsisdnToken(sid, clientSecret, msisdnToken, identityAccessToken) { - // TODO: Types - const params = { - sid: sid, - client_secret: clientSecret, - token: msisdnToken, - }; - return this.http.idServerRequest(http_api_1.Method.Post, "/validate/msisdn/submitToken", params, http_api_1.IdentityPrefix.V2, identityAccessToken); - } - /** - * Submits a MSISDN token to an arbitrary URL. - * - * This is used when submitting the code sent by SMS to a phone number in the - * newer 3PID flow where the homeserver validates 3PID ownership (as part of - * `requestAdd3pidMsisdnToken`). The homeserver response may include a - * `submit_url` to specify where the token should be sent, and this helper can - * be used to pass the token to this URL. - * - * @param url - The URL to submit the token to - * @param sid - The sid given in the response to requestToken - * @param clientSecret - A secret binary string generated by the client. - * This must be the same value submitted in the requestToken call. - * @param msisdnToken - The MSISDN token, as enetered by the user. - * - * @returns Promise which resolves: Object, currently with no parameters. - * @returns Rejects: with an error response. - */ - submitMsisdnTokenOtherUrl(url, sid, clientSecret, msisdnToken) { - // TODO: Types - const params = { - sid: sid, - client_secret: clientSecret, - token: msisdnToken, - }; - return this.http.requestOtherUrl(http_api_1.Method.Post, url, params); - } - /** - * Gets the V2 hashing information from the identity server. Primarily useful for - * lookups. - * @param identityAccessToken - The access token for the identity server. - * @returns The hashing information for the identity server. - */ - getIdentityHashDetails(identityAccessToken) { - // TODO: Types - return this.http.idServerRequest(http_api_1.Method.Get, "/hash_details", undefined, http_api_1.IdentityPrefix.V2, identityAccessToken); - } - /** - * Performs a hashed lookup of addresses against the identity server. This is - * only supported on identity servers which have at least the version 2 API. - * @param addressPairs - An array of 2 element arrays. - * The first element of each pair is the address, the second is the 3PID medium. - * Eg: `["email@example.org", "email"]` - * @param identityAccessToken - The access token for the identity server. - * @returns A collection of address mappings to - * found MXIDs. Results where no user could be found will not be listed. - */ - identityHashedLookup(addressPairs, identityAccessToken) { - return __awaiter(this, void 0, void 0, function* () { - const params = { - // addresses: ["email@example.org", "10005550000"], - // algorithm: "sha256", - // pepper: "abc123" - }; - // Get hash information first before trying to do a lookup - const hashes = yield this.getIdentityHashDetails(identityAccessToken); - if (!hashes || !hashes["lookup_pepper"] || !hashes["algorithms"]) { - throw new Error("Unsupported identity server: bad response"); - } - params["pepper"] = hashes["lookup_pepper"]; - const localMapping = { - // hashed identifier => plain text address - // For use in this function's return format - }; - // When picking an algorithm, we pick the hashed over no hashes - if (hashes["algorithms"].includes("sha256")) { - // Abuse the olm hashing - const olmutil = new global.Olm.Utility(); - params["addresses"] = addressPairs.map((p) => { - const addr = p[0].toLowerCase(); // lowercase to get consistent hashes - const med = p[1].toLowerCase(); - const hashed = olmutil - .sha256(`${addr} ${med} ${params["pepper"]}`) - .replace(/\+/g, "-") - .replace(/\//g, "_"); // URL-safe base64 - // Map the hash to a known (case-sensitive) address. We use the case - // sensitive version because the caller might be expecting that. - localMapping[hashed] = p[0]; - return hashed; - }); - params["algorithm"] = "sha256"; - } - else if (hashes["algorithms"].includes("none")) { - params["addresses"] = addressPairs.map((p) => { - const addr = p[0].toLowerCase(); // lowercase to get consistent hashes - const med = p[1].toLowerCase(); - const unhashed = `${addr} ${med}`; - // Map the unhashed values to a known (case-sensitive) address. We use - // the case-sensitive version because the caller might be expecting that. - localMapping[unhashed] = p[0]; - return unhashed; - }); - params["algorithm"] = "none"; - } - else { - throw new Error("Unsupported identity server: unknown hash algorithm"); - } - const response = yield this.http.idServerRequest(http_api_1.Method.Post, "/lookup", params, http_api_1.IdentityPrefix.V2, identityAccessToken); - if (!(response === null || response === void 0 ? void 0 : response["mappings"])) - return []; // no results - const foundAddresses = []; - for (const hashed of Object.keys(response["mappings"])) { - const mxid = response["mappings"][hashed]; - const plainAddress = localMapping[hashed]; - if (!plainAddress) { - throw new Error("Identity server returned more results than expected"); - } - foundAddresses.push({ address: plainAddress, mxid }); - } - return foundAddresses; - }); - } - /** - * Looks up the public Matrix ID mapping for a given 3rd party - * identifier from the identity server - * - * @param medium - The medium of the threepid, eg. 'email' - * @param address - The textual address of the threepid - * @param identityAccessToken - The `access_token` field of the Identity - * Server `/account/register` response (see {@link registerWithIdentityServer}). - * - * @returns Promise which resolves: A threepid mapping - * object or the empty object if no mapping - * exists - * @returns Rejects: with an error response. - */ - lookupThreePid(medium, address, identityAccessToken) { - return __awaiter(this, void 0, void 0, function* () { - // TODO: Types - // Note: we're using the V2 API by calling this function, but our - // function contract requires a V1 response. We therefore have to - // convert it manually. - const response = yield this.identityHashedLookup([[address, medium]], identityAccessToken); - const result = response.find((p) => p.address === address); - if (!result) { - return {}; - } - const mapping = { - address, - medium, - mxid: result.mxid, - // We can't reasonably fill these parameters: - // not_before - // not_after - // ts - // signatures - }; - return mapping; - }); - } - /** - * Looks up the public Matrix ID mappings for multiple 3PIDs. - * - * @param query - Array of arrays containing - * [medium, address] - * @param identityAccessToken - The `access_token` field of the Identity - * Server `/account/register` response (see {@link registerWithIdentityServer}). - * - * @returns Promise which resolves: Lookup results from IS. - * @returns Rejects: with an error response. - */ - bulkLookupThreePids(query, identityAccessToken) { - return __awaiter(this, void 0, void 0, function* () { - // TODO: Types - // Note: we're using the V2 API by calling this function, but our - // function contract requires a V1 response. We therefore have to - // convert it manually. - const response = yield this.identityHashedLookup( - // We have to reverse the query order to get [address, medium] pairs - query.map((p) => [p[1], p[0]]), identityAccessToken); - const v1results = []; - for (const mapping of response) { - const originalQuery = query.find((p) => p[1] === mapping.address); - if (!originalQuery) { - throw new Error("Identity sever returned unexpected results"); - } - v1results.push([ - originalQuery[0], - mapping.address, - mapping.mxid, - ]); - } - return { threepids: v1results }; - }); - } - /** - * Get account info from the identity server. This is useful as a neutral check - * to verify that other APIs are likely to approve access by testing that the - * token is valid, terms have been agreed, etc. - * - * @param identityAccessToken - The `access_token` field of the Identity - * Server `/account/register` response (see {@link registerWithIdentityServer}). - * - * @returns Promise which resolves: an object with account info. - * @returns Rejects: with an error response. - */ - getIdentityAccount(identityAccessToken) { - // TODO: Types - return this.http.idServerRequest(http_api_1.Method.Get, "/account", undefined, http_api_1.IdentityPrefix.V2, identityAccessToken); - } - /** - * Send an event to a specific list of devices. - * This is a low-level API that simply wraps the HTTP API - * call to send to-device messages. We recommend using - * queueToDevice() which is a higher level API. - * - * @param eventType - type of event to send - * content to send. Map from user_id to device_id to content object. - * @param txnId - transaction id. One will be made up if not - * supplied. - * @returns Promise which resolves: to an empty object `{}` - */ - sendToDevice(eventType, contentMap, txnId) { - const path = utils.encodeUri("/sendToDevice/$eventType/$txnId", { - $eventType: eventType, - $txnId: txnId ? txnId : this.makeTxnId(), - }); - const body = { - messages: utils.recursiveMapToObject(contentMap), - }; - const targets = new Map(); - for (const [userId, deviceMessages] of contentMap) { - targets.set(userId, Array.from(deviceMessages.keys())); - } - logger_1.logger.log(`PUT ${path}`, targets); - return this.http.authedRequest(http_api_1.Method.Put, path, undefined, body); - } - /** - * Sends events directly to specific devices using Matrix's to-device - * messaging system. The batch will be split up into appropriately sized - * batches for sending and stored in the store so they can be retried - * later if they fail to send. Retries will happen automatically. - * @param batch - The to-device messages to send - */ - queueToDevice(batch) { - return this.toDeviceMessageQueue.queueBatch(batch); - } - /** - * Get the third party protocols that can be reached using - * this HS - * @returns Promise which resolves to the result object - */ - getThirdpartyProtocols() { - return this.http - .authedRequest(http_api_1.Method.Get, "/thirdparty/protocols") - .then((response) => { - // sanity check - if (!response || typeof response !== "object") { - throw new Error(`/thirdparty/protocols did not return an object: ${response}`); - } - return response; - }); - } - /** - * Get information on how a specific place on a third party protocol - * may be reached. - * @param protocol - The protocol given in getThirdpartyProtocols() - * @param params - Protocol-specific parameters, as given in the - * response to getThirdpartyProtocols() - * @returns Promise which resolves to the result object - */ - getThirdpartyLocation(protocol, params) { - const path = utils.encodeUri("/thirdparty/location/$protocol", { - $protocol: protocol, - }); - return this.http.authedRequest(http_api_1.Method.Get, path, params); - } - /** - * Get information on how a specific user on a third party protocol - * may be reached. - * @param protocol - The protocol given in getThirdpartyProtocols() - * @param params - Protocol-specific parameters, as given in the - * response to getThirdpartyProtocols() - * @returns Promise which resolves to the result object - */ - getThirdpartyUser(protocol, params) { - // TODO: Types - const path = utils.encodeUri("/thirdparty/user/$protocol", { - $protocol: protocol, - }); - return this.http.authedRequest(http_api_1.Method.Get, path, params); - } - getTerms(serviceType, baseUrl) { - // TODO: Types - const url = this.termsUrlForService(serviceType, baseUrl); - return this.http.requestOtherUrl(http_api_1.Method.Get, url); - } - agreeToTerms(serviceType, baseUrl, accessToken, termsUrls) { - const url = this.termsUrlForService(serviceType, baseUrl); - const headers = { - Authorization: "Bearer " + accessToken, - }; - return this.http.requestOtherUrl(http_api_1.Method.Post, url, { - user_accepts: termsUrls, - }, { headers }); - } - /** - * Reports an event as inappropriate to the server, which may then notify the appropriate people. - * @param roomId - The room in which the event being reported is located. - * @param eventId - The event to report. - * @param score - The score to rate this content as where -100 is most offensive and 0 is inoffensive. - * @param reason - The reason the content is being reported. May be blank. - * @returns Promise which resolves to an empty object if successful - */ - reportEvent(roomId, eventId, score, reason) { - const path = utils.encodeUri("/rooms/$roomId/report/$eventId", { - $roomId: roomId, - $eventId: eventId, - }); - return this.http.authedRequest(http_api_1.Method.Post, path, undefined, { score, reason }); - } - /** - * Fetches or paginates a room hierarchy as defined by MSC2946. - * Falls back gracefully to sourcing its data from `getSpaceSummary` if this API is not yet supported by the server. - * @param roomId - The ID of the space-room to use as the root of the summary. - * @param limit - The maximum number of rooms to return per page. - * @param maxDepth - The maximum depth in the tree from the root room to return. - * @param suggestedOnly - Whether to only return rooms with suggested=true. - * @param fromToken - The opaque token to paginate a previous request. - * @returns the response, with next_batch & rooms fields. - */ - getRoomHierarchy(roomId, limit, maxDepth, suggestedOnly = false, fromToken) { - const path = utils.encodeUri("/rooms/$roomId/hierarchy", { - $roomId: roomId, - }); - const queryParams = { - suggested_only: String(suggestedOnly), - max_depth: maxDepth === null || maxDepth === void 0 ? void 0 : maxDepth.toString(), - from: fromToken, - limit: limit === null || limit === void 0 ? void 0 : limit.toString(), - }; - return this.http - .authedRequest(http_api_1.Method.Get, path, queryParams, undefined, { - prefix: http_api_1.ClientPrefix.V1, - }) - .catch((e) => { - if (e.errcode === "M_UNRECOGNIZED") { - // fall back to the prefixed hierarchy API. - return this.http.authedRequest(http_api_1.Method.Get, path, queryParams, undefined, { - prefix: "/_matrix/client/unstable/org.matrix.msc2946", - }); - } - throw e; - }); - } - /** - * Creates a new file tree space with the given name. The client will pick - * defaults for how it expects to be able to support the remaining API offered - * by the returned class. - * - * Note that this is UNSTABLE and may have breaking changes without notice. - * @param name - The name of the tree space. - * @returns Promise which resolves to the created space. - */ - unstableCreateFileTree(name) { - return __awaiter(this, void 0, void 0, function* () { - const { room_id: roomId } = yield this.createRoom({ - name: name, - preset: partials_1.Preset.PrivateChat, - power_level_content_override: Object.assign(Object.assign({}, MSC3089TreeSpace_1.DEFAULT_TREE_POWER_LEVELS_TEMPLATE), { users: { - [this.getUserId()]: 100, - } }), - creation_content: { - [event_2.RoomCreateTypeField]: event_2.RoomType.Space, - }, - initial_state: [ - { - type: event_2.UNSTABLE_MSC3088_PURPOSE.name, - state_key: event_2.UNSTABLE_MSC3089_TREE_SUBTYPE.name, - content: { - [event_2.UNSTABLE_MSC3088_ENABLED.name]: true, - }, - }, - { - type: event_2.EventType.RoomEncryption, - state_key: "", - content: { - algorithm: olmlib.MEGOLM_ALGORITHM, - }, - }, - ], - }); - return new MSC3089TreeSpace_1.MSC3089TreeSpace(this, roomId); - }); - } - /** - * Gets a reference to a tree space, if the room ID given is a tree space. If the room - * does not appear to be a tree space then null is returned. - * - * Note that this is UNSTABLE and may have breaking changes without notice. - * @param roomId - The room ID to get a tree space reference for. - * @returns The tree space, or null if not a tree space. - */ - unstableGetFileTreeSpace(roomId) { - var _a, _b; - const room = this.getRoom(roomId); - if ((room === null || room === void 0 ? void 0 : room.getMyMembership()) !== "join") - return null; - const createEvent = room.currentState.getStateEvents(event_2.EventType.RoomCreate, ""); - const purposeEvent = room.currentState.getStateEvents(event_2.UNSTABLE_MSC3088_PURPOSE.name, event_2.UNSTABLE_MSC3089_TREE_SUBTYPE.name); - if (!createEvent) - throw new Error("Expected single room create event"); - if (!((_a = purposeEvent === null || purposeEvent === void 0 ? void 0 : purposeEvent.getContent()) === null || _a === void 0 ? void 0 : _a[event_2.UNSTABLE_MSC3088_ENABLED.name])) - return null; - if (((_b = createEvent.getContent()) === null || _b === void 0 ? void 0 : _b[event_2.RoomCreateTypeField]) !== event_2.RoomType.Space) - return null; - return new MSC3089TreeSpace_1.MSC3089TreeSpace(this, roomId); - } - /** - * Perform a single MSC3575 sliding sync request. - * @param req - The request to make. - * @param proxyBaseUrl - The base URL for the sliding sync proxy. - * @param abortSignal - Optional signal to abort request mid-flight. - * @returns The sliding sync response, or a standard error. - * @throws on non 2xx status codes with an object with a field "httpStatus":number. - */ - slidingSync(req, proxyBaseUrl, abortSignal) { - const qps = {}; - if (req.pos) { - qps.pos = req.pos; - delete req.pos; - } - if (req.timeout) { - qps.timeout = req.timeout; - delete req.timeout; - } - const clientTimeout = req.clientTimeout; - delete req.clientTimeout; - return this.http.authedRequest(http_api_1.Method.Post, "/sync", qps, req, { - prefix: "/_matrix/client/unstable/org.matrix.msc3575", - baseUrl: proxyBaseUrl, - localTimeoutMs: clientTimeout, - abortSignal, - }); - } - /** - * @deprecated use supportsThreads() instead - */ - supportsExperimentalThreads() { - var _a; - logger_1.logger.warn(`supportsExperimentalThreads() is deprecated, use supportThreads() instead`); - return ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.experimentalThreadSupport) || false; - } - /** - * A helper to determine thread support - * @returns a boolean to determine if threads are enabled - */ - supportsThreads() { - var _a; - return ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.threadSupport) || false; - } - /** - * A helper to determine intentional mentions support - * @returns a boolean to determine if intentional mentions are enabled - * @experimental - */ - supportsIntentionalMentions() { - var _a; - return ((_a = this.clientOpts) === null || _a === void 0 ? void 0 : _a.intentionalMentions) || false; - } - /** - * Fetches the summary of a room as defined by an initial version of MSC3266 and implemented in Synapse - * Proposed at https://github.com/matrix-org/matrix-doc/pull/3266 - * @param roomIdOrAlias - The ID or alias of the room to get the summary of. - * @param via - The list of servers which know about the room if only an ID was provided. - */ - getRoomSummary(roomIdOrAlias, via) { - return __awaiter(this, void 0, void 0, function* () { - const path = utils.encodeUri("/rooms/$roomid/summary", { $roomid: roomIdOrAlias }); - return this.http.authedRequest(http_api_1.Method.Get, path, { via }, undefined, { - prefix: "/_matrix/client/unstable/im.nheko.summary", - }); - }); - } - /** - * Processes a list of threaded events and adds them to their respective timelines - * @param room - the room the adds the threaded events - * @param threadedEvents - an array of the threaded events - * @param toStartOfTimeline - the direction in which we want to add the events - */ - processThreadEvents(room, threadedEvents, toStartOfTimeline) { - room.processThreadedEvents(threadedEvents, toStartOfTimeline); - } - /** - * Processes a list of thread roots and creates a thread model - * @param room - the room to create the threads in - * @param threadedEvents - an array of thread roots - * @param toStartOfTimeline - the direction - */ - processThreadRoots(room, threadedEvents, toStartOfTimeline) { - room.processThreadRoots(threadedEvents, toStartOfTimeline); - } - processBeaconEvents(room, events) { - this.processAggregatedTimelineEvents(room, events); - } - /** - * Calls aggregation functions for event types that are aggregated - * Polls and location beacons - * @param room - room the events belong to - * @param events - timeline events to be processed - * @returns - */ - processAggregatedTimelineEvents(room, events) { - if (!(events === null || events === void 0 ? void 0 : events.length)) - return; - if (!room) - return; - room.currentState.processBeaconEvents(events, this); - room.processPollEvents(events); - } - /** - * Fetches information about the user for the configured access token. - */ - whoami() { - return __awaiter(this, void 0, void 0, function* () { - return this.http.authedRequest(http_api_1.Method.Get, "/account/whoami"); - }); - } - /** - * Find the event_id closest to the given timestamp in the given direction. - * @returns Resolves: A promise of an object containing the event_id and - * origin_server_ts of the closest event to the timestamp in the given direction - * @returns Rejects: when the request fails (module:http-api.MatrixError) - */ - timestampToEvent(roomId, timestamp, dir) { - return __awaiter(this, void 0, void 0, function* () { - const path = utils.encodeUri("/rooms/$roomId/timestamp_to_event", { - $roomId: roomId, - }); - const queryParams = { - ts: timestamp.toString(), - dir: dir, - }; - try { - return yield this.http.authedRequest(http_api_1.Method.Get, path, queryParams, undefined, { - prefix: http_api_1.ClientPrefix.V1, - }); - } - catch (err) { - // Fallback to the prefixed unstable endpoint. Since the stable endpoint is - // new, we should also try the unstable endpoint before giving up. We can - // remove this fallback request in a year (remove after 2023-11-28). - if (err.errcode === "M_UNRECOGNIZED" && - // XXX: The 400 status code check should be removed in the future - // when Synapse is compliant with MSC3743. - (err.httpStatus === 400 || - // This the correct standard status code for an unsupported - // endpoint according to MSC3743. Not Found and Method Not Allowed - // both indicate that this endpoint+verb combination is - // not supported. - err.httpStatus === 404 || - err.httpStatus === 405)) { - return yield this.http.authedRequest(http_api_1.Method.Get, path, queryParams, undefined, { - prefix: "/_matrix/client/unstable/org.matrix.msc3030", - }); - } - throw err; - } - }); - } -} -exports.MatrixClient = MatrixClient; -MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY = "RESTORE_BACKUP_ERROR_BAD_KEY"; -/** - * recalculates an accurate notifications count on event decryption. - * Servers do not have enough knowledge about encrypted events to calculate an - * accurate notification_count - */ -function fixNotificationCountOnDecryption(cli, event) { - var _a, _b; - const ourUserId = cli.getUserId(); - const eventId = event.getId(); - const room = cli.getRoom(event.getRoomId()); - if (!room || !ourUserId || !eventId) - return; - const oldActions = event.getPushActions(); - const actions = cli.getPushActionsForEvent(event, true); - const isThreadEvent = !!event.threadRootId && !event.isThreadRoot; - const currentHighlightCount = room.getUnreadCountForEventContext(room_1.NotificationCountType.Highlight, event); - // Ensure the unread counts are kept up to date if the event is encrypted - // We also want to make sure that the notification count goes up if we already - // have encrypted events to avoid other code from resetting 'highlight' to zero. - const oldHighlight = !!((_a = oldActions === null || oldActions === void 0 ? void 0 : oldActions.tweaks) === null || _a === void 0 ? void 0 : _a.highlight); - const newHighlight = !!((_b = actions === null || actions === void 0 ? void 0 : actions.tweaks) === null || _b === void 0 ? void 0 : _b.highlight); - let hasReadEvent; - if (isThreadEvent) { - const thread = room.getThread(event.threadRootId); - hasReadEvent = thread - ? thread.hasUserReadEvent(ourUserId, eventId) - : // If the thread object does not exist in the room yet, we don't - // want to calculate notification for this event yet. We have not - // restored the read receipts yet and can't accurately calculate - // notifications at this stage. - // - // This issue can likely go away when MSC3874 is implemented - true; - } - else { - hasReadEvent = room.hasUserReadEvent(ourUserId, eventId); - } - if (hasReadEvent) { - // If the event has been read, ignore it. - return; - } - if (oldHighlight !== newHighlight || currentHighlightCount > 0) { - // TODO: Handle mentions received while the client is offline - // See also https://github.com/vector-im/element-web/issues/9069 - let newCount = currentHighlightCount; - if (newHighlight && !oldHighlight) - newCount++; - if (!newHighlight && oldHighlight) - newCount--; - if (isThreadEvent) { - room.setThreadUnreadNotificationCount(event.threadRootId, room_1.NotificationCountType.Highlight, newCount); - } - else { - room.setUnreadNotificationCount(room_1.NotificationCountType.Highlight, newCount); - } - } - // Total count is used to typically increment a room notification counter, but not loudly highlight it. - const currentTotalCount = room.getUnreadCountForEventContext(room_1.NotificationCountType.Total, event); - // `notify` is used in practice for incrementing the total count - const newNotify = !!(actions === null || actions === void 0 ? void 0 : actions.notify); - // The room total count is NEVER incremented by the server for encrypted rooms. We basically ignore - // the server here as it's always going to tell us to increment for encrypted events. - if (newNotify) { - if (isThreadEvent) { - room.setThreadUnreadNotificationCount(event.threadRootId, room_1.NotificationCountType.Total, currentTotalCount + 1); - } - else { - room.setUnreadNotificationCount(room_1.NotificationCountType.Total, currentTotalCount + 1); - } - } -} -exports.fixNotificationCountOnDecryption = fixNotificationCountOnDecryption; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./@types/PushRules":304,"./@types/beacon":305,"./@types/event":306,"./@types/partials":309,"./@types/read_receipts":311,"./@types/search":313,"./NamespacedValue":316,"./ReEmitter":317,"./ToDeviceMessageQueue":318,"./autodiscovery":319,"./content-helpers":322,"./content-repo":323,"./crypto":341,"./crypto/RoomList":329,"./crypto/api":336,"./crypto/backup":337,"./crypto/dehydration":339,"./crypto/key_passphrase":342,"./crypto/olmlib":343,"./crypto/recoverykey":344,"./event-mapper":360,"./feature":362,"./filter":364,"./http-api":367,"./logger":374,"./models/MSC3089TreeSpace":377,"./models/event":383,"./models/event-timeline":382,"./models/invites-ignorer":384,"./models/room":392,"./models/room-member":389,"./models/search-result":393,"./models/thread":394,"./models/typed-event-emitter":395,"./models/user":396,"./pushprocessor":397,"./randomstring":398,"./rust-crypto":402,"./rust-crypto/constants":401,"./service-types":405,"./sliding-sync-sdk":406,"./store/stub":412,"./sync":414,"./utils":416,"./webrtc/call":418,"./webrtc/callEventHandler":419,"./webrtc/groupCall":422,"./webrtc/groupCallEventHandler":423,"./webrtc/mediaHandler":424}],322:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseBeaconContent = exports.makeBeaconContent = exports.parseBeaconInfoContent = exports.makeBeaconInfoContent = exports.parseTopicContent = exports.makeTopicContent = exports.parseLocationEvent = exports.makeLocationContent = exports.getTextForLocationEvent = exports.makeEmoteMessage = exports.makeNotice = exports.makeTextMessage = exports.makeHtmlEmote = exports.makeHtmlNotice = exports.makeHtmlMessage = void 0; -const event_1 = require("./@types/event"); -const extensible_events_1 = require("./@types/extensible_events"); -const utilities_1 = require("./extensible_events_v1/utilities"); -const location_1 = require("./@types/location"); -const topic_1 = require("./@types/topic"); -/** - * Generates the content for a HTML Message event - * @param body - the plaintext body of the message - * @param htmlBody - the HTML representation of the message - * @returns - */ -function makeHtmlMessage(body, htmlBody) { - return { - msgtype: event_1.MsgType.Text, - format: "org.matrix.custom.html", - body: body, - formatted_body: htmlBody, - }; -} -exports.makeHtmlMessage = makeHtmlMessage; -/** - * Generates the content for a HTML Notice event - * @param body - the plaintext body of the notice - * @param htmlBody - the HTML representation of the notice - * @returns - */ -function makeHtmlNotice(body, htmlBody) { - return { - msgtype: event_1.MsgType.Notice, - format: "org.matrix.custom.html", - body: body, - formatted_body: htmlBody, - }; -} -exports.makeHtmlNotice = makeHtmlNotice; -/** - * Generates the content for a HTML Emote event - * @param body - the plaintext body of the emote - * @param htmlBody - the HTML representation of the emote - * @returns - */ -function makeHtmlEmote(body, htmlBody) { - return { - msgtype: event_1.MsgType.Emote, - format: "org.matrix.custom.html", - body: body, - formatted_body: htmlBody, - }; -} -exports.makeHtmlEmote = makeHtmlEmote; -/** - * Generates the content for a Plaintext Message event - * @param body - the plaintext body of the emote - * @returns - */ -function makeTextMessage(body) { - return { - msgtype: event_1.MsgType.Text, - body: body, - }; -} -exports.makeTextMessage = makeTextMessage; -/** - * Generates the content for a Plaintext Notice event - * @param body - the plaintext body of the notice - * @returns - */ -function makeNotice(body) { - return { - msgtype: event_1.MsgType.Notice, - body: body, - }; -} -exports.makeNotice = makeNotice; -/** - * Generates the content for a Plaintext Emote event - * @param body - the plaintext body of the emote - * @returns - */ -function makeEmoteMessage(body) { - return { - msgtype: event_1.MsgType.Emote, - body: body, - }; -} -exports.makeEmoteMessage = makeEmoteMessage; -/** Location content helpers */ -const getTextForLocationEvent = (uri, assetType, timestamp, description) => { - const date = `at ${new Date(timestamp).toISOString()}`; - const assetName = assetType === location_1.LocationAssetType.Self ? "User" : undefined; - const quotedDescription = description ? `"${description}"` : undefined; - return [assetName, "Location", quotedDescription, uri, date].filter(Boolean).join(" "); -}; -exports.getTextForLocationEvent = getTextForLocationEvent; -/** - * Generates the content for a Location event - * @param uri - a geo:// uri for the location - * @param timestamp - the timestamp when the location was correct (milliseconds since the UNIX epoch) - * @param description - the (optional) label for this location on the map - * @param assetType - the (optional) asset type of this location e.g. "m.self" - * @param text - optional. A text for the location - */ -const makeLocationContent = ( -// this is first but optional -// to avoid a breaking change -text, uri, timestamp, description, assetType) => { - const defaultedText = text !== null && text !== void 0 ? text : (0, exports.getTextForLocationEvent)(uri, assetType || location_1.LocationAssetType.Self, timestamp, description); - const timestampEvent = timestamp ? { [location_1.M_TIMESTAMP.name]: timestamp } : {}; - return Object.assign({ msgtype: event_1.MsgType.Location, body: defaultedText, geo_uri: uri, [location_1.M_LOCATION.name]: { - description, - uri, - }, [location_1.M_ASSET.name]: { - type: assetType || location_1.LocationAssetType.Self, - }, [extensible_events_1.M_TEXT.name]: defaultedText }, timestampEvent); -}; -exports.makeLocationContent = makeLocationContent; -/** - * Parse location event content and transform to - * a backwards compatible modern m.location event format - */ -const parseLocationEvent = (wireEventContent) => { - var _a, _b; - const location = location_1.M_LOCATION.findIn(wireEventContent); - const asset = location_1.M_ASSET.findIn(wireEventContent); - const timestamp = location_1.M_TIMESTAMP.findIn(wireEventContent); - const text = extensible_events_1.M_TEXT.findIn(wireEventContent); - const geoUri = (_a = location === null || location === void 0 ? void 0 : location.uri) !== null && _a !== void 0 ? _a : wireEventContent === null || wireEventContent === void 0 ? void 0 : wireEventContent.geo_uri; - const description = location === null || location === void 0 ? void 0 : location.description; - const assetType = (_b = asset === null || asset === void 0 ? void 0 : asset.type) !== null && _b !== void 0 ? _b : location_1.LocationAssetType.Self; - const fallbackText = text !== null && text !== void 0 ? text : wireEventContent.body; - return (0, exports.makeLocationContent)(fallbackText, geoUri, timestamp !== null && timestamp !== void 0 ? timestamp : undefined, description, assetType); -}; -exports.parseLocationEvent = parseLocationEvent; -const makeTopicContent = (topic, htmlTopic) => { - const renderings = [{ body: topic, mimetype: "text/plain" }]; - if ((0, utilities_1.isProvided)(htmlTopic)) { - renderings.push({ body: htmlTopic, mimetype: "text/html" }); - } - return { topic, [topic_1.M_TOPIC.name]: renderings }; -}; -exports.makeTopicContent = makeTopicContent; -const parseTopicContent = (content) => { - var _a, _b, _c; - const mtopic = topic_1.M_TOPIC.findIn(content); - if (!Array.isArray(mtopic)) { - return { text: content.topic }; - } - const text = (_b = (_a = mtopic === null || mtopic === void 0 ? void 0 : mtopic.find((r) => !(0, utilities_1.isProvided)(r.mimetype) || r.mimetype === "text/plain")) === null || _a === void 0 ? void 0 : _a.body) !== null && _b !== void 0 ? _b : content.topic; - const html = (_c = mtopic === null || mtopic === void 0 ? void 0 : mtopic.find((r) => r.mimetype === "text/html")) === null || _c === void 0 ? void 0 : _c.body; - return { text, html }; -}; -exports.parseTopicContent = parseTopicContent; -const makeBeaconInfoContent = (timeout, isLive, description, assetType, timestamp) => ({ - description, - timeout, - live: isLive, - [location_1.M_TIMESTAMP.name]: timestamp || Date.now(), - [location_1.M_ASSET.name]: { - type: assetType !== null && assetType !== void 0 ? assetType : location_1.LocationAssetType.Self, - }, -}); -exports.makeBeaconInfoContent = makeBeaconInfoContent; -/** - * Flatten beacon info event content - */ -const parseBeaconInfoContent = (content) => { - var _a; - const { description, timeout, live } = content; - const timestamp = (_a = location_1.M_TIMESTAMP.findIn(content)) !== null && _a !== void 0 ? _a : undefined; - const asset = location_1.M_ASSET.findIn(content); - return { - description, - timeout, - live, - assetType: asset === null || asset === void 0 ? void 0 : asset.type, - timestamp, - }; -}; -exports.parseBeaconInfoContent = parseBeaconInfoContent; -const makeBeaconContent = (uri, timestamp, beaconInfoEventId, description) => ({ - [location_1.M_LOCATION.name]: { - description, - uri, - }, - [location_1.M_TIMESTAMP.name]: timestamp, - "m.relates_to": { - rel_type: extensible_events_1.REFERENCE_RELATION.name, - event_id: beaconInfoEventId, - }, -}); -exports.makeBeaconContent = makeBeaconContent; -const parseBeaconContent = (content) => { - var _a; - const location = location_1.M_LOCATION.findIn(content); - const timestamp = (_a = location_1.M_TIMESTAMP.findIn(content)) !== null && _a !== void 0 ? _a : undefined; - return { - description: location === null || location === void 0 ? void 0 : location.description, - uri: location === null || location === void 0 ? void 0 : location.uri, - timestamp, - }; -}; -exports.parseBeaconContent = parseBeaconContent; - -},{"./@types/event":306,"./@types/extensible_events":307,"./@types/location":308,"./@types/topic":315,"./extensible_events_v1/utilities":361}],323:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getHttpUriForMxc = void 0; -const utils = __importStar(require("./utils")); -/** - * Get the HTTP URL for an MXC URI. - * @param baseUrl - The base homeserver url which has a content repo. - * @param mxc - The mxc:// URI. - * @param width - The desired width of the thumbnail. - * @param height - The desired height of the thumbnail. - * @param resizeMethod - The thumbnail resize method to use, either - * "crop" or "scale". - * @param allowDirectLinks - If true, return any non-mxc URLs - * directly. Fetching such URLs will leak information about the user to - * anyone they share a room with. If false, will return the emptry string - * for such URLs. - * @returns The complete URL to the content. - */ -function getHttpUriForMxc(baseUrl, mxc, width, height, resizeMethod, allowDirectLinks = false) { - if (typeof mxc !== "string" || !mxc) { - return ""; - } - if (mxc.indexOf("mxc://") !== 0) { - if (allowDirectLinks) { - return mxc; - } - else { - return ""; - } - } - let serverAndMediaId = mxc.slice(6); // strips mxc:// - let prefix = "/_matrix/media/r0/download/"; - const params = {}; - if (width) { - params["width"] = Math.round(width).toString(); - } - if (height) { - params["height"] = Math.round(height).toString(); - } - if (resizeMethod) { - params["method"] = resizeMethod; - } - if (Object.keys(params).length > 0) { - // these are thumbnailing params so they probably want the - // thumbnailing API... - prefix = "/_matrix/media/r0/thumbnail/"; - } - const fragmentOffset = serverAndMediaId.indexOf("#"); - let fragment = ""; - if (fragmentOffset >= 0) { - fragment = serverAndMediaId.slice(fragmentOffset); - serverAndMediaId = serverAndMediaId.slice(0, fragmentOffset); - } - const urlParams = Object.keys(params).length === 0 ? "" : "?" + utils.encodeParams(params); - return baseUrl + prefix + serverAndMediaId + urlParams + fragment; -} -exports.getHttpUriForMxc = getHttpUriForMxc; - -},{"./utils":416}],324:[function(require,module,exports){ -(function (global,Buffer){(function (){ -"use strict"; -/* -Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.requestKeysDuringVerification = exports.createCryptoStoreCacheCallbacks = exports.DeviceTrustLevel = exports.UserTrustLevel = exports.CrossSigningLevel = exports.CrossSigningInfo = void 0; -const olmlib_1 = require("./olmlib"); -const logger_1 = require("../logger"); -const indexeddb_crypto_store_1 = require("../crypto/store/indexeddb-crypto-store"); -const aes_1 = require("./aes"); -const KEY_REQUEST_TIMEOUT_MS = 1000 * 60; -function publicKeyFromKeyInfo(keyInfo) { - // `keys` is an object with { [`ed25519:${pubKey}`]: pubKey } - // We assume only a single key, and we want the bare form without type - // prefix, so we select the values. - return Object.values(keyInfo.keys)[0]; -} -class CrossSigningInfo { - /** - * Information about a user's cross-signing keys - * - * @param userId - the user that the information is about - * @param callbacks - Callbacks used to interact with the app - * Requires getCrossSigningKey and saveCrossSigningKeys - * @param cacheCallbacks - Callbacks used to interact with the cache - */ - constructor(userId, callbacks = {}, cacheCallbacks = {}) { - this.userId = userId; - this.callbacks = callbacks; - this.cacheCallbacks = cacheCallbacks; - this.keys = {}; - this.firstUse = true; - // This tracks whether we've ever verified this user with any identity. - // When you verify a user, any devices online at the time that receive - // the verifying signature via the homeserver will latch this to true - // and can use it in the future to detect cases where the user has - // become unverified later for any reason. - this.crossSigningVerifiedBefore = false; - } - static fromStorage(obj, userId) { - const res = new CrossSigningInfo(userId); - for (const prop in obj) { - if (obj.hasOwnProperty(prop)) { - // @ts-ignore - ts doesn't like this and nor should we - res[prop] = obj[prop]; - } - } - return res; - } - toStorage() { - return { - keys: this.keys, - firstUse: this.firstUse, - crossSigningVerifiedBefore: this.crossSigningVerifiedBefore, - }; - } - /** - * Calls the app callback to ask for a private key - * - * @param type - The key type ("master", "self_signing", or "user_signing") - * @param expectedPubkey - The matching public key or undefined to use - * the stored public key for the given key type. - * @returns An array with [ public key, Olm.PkSigning ] - */ - getCrossSigningKey(type, expectedPubkey) { - return __awaiter(this, void 0, void 0, function* () { - const shouldCache = ["master", "self_signing", "user_signing"].indexOf(type) >= 0; - if (!this.callbacks.getCrossSigningKey) { - throw new Error("No getCrossSigningKey callback supplied"); - } - if (expectedPubkey === undefined) { - expectedPubkey = this.getId(type); - } - function validateKey(key) { - if (!key) - return; - const signing = new global.Olm.PkSigning(); - const gotPubkey = signing.init_with_seed(key); - if (gotPubkey === expectedPubkey) { - return [gotPubkey, signing]; - } - signing.free(); - } - let privkey = null; - if (this.cacheCallbacks.getCrossSigningKeyCache && shouldCache) { - privkey = yield this.cacheCallbacks.getCrossSigningKeyCache(type, expectedPubkey); - } - const cacheresult = validateKey(privkey); - if (cacheresult) { - return cacheresult; - } - privkey = yield this.callbacks.getCrossSigningKey(type, expectedPubkey); - const result = validateKey(privkey); - if (result) { - if (this.cacheCallbacks.storeCrossSigningKeyCache && shouldCache) { - yield this.cacheCallbacks.storeCrossSigningKeyCache(type, privkey); - } - return result; - } - /* No keysource even returned a key */ - if (!privkey) { - throw new Error("getCrossSigningKey callback for " + type + " returned falsey"); - } - /* We got some keys from the keysource, but none of them were valid */ - throw new Error("Key type " + type + " from getCrossSigningKey callback did not match"); - }); - } - /** - * Check whether the private keys exist in secret storage. - * XXX: This could be static, be we often seem to have an instance when we - * want to know this anyway... - * - * @param secretStorage - The secret store using account data - * @returns map of key name to key info the secret is encrypted - * with, or null if it is not present or not encrypted with a trusted - * key - */ - isStoredInSecretStorage(secretStorage) { - return __awaiter(this, void 0, void 0, function* () { - // check what SSSS keys have encrypted the master key (if any) - const stored = (yield secretStorage.isStored("m.cross_signing.master")) || {}; - // then check which of those SSSS keys have also encrypted the SSK and USK - function intersect(s) { - for (const k of Object.keys(stored)) { - if (!s[k]) { - delete stored[k]; - } - } - } - for (const type of ["self_signing", "user_signing"]) { - intersect((yield secretStorage.isStored(`m.cross_signing.${type}`)) || {}); - } - return Object.keys(stored).length ? stored : null; - }); - } - /** - * Store private keys in secret storage for use by other devices. This is - * typically called in conjunction with the creation of new cross-signing - * keys. - * - * @param keys - The keys to store - * @param secretStorage - The secret store using account data - */ - static storeInSecretStorage(keys, secretStorage) { - return __awaiter(this, void 0, void 0, function* () { - for (const [type, privateKey] of keys) { - const encodedKey = (0, olmlib_1.encodeBase64)(privateKey); - yield secretStorage.store(`m.cross_signing.${type}`, encodedKey); - } - }); - } - /** - * Get private keys from secret storage created by some other device. This - * also passes the private keys to the app-specific callback. - * - * @param type - The type of key to get. One of "master", - * "self_signing", or "user_signing". - * @param secretStorage - The secret store using account data - * @returns The private key - */ - static getFromSecretStorage(type, secretStorage) { - return __awaiter(this, void 0, void 0, function* () { - const encodedKey = yield secretStorage.get(`m.cross_signing.${type}`); - if (!encodedKey) { - return null; - } - return (0, olmlib_1.decodeBase64)(encodedKey); - }); - } - /** - * Check whether the private keys exist in the local key cache. - * - * @param type - The type of key to get. One of "master", - * "self_signing", or "user_signing". Optional, will check all by default. - * @returns True if all keys are stored in the local cache. - */ - isStoredInKeyCache(type) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const cacheCallbacks = this.cacheCallbacks; - if (!cacheCallbacks) - return false; - const types = type ? [type] : ["master", "self_signing", "user_signing"]; - for (const t of types) { - if (!(yield ((_a = cacheCallbacks.getCrossSigningKeyCache) === null || _a === void 0 ? void 0 : _a.call(cacheCallbacks, t)))) { - return false; - } - } - return true; - }); - } - /** - * Get cross-signing private keys from the local cache. - * - * @returns A map from key type (string) to private key (Uint8Array) - */ - getCrossSigningKeysFromCache() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const keys = new Map(); - const cacheCallbacks = this.cacheCallbacks; - if (!cacheCallbacks) - return keys; - for (const type of ["master", "self_signing", "user_signing"]) { - const privKey = yield ((_a = cacheCallbacks.getCrossSigningKeyCache) === null || _a === void 0 ? void 0 : _a.call(cacheCallbacks, type)); - if (!privKey) { - continue; - } - keys.set(type, privKey); - } - return keys; - }); - } - /** - * Get the ID used to identify the user. This can also be used to test for - * the existence of a given key type. - * - * @param type - The type of key to get the ID of. One of "master", - * "self_signing", or "user_signing". Defaults to "master". - * - * @returns the ID - */ - getId(type = "master") { - if (!this.keys[type]) - return null; - const keyInfo = this.keys[type]; - return publicKeyFromKeyInfo(keyInfo); - } - /** - * Create new cross-signing keys for the given key types. The public keys - * will be held in this class, while the private keys are passed off to the - * `saveCrossSigningKeys` application callback. - * - * @param level - The key types to reset - */ - resetKeys(level) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.callbacks.saveCrossSigningKeys) { - throw new Error("No saveCrossSigningKeys callback supplied"); - } - // If we're resetting the master key, we reset all keys - if (level === undefined || level & CrossSigningLevel.MASTER || !this.keys.master) { - level = CrossSigningLevel.MASTER | CrossSigningLevel.USER_SIGNING | CrossSigningLevel.SELF_SIGNING; - } - else if (level === 0) { - return; - } - const privateKeys = {}; - const keys = {}; - let masterSigning; - let masterPub; - try { - if (level & CrossSigningLevel.MASTER) { - masterSigning = new global.Olm.PkSigning(); - privateKeys.master = masterSigning.generate_seed(); - masterPub = masterSigning.init_with_seed(privateKeys.master); - keys.master = { - user_id: this.userId, - usage: ["master"], - keys: { - ["ed25519:" + masterPub]: masterPub, - }, - }; - } - else { - [masterPub, masterSigning] = yield this.getCrossSigningKey("master"); - } - if (level & CrossSigningLevel.SELF_SIGNING) { - const sskSigning = new global.Olm.PkSigning(); - try { - privateKeys.self_signing = sskSigning.generate_seed(); - const sskPub = sskSigning.init_with_seed(privateKeys.self_signing); - keys.self_signing = { - user_id: this.userId, - usage: ["self_signing"], - keys: { - ["ed25519:" + sskPub]: sskPub, - }, - }; - (0, olmlib_1.pkSign)(keys.self_signing, masterSigning, this.userId, masterPub); - } - finally { - sskSigning.free(); - } - } - if (level & CrossSigningLevel.USER_SIGNING) { - const uskSigning = new global.Olm.PkSigning(); - try { - privateKeys.user_signing = uskSigning.generate_seed(); - const uskPub = uskSigning.init_with_seed(privateKeys.user_signing); - keys.user_signing = { - user_id: this.userId, - usage: ["user_signing"], - keys: { - ["ed25519:" + uskPub]: uskPub, - }, - }; - (0, olmlib_1.pkSign)(keys.user_signing, masterSigning, this.userId, masterPub); - } - finally { - uskSigning.free(); - } - } - Object.assign(this.keys, keys); - this.callbacks.saveCrossSigningKeys(privateKeys); - } - finally { - if (masterSigning) { - masterSigning.free(); - } - } - }); - } - /** - * unsets the keys, used when another session has reset the keys, to disable cross-signing - */ - clearKeys() { - this.keys = {}; - } - setKeys(keys) { - const signingKeys = {}; - if (keys.master) { - if (keys.master.user_id !== this.userId) { - const error = "Mismatched user ID " + keys.master.user_id + " in master key from " + this.userId; - logger_1.logger.error(error); - throw new Error(error); - } - if (!this.keys.master) { - // this is the first key we've seen, so first-use is true - this.firstUse = true; - } - else if (publicKeyFromKeyInfo(keys.master) !== this.getId()) { - // this is a different key, so first-use is false - this.firstUse = false; - } // otherwise, same key, so no change - signingKeys.master = keys.master; - } - else if (this.keys.master) { - signingKeys.master = this.keys.master; - } - else { - throw new Error("Tried to set cross-signing keys without a master key"); - } - const masterKey = publicKeyFromKeyInfo(signingKeys.master); - // verify signatures - if (keys.user_signing) { - if (keys.user_signing.user_id !== this.userId) { - const error = "Mismatched user ID " + keys.master.user_id + " in user_signing key from " + this.userId; - logger_1.logger.error(error); - throw new Error(error); - } - try { - (0, olmlib_1.pkVerify)(keys.user_signing, masterKey, this.userId); - } - catch (e) { - logger_1.logger.error("invalid signature on user-signing key"); - // FIXME: what do we want to do here? - throw e; - } - } - if (keys.self_signing) { - if (keys.self_signing.user_id !== this.userId) { - const error = "Mismatched user ID " + keys.master.user_id + " in self_signing key from " + this.userId; - logger_1.logger.error(error); - throw new Error(error); - } - try { - (0, olmlib_1.pkVerify)(keys.self_signing, masterKey, this.userId); - } - catch (e) { - logger_1.logger.error("invalid signature on self-signing key"); - // FIXME: what do we want to do here? - throw e; - } - } - // if everything checks out, then save the keys - if (keys.master) { - this.keys.master = keys.master; - // if the master key is set, then the old self-signing and user-signing keys are obsolete - delete this.keys["self_signing"]; - delete this.keys["user_signing"]; - } - if (keys.self_signing) { - this.keys.self_signing = keys.self_signing; - } - if (keys.user_signing) { - this.keys.user_signing = keys.user_signing; - } - } - updateCrossSigningVerifiedBefore(isCrossSigningVerified) { - // It is critical that this value latches forward from false to true but - // never back to false to avoid a downgrade attack. - if (!this.crossSigningVerifiedBefore && isCrossSigningVerified) { - this.crossSigningVerifiedBefore = true; - } - } - signObject(data, type) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.keys[type]) { - throw new Error("Attempted to sign with " + type + " key but no such key present"); - } - const [pubkey, signing] = yield this.getCrossSigningKey(type); - try { - (0, olmlib_1.pkSign)(data, signing, this.userId, pubkey); - return data; - } - finally { - signing.free(); - } - }); - } - signUser(key) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.keys.user_signing) { - logger_1.logger.info("No user signing key: not signing user"); - return; - } - return this.signObject(key.keys.master, "user_signing"); - }); - } - signDevice(userId, device) { - return __awaiter(this, void 0, void 0, function* () { - if (userId !== this.userId) { - throw new Error(`Trying to sign ${userId}'s device; can only sign our own device`); - } - if (!this.keys.self_signing) { - logger_1.logger.info("No self signing key: not signing device"); - return; - } - return this.signObject({ - algorithms: device.algorithms, - keys: device.keys, - device_id: device.deviceId, - user_id: userId, - }, "self_signing"); - }); - } - /** - * Check whether a given user is trusted. - * - * @param userCrossSigning - Cross signing info for user - * - * @returns - */ - checkUserTrust(userCrossSigning) { - // if we're checking our own key, then it's trusted if the master key - // and self-signing key match - if (this.userId === userCrossSigning.userId && - this.getId() && - this.getId() === userCrossSigning.getId() && - this.getId("self_signing") && - this.getId("self_signing") === userCrossSigning.getId("self_signing")) { - return new UserTrustLevel(true, true, this.firstUse); - } - if (!this.keys.user_signing) { - // If there's no user signing key, they can't possibly be verified. - // They may be TOFU trusted though. - return new UserTrustLevel(false, false, userCrossSigning.firstUse); - } - let userTrusted; - const userMaster = userCrossSigning.keys.master; - const uskId = this.getId("user_signing"); - try { - (0, olmlib_1.pkVerify)(userMaster, uskId, this.userId); - userTrusted = true; - } - catch (e) { - userTrusted = false; - } - return new UserTrustLevel(userTrusted, userCrossSigning.crossSigningVerifiedBefore, userCrossSigning.firstUse); - } - /** - * Check whether a given device is trusted. - * - * @param userCrossSigning - Cross signing info for user - * @param device - The device to check - * @param localTrust - Whether the device is trusted locally - * @param trustCrossSignedDevices - Whether we trust cross signed devices - * - * @returns - */ - checkDeviceTrust(userCrossSigning, device, localTrust, trustCrossSignedDevices) { - const userTrust = this.checkUserTrust(userCrossSigning); - const userSSK = userCrossSigning.keys.self_signing; - if (!userSSK) { - // if the user has no self-signing key then we cannot make any - // trust assertions about this device from cross-signing - return new DeviceTrustLevel(false, false, localTrust, trustCrossSignedDevices); - } - const deviceObj = deviceToObject(device, userCrossSigning.userId); - try { - // if we can verify the user's SSK from their master key... - (0, olmlib_1.pkVerify)(userSSK, userCrossSigning.getId(), userCrossSigning.userId); - // ...and this device's key from their SSK... - (0, olmlib_1.pkVerify)(deviceObj, publicKeyFromKeyInfo(userSSK), userCrossSigning.userId); - // ...then we trust this device as much as far as we trust the user - return DeviceTrustLevel.fromUserTrustLevel(userTrust, localTrust, trustCrossSignedDevices); - } - catch (e) { - return new DeviceTrustLevel(false, false, localTrust, trustCrossSignedDevices); - } - } - /** - * @returns Cache callbacks - */ - getCacheCallbacks() { - return this.cacheCallbacks; - } -} -exports.CrossSigningInfo = CrossSigningInfo; -function deviceToObject(device, userId) { - return { - algorithms: device.algorithms, - keys: device.keys, - device_id: device.deviceId, - user_id: userId, - signatures: device.signatures, - }; -} -var CrossSigningLevel; -(function (CrossSigningLevel) { - CrossSigningLevel[CrossSigningLevel["MASTER"] = 4] = "MASTER"; - CrossSigningLevel[CrossSigningLevel["USER_SIGNING"] = 2] = "USER_SIGNING"; - CrossSigningLevel[CrossSigningLevel["SELF_SIGNING"] = 1] = "SELF_SIGNING"; -})(CrossSigningLevel = exports.CrossSigningLevel || (exports.CrossSigningLevel = {})); -/** - * Represents the ways in which we trust a user - */ -class UserTrustLevel { - constructor(crossSigningVerified, crossSigningVerifiedBefore, tofu) { - this.crossSigningVerified = crossSigningVerified; - this.crossSigningVerifiedBefore = crossSigningVerifiedBefore; - this.tofu = tofu; - } - /** - * @returns true if this user is verified via any means - */ - isVerified() { - return this.isCrossSigningVerified(); - } - /** - * @returns true if this user is verified via cross signing - */ - isCrossSigningVerified() { - return this.crossSigningVerified; - } - /** - * @returns true if we ever verified this user before (at least for - * the history of verifications observed by this device). - */ - wasCrossSigningVerified() { - return this.crossSigningVerifiedBefore; - } - /** - * @returns true if this user's key is trusted on first use - */ - isTofu() { - return this.tofu; - } -} -exports.UserTrustLevel = UserTrustLevel; -/** - * Represents the ways in which we trust a device - */ -class DeviceTrustLevel { - constructor(crossSigningVerified, tofu, localVerified, trustCrossSignedDevices) { - this.crossSigningVerified = crossSigningVerified; - this.tofu = tofu; - this.localVerified = localVerified; - this.trustCrossSignedDevices = trustCrossSignedDevices; - } - static fromUserTrustLevel(userTrustLevel, localVerified, trustCrossSignedDevices) { - return new DeviceTrustLevel(userTrustLevel.isCrossSigningVerified(), userTrustLevel.isTofu(), localVerified, trustCrossSignedDevices); - } - /** - * @returns true if this device is verified via any means - */ - isVerified() { - return Boolean(this.isLocallyVerified() || (this.trustCrossSignedDevices && this.isCrossSigningVerified())); - } - /** - * @returns true if this device is verified via cross signing - */ - isCrossSigningVerified() { - return this.crossSigningVerified; - } - /** - * @returns true if this device is verified locally - */ - isLocallyVerified() { - return this.localVerified; - } - /** - * @returns true if this device is trusted from a user's key - * that is trusted on first use - */ - isTofu() { - return this.tofu; - } -} -exports.DeviceTrustLevel = DeviceTrustLevel; -function createCryptoStoreCacheCallbacks(store, olmDevice) { - return { - getCrossSigningKeyCache: function (type, _expectedPublicKey) { - return __awaiter(this, void 0, void 0, function* () { - const key = yield new Promise((resolve) => { - return store.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - store.getSecretStorePrivateKey(txn, resolve, type); - }); - }); - if (key && key.ciphertext) { - const pickleKey = Buffer.from(olmDevice.pickleKey); - const decrypted = yield (0, aes_1.decryptAES)(key, pickleKey, type); - return (0, olmlib_1.decodeBase64)(decrypted); - } - else { - return key; - } - }); - }, - storeCrossSigningKeyCache: function (type, key) { - return __awaiter(this, void 0, void 0, function* () { - if (!(key instanceof Uint8Array)) { - throw new Error(`storeCrossSigningKeyCache expects Uint8Array, got ${key}`); - } - const pickleKey = Buffer.from(olmDevice.pickleKey); - const encryptedKey = yield (0, aes_1.encryptAES)((0, olmlib_1.encodeBase64)(key), pickleKey, type); - return store.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - store.storeSecretStorePrivateKey(txn, type, encryptedKey); - }); - }); - }, - }; -} -exports.createCryptoStoreCacheCallbacks = createCryptoStoreCacheCallbacks; -/** - * Request cross-signing keys from another device during verification. - * - * @param baseApis - base Matrix API interface - * @param userId - The user ID being verified - * @param deviceId - The device ID being verified - */ -function requestKeysDuringVerification(baseApis, userId, deviceId) { - return __awaiter(this, void 0, void 0, function* () { - // If this is a self-verification, ask the other party for keys - if (baseApis.getUserId() !== userId) { - return; - } - logger_1.logger.log("Cross-signing: Self-verification done; requesting keys"); - // This happens asynchronously, and we're not concerned about waiting for - // it. We return here in order to test. - return new Promise((resolve, reject) => { - const client = baseApis; - const original = client.crypto.crossSigningInfo; - // We already have all of the infrastructure we need to validate and - // cache cross-signing keys, so instead of replicating that, here we set - // up callbacks that request them from the other device and call - // CrossSigningInfo.getCrossSigningKey() to validate/cache - const crossSigning = new CrossSigningInfo(original.userId, { - getCrossSigningKey: (type) => __awaiter(this, void 0, void 0, function* () { - logger_1.logger.debug("Cross-signing: requesting secret", type, deviceId); - const { promise } = client.requestSecret(`m.cross_signing.${type}`, [deviceId]); - const result = yield promise; - const decoded = (0, olmlib_1.decodeBase64)(result); - return Uint8Array.from(decoded); - }), - }, original.getCacheCallbacks()); - crossSigning.keys = original.keys; - // XXX: get all keys out if we get one key out - // https://github.com/vector-im/element-web/issues/12604 - // then change here to reject on the timeout - // Requests can be ignored, so don't wait around forever - const timeout = new Promise((resolve) => { - setTimeout(resolve, KEY_REQUEST_TIMEOUT_MS, new Error("Timeout")); - }); - // also request and cache the key backup key - const backupKeyPromise = (() => __awaiter(this, void 0, void 0, function* () { - const cachedKey = yield client.crypto.getSessionBackupPrivateKey(); - if (!cachedKey) { - logger_1.logger.info("No cached backup key found. Requesting..."); - const secretReq = client.requestSecret("m.megolm_backup.v1", [deviceId]); - const base64Key = yield secretReq.promise; - logger_1.logger.info("Got key backup key, decoding..."); - const decodedKey = (0, olmlib_1.decodeBase64)(base64Key); - logger_1.logger.info("Decoded backup key, storing..."); - yield client.crypto.storeSessionBackupPrivateKey(Uint8Array.from(decodedKey)); - logger_1.logger.info("Backup key stored. Starting backup restore..."); - const backupInfo = yield client.getKeyBackupVersion(); - // no need to await for this - just let it go in the bg - client.restoreKeyBackupWithCache(undefined, undefined, backupInfo).then(() => { - logger_1.logger.info("Backup restored."); - }); - } - }))(); - // We call getCrossSigningKey() for its side-effects - return Promise.race([ - Promise.all([ - crossSigning.getCrossSigningKey("master"), - crossSigning.getCrossSigningKey("self_signing"), - crossSigning.getCrossSigningKey("user_signing"), - backupKeyPromise, - ]), - timeout, - ]).then(resolve, reject); - }).catch((e) => { - logger_1.logger.warn("Cross-signing: failure while requesting keys:", e); - }); - }); -} -exports.requestKeysDuringVerification = requestKeysDuringVerification; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) - -},{"../crypto/store/indexeddb-crypto-store":346,"../logger":374,"./aes":331,"./olmlib":343,"buffer":68}],325:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DeviceList = exports.TrackingStatus = void 0; -/** - * Manages the list of other users' devices - */ -const logger_1 = require("../logger"); -const deviceinfo_1 = require("./deviceinfo"); -const CrossSigning_1 = require("./CrossSigning"); -const olmlib = __importStar(require("./olmlib")); -const indexeddb_crypto_store_1 = require("./store/indexeddb-crypto-store"); -const utils_1 = require("../utils"); -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -const index_1 = require("./index"); -/* State transition diagram for DeviceList.deviceTrackingStatus - * - * | - * stopTrackingDeviceList V - * +---------------------> NOT_TRACKED - * | | - * +<--------------------+ | startTrackingDeviceList - * | | V - * | +-------------> PENDING_DOWNLOAD <--------------------+-+ - * | | ^ | | | - * | | restart download | | start download | | invalidateUserDeviceList - * | | client failed | | | | - * | | | V | | - * | +------------ DOWNLOAD_IN_PROGRESS -------------------+ | - * | | | | - * +<-------------------+ | download successful | - * ^ V | - * +----------------------- UP_TO_DATE ------------------------+ - */ -// constants for DeviceList.deviceTrackingStatus -var TrackingStatus; -(function (TrackingStatus) { - TrackingStatus[TrackingStatus["NotTracked"] = 0] = "NotTracked"; - TrackingStatus[TrackingStatus["PendingDownload"] = 1] = "PendingDownload"; - TrackingStatus[TrackingStatus["DownloadInProgress"] = 2] = "DownloadInProgress"; - TrackingStatus[TrackingStatus["UpToDate"] = 3] = "UpToDate"; -})(TrackingStatus = exports.TrackingStatus || (exports.TrackingStatus = {})); -class DeviceList extends typed_event_emitter_1.TypedEventEmitter { - constructor(baseApis, cryptoStore, olmDevice, - // Maximum number of user IDs per request to prevent server overload (#1619) - keyDownloadChunkSize = 250) { - super(); - this.cryptoStore = cryptoStore; - this.keyDownloadChunkSize = keyDownloadChunkSize; - this.devices = {}; - this.crossSigningInfo = {}; - // map of identity keys to the user who owns it - this.userByIdentityKey = {}; - // which users we are tracking device status for. - this.deviceTrackingStatus = {}; // loaded from storage in load() - // The 'next_batch' sync token at the point the data was written, - // ie. a token representing the point immediately after the - // moment represented by the snapshot in the db. - this.syncToken = null; - this.keyDownloadsInProgressByUser = new Map(); - // Set whenever changes are made other than setting the sync token - this.dirty = false; - // Promise resolved when device data is saved - this.savePromise = null; - // Function that resolves the save promise - this.resolveSavePromise = null; - // The time the save is scheduled for - this.savePromiseTime = null; - // The timer used to delay the save - this.saveTimer = null; - // True if we have fetched data from the server or loaded a non-empty - // set of device data from the store - this.hasFetched = null; - this.serialiser = new DeviceListUpdateSerialiser(baseApis, olmDevice, this); - } - /** - * Load the device tracking state from storage - */ - load() { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_DEVICE_DATA], (txn) => { - this.cryptoStore.getEndToEndDeviceData(txn, (deviceData) => { - var _a; - this.hasFetched = Boolean(deviceData && deviceData.devices); - this.devices = deviceData ? deviceData.devices : {}; - this.crossSigningInfo = deviceData ? deviceData.crossSigningInfo || {} : {}; - this.deviceTrackingStatus = deviceData ? deviceData.trackingStatus : {}; - this.syncToken = (_a = deviceData === null || deviceData === void 0 ? void 0 : deviceData.syncToken) !== null && _a !== void 0 ? _a : null; - this.userByIdentityKey = {}; - for (const user of Object.keys(this.devices)) { - const userDevices = this.devices[user]; - for (const device of Object.keys(userDevices)) { - const idKey = userDevices[device].keys["curve25519:" + device]; - if (idKey !== undefined) { - this.userByIdentityKey[idKey] = user; - } - } - } - }); - }); - for (const u of Object.keys(this.deviceTrackingStatus)) { - // if a download was in progress when we got shut down, it isn't any more. - if (this.deviceTrackingStatus[u] == TrackingStatus.DownloadInProgress) { - this.deviceTrackingStatus[u] = TrackingStatus.PendingDownload; - } - } - }); - } - stop() { - if (this.saveTimer !== null) { - clearTimeout(this.saveTimer); - } - } - /** - * Save the device tracking state to storage, if any changes are - * pending other than updating the sync token - * - * The actual save will be delayed by a short amount of time to - * aggregate multiple writes to the database. - * - * @param delay - Time in ms before which the save actually happens. - * By default, the save is delayed for a short period in order to batch - * multiple writes, but this behaviour can be disabled by passing 0. - * - * @returns true if the data was saved, false if - * it was not (eg. because no changes were pending). The promise - * will only resolve once the data is saved, so may take some time - * to resolve. - */ - saveIfDirty(delay = 500) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.dirty) - return Promise.resolve(false); - // Delay saves for a bit so we can aggregate multiple saves that happen - // in quick succession (eg. when a whole room's devices are marked as known) - const targetTime = Date.now() + delay; - if (this.savePromiseTime && targetTime < this.savePromiseTime) { - // There's a save scheduled but for after we would like: cancel - // it & schedule one for the time we want - clearTimeout(this.saveTimer); - this.saveTimer = null; - this.savePromiseTime = null; - // (but keep the save promise since whatever called save before - // will still want to know when the save is done) - } - let savePromise = this.savePromise; - if (savePromise === null) { - savePromise = new Promise((resolve) => { - this.resolveSavePromise = resolve; - }); - this.savePromise = savePromise; - } - if (this.saveTimer === null) { - const resolveSavePromise = this.resolveSavePromise; - this.savePromiseTime = targetTime; - this.saveTimer = setTimeout(() => { - logger_1.logger.log("Saving device tracking data", this.syncToken); - // null out savePromise now (after the delay but before the write), - // otherwise we could return the existing promise when the save has - // actually already happened. - this.savePromiseTime = null; - this.saveTimer = null; - this.savePromise = null; - this.resolveSavePromise = null; - this.cryptoStore - .doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_DEVICE_DATA], (txn) => { - var _a; - this.cryptoStore.storeEndToEndDeviceData({ - devices: this.devices, - crossSigningInfo: this.crossSigningInfo, - trackingStatus: this.deviceTrackingStatus, - syncToken: (_a = this.syncToken) !== null && _a !== void 0 ? _a : undefined, - }, txn); - }) - .then(() => { - // The device list is considered dirty until the write completes. - this.dirty = false; - resolveSavePromise === null || resolveSavePromise === void 0 ? void 0 : resolveSavePromise(true); - }, (err) => { - logger_1.logger.error("Failed to save device tracking data", this.syncToken); - logger_1.logger.error(err); - }); - }, delay); - } - return savePromise; - }); - } - /** - * Gets the sync token last set with setSyncToken - * - * @returns The sync token - */ - getSyncToken() { - return this.syncToken; - } - /** - * Sets the sync token that the app will pass as the 'since' to the /sync - * endpoint next time it syncs. - * The sync token must always be set after any changes made as a result of - * data in that sync since setting the sync token to a newer one will mean - * those changed will not be synced from the server if a new client starts - * up with that data. - * - * @param st - The sync token - */ - setSyncToken(st) { - this.syncToken = st; - } - /** - * Ensures up to date keys for a list of users are stored in the session store, - * downloading and storing them if they're not (or if forceDownload is - * true). - * @param userIds - The users to fetch. - * @param forceDownload - Always download the keys even if cached. - * - * @returns A promise which resolves to a map userId-\>deviceId-\>{@link DeviceInfo}. - */ - downloadKeys(userIds, forceDownload) { - const usersToDownload = []; - const promises = []; - userIds.forEach((u) => { - const trackingStatus = this.deviceTrackingStatus[u]; - if (this.keyDownloadsInProgressByUser.has(u)) { - // already a key download in progress/queued for this user; its results - // will be good enough for us. - logger_1.logger.log(`downloadKeys: already have a download in progress for ` + `${u}: awaiting its result`); - promises.push(this.keyDownloadsInProgressByUser.get(u)); - } - else if (forceDownload || trackingStatus != TrackingStatus.UpToDate) { - usersToDownload.push(u); - } - }); - if (usersToDownload.length != 0) { - logger_1.logger.log("downloadKeys: downloading for", usersToDownload); - const downloadPromise = this.doKeyDownload(usersToDownload); - promises.push(downloadPromise); - } - if (promises.length === 0) { - logger_1.logger.log("downloadKeys: already have all necessary keys"); - } - return Promise.all(promises).then(() => { - return this.getDevicesFromStore(userIds); - }); - } - /** - * Get the stored device keys for a list of user ids - * - * @param userIds - the list of users to list keys for. - * - * @returns userId-\>deviceId-\>{@link DeviceInfo}. - */ - getDevicesFromStore(userIds) { - const stored = new Map(); - userIds.forEach((userId) => { - var _a; - const deviceMap = new Map(); - (_a = this.getStoredDevicesForUser(userId)) === null || _a === void 0 ? void 0 : _a.forEach(function (device) { - deviceMap.set(device.deviceId, device); - }); - stored.set(userId, deviceMap); - }); - return stored; - } - /** - * Returns a list of all user IDs the DeviceList knows about - * - * @returns All known user IDs - */ - getKnownUserIds() { - return Object.keys(this.devices); - } - /** - * Get the stored device keys for a user id - * - * @param userId - the user to list keys for. - * - * @returns list of devices, or null if we haven't - * managed to get a list of devices for this user yet. - */ - getStoredDevicesForUser(userId) { - const devs = this.devices[userId]; - if (!devs) { - return null; - } - const res = []; - for (const deviceId in devs) { - if (devs.hasOwnProperty(deviceId)) { - res.push(deviceinfo_1.DeviceInfo.fromStorage(devs[deviceId], deviceId)); - } - } - return res; - } - /** - * Get the stored device data for a user, in raw object form - * - * @param userId - the user to get data for - * - * @returns `deviceId->{object}` devices, or undefined if - * there is no data for this user. - */ - getRawStoredDevicesForUser(userId) { - return this.devices[userId]; - } - getStoredCrossSigningForUser(userId) { - if (!this.crossSigningInfo[userId]) - return null; - return CrossSigning_1.CrossSigningInfo.fromStorage(this.crossSigningInfo[userId], userId); - } - storeCrossSigningForUser(userId, info) { - this.crossSigningInfo[userId] = info; - this.dirty = true; - } - /** - * Get the stored keys for a single device - * - * - * @returns device, or undefined - * if we don't know about this device - */ - getStoredDevice(userId, deviceId) { - const devs = this.devices[userId]; - if (!(devs === null || devs === void 0 ? void 0 : devs[deviceId])) { - return undefined; - } - return deviceinfo_1.DeviceInfo.fromStorage(devs[deviceId], deviceId); - } - /** - * Get a user ID by one of their device's curve25519 identity key - * - * @param algorithm - encryption algorithm - * @param senderKey - curve25519 key to match - * - * @returns user ID - */ - getUserByIdentityKey(algorithm, senderKey) { - if (algorithm !== olmlib.OLM_ALGORITHM && algorithm !== olmlib.MEGOLM_ALGORITHM) { - // we only deal in olm keys - return null; - } - return this.userByIdentityKey[senderKey]; - } - /** - * Find a device by curve25519 identity key - * - * @param algorithm - encryption algorithm - * @param senderKey - curve25519 key to match - */ - getDeviceByIdentityKey(algorithm, senderKey) { - const userId = this.getUserByIdentityKey(algorithm, senderKey); - if (!userId) { - return null; - } - const devices = this.devices[userId]; - if (!devices) { - return null; - } - for (const deviceId in devices) { - if (!devices.hasOwnProperty(deviceId)) { - continue; - } - const device = devices[deviceId]; - for (const keyId in device.keys) { - if (!device.keys.hasOwnProperty(keyId)) { - continue; - } - if (keyId.indexOf("curve25519:") !== 0) { - continue; - } - const deviceKey = device.keys[keyId]; - if (deviceKey == senderKey) { - return deviceinfo_1.DeviceInfo.fromStorage(device, deviceId); - } - } - } - // doesn't match a known device - return null; - } - /** - * Replaces the list of devices for a user with the given device list - * - * @param userId - The user ID - * @param devices - New device info for user - */ - storeDevicesForUser(userId, devices) { - this.setRawStoredDevicesForUser(userId, devices); - this.dirty = true; - } - /** - * flag the given user for device-list tracking, if they are not already. - * - * This will mean that a subsequent call to refreshOutdatedDeviceLists() - * will download the device list for the user, and that subsequent calls to - * invalidateUserDeviceList will trigger more updates. - * - */ - startTrackingDeviceList(userId) { - // sanity-check the userId. This is mostly paranoia, but if synapse - // can't parse the userId we give it as an mxid, it 500s the whole - // request and we can never update the device lists again (because - // the broken userId is always 'invalid' and always included in any - // refresh request). - // By checking it is at least a string, we can eliminate a class of - // silly errors. - if (typeof userId !== "string") { - throw new Error("userId must be a string; was " + userId); - } - if (!this.deviceTrackingStatus[userId]) { - logger_1.logger.log("Now tracking device list for " + userId); - this.deviceTrackingStatus[userId] = TrackingStatus.PendingDownload; - // we don't yet persist the tracking status, since there may be a lot - // of calls; we save all data together once the sync is done - this.dirty = true; - } - } - /** - * Mark the given user as no longer being tracked for device-list updates. - * - * This won't affect any in-progress downloads, which will still go on to - * complete; it will just mean that we don't think that we have an up-to-date - * list for future calls to downloadKeys. - * - */ - stopTrackingDeviceList(userId) { - if (this.deviceTrackingStatus[userId]) { - logger_1.logger.log("No longer tracking device list for " + userId); - this.deviceTrackingStatus[userId] = TrackingStatus.NotTracked; - // we don't yet persist the tracking status, since there may be a lot - // of calls; we save all data together once the sync is done - this.dirty = true; - } - } - /** - * Set all users we're currently tracking to untracked - * - * This will flag each user whose devices we are tracking as in need of an - * update. - */ - stopTrackingAllDeviceLists() { - for (const userId of Object.keys(this.deviceTrackingStatus)) { - this.deviceTrackingStatus[userId] = TrackingStatus.NotTracked; - } - this.dirty = true; - } - /** - * Mark the cached device list for the given user outdated. - * - * If we are not tracking this user's devices, we'll do nothing. Otherwise - * we flag the user as needing an update. - * - * This doesn't actually set off an update, so that several users can be - * batched together. Call refreshOutdatedDeviceLists() for that. - * - */ - invalidateUserDeviceList(userId) { - if (this.deviceTrackingStatus[userId]) { - logger_1.logger.log("Marking device list outdated for", userId); - this.deviceTrackingStatus[userId] = TrackingStatus.PendingDownload; - // we don't yet persist the tracking status, since there may be a lot - // of calls; we save all data together once the sync is done - this.dirty = true; - } - } - /** - * If we have users who have outdated device lists, start key downloads for them - * - * @returns which completes when the download completes; normally there - * is no need to wait for this (it's mostly for the unit tests). - */ - refreshOutdatedDeviceLists() { - this.saveIfDirty(); - const usersToDownload = []; - for (const userId of Object.keys(this.deviceTrackingStatus)) { - const stat = this.deviceTrackingStatus[userId]; - if (stat == TrackingStatus.PendingDownload) { - usersToDownload.push(userId); - } - } - return this.doKeyDownload(usersToDownload); - } - /** - * Set the stored device data for a user, in raw object form - * Used only by internal class DeviceListUpdateSerialiser - * - * @param userId - the user to get data for - * - * @param devices - `deviceId->{object}` the new devices - */ - setRawStoredDevicesForUser(userId, devices) { - // remove old devices from userByIdentityKey - if (this.devices[userId] !== undefined) { - for (const [deviceId, dev] of Object.entries(this.devices[userId])) { - const identityKey = dev.keys["curve25519:" + deviceId]; - delete this.userByIdentityKey[identityKey]; - } - } - this.devices[userId] = devices; - // add new devices into userByIdentityKey - for (const [deviceId, dev] of Object.entries(devices)) { - const identityKey = dev.keys["curve25519:" + deviceId]; - this.userByIdentityKey[identityKey] = userId; - } - } - setRawStoredCrossSigningForUser(userId, info) { - this.crossSigningInfo[userId] = info; - } - /** - * Fire off download update requests for the given users, and update the - * device list tracking status for them, and the - * keyDownloadsInProgressByUser map for them. - * - * @param users - list of userIds - * - * @returns resolves when all the users listed have - * been updated. rejects if there was a problem updating any of the - * users. - */ - doKeyDownload(users) { - if (users.length === 0) { - // nothing to do - return Promise.resolve(); - } - const prom = this.serialiser.updateDevicesForUsers(users, this.syncToken).then(() => { - finished(true); - }, (e) => { - logger_1.logger.error("Error downloading keys for " + users + ":", e); - finished(false); - throw e; - }); - users.forEach((u) => { - this.keyDownloadsInProgressByUser.set(u, prom); - const stat = this.deviceTrackingStatus[u]; - if (stat == TrackingStatus.PendingDownload) { - this.deviceTrackingStatus[u] = TrackingStatus.DownloadInProgress; - } - }); - const finished = (success) => { - this.emit(index_1.CryptoEvent.WillUpdateDevices, users, !this.hasFetched); - users.forEach((u) => { - this.dirty = true; - // we may have queued up another download request for this user - // since we started this request. If that happens, we should - // ignore the completion of the first one. - if (this.keyDownloadsInProgressByUser.get(u) !== prom) { - logger_1.logger.log("Another update in the queue for", u, "- not marking up-to-date"); - return; - } - this.keyDownloadsInProgressByUser.delete(u); - const stat = this.deviceTrackingStatus[u]; - if (stat == TrackingStatus.DownloadInProgress) { - if (success) { - // we didn't get any new invalidations since this download started: - // this user's device list is now up to date. - this.deviceTrackingStatus[u] = TrackingStatus.UpToDate; - logger_1.logger.log("Device list for", u, "now up to date"); - } - else { - this.deviceTrackingStatus[u] = TrackingStatus.PendingDownload; - } - } - }); - this.saveIfDirty(); - this.emit(index_1.CryptoEvent.DevicesUpdated, users, !this.hasFetched); - this.hasFetched = true; - }; - return prom; - } -} -exports.DeviceList = DeviceList; -/** - * Serialises updates to device lists - * - * Ensures that results from /keys/query are not overwritten if a second call - * completes *before* an earlier one. - * - * It currently does this by ensuring only one call to /keys/query happens at a - * time (and queuing other requests up). - */ -class DeviceListUpdateSerialiser { - /* - * @param baseApis - Base API object - * @param olmDevice - The Olm Device - * @param deviceList - The device list object, the device list to be updated - */ - constructor(baseApis, olmDevice, deviceList) { - this.baseApis = baseApis; - this.olmDevice = olmDevice; - this.deviceList = deviceList; - this.downloadInProgress = false; - // users which are queued for download - // userId -> true - this.keyDownloadsQueuedByUser = {}; - } - /** - * Make a key query request for the given users - * - * @param users - list of user ids - * - * @param syncToken - sync token to pass in the query request, to - * help the HS give the most recent results - * - * @returns resolves when all the users listed have - * been updated. rejects if there was a problem updating any of the - * users. - */ - updateDevicesForUsers(users, syncToken) { - users.forEach((u) => { - this.keyDownloadsQueuedByUser[u] = true; - }); - if (!this.queuedQueryDeferred) { - this.queuedQueryDeferred = (0, utils_1.defer)(); - } - // We always take the new sync token and just use the latest one we've - // been given, since it just needs to be at least as recent as the - // sync response the device invalidation message arrived in - this.syncToken = syncToken; - if (this.downloadInProgress) { - // just queue up these users - logger_1.logger.log("Queued key download for", users); - return this.queuedQueryDeferred.promise; - } - // start a new download. - return this.doQueuedQueries(); - } - doQueuedQueries() { - if (this.downloadInProgress) { - throw new Error("DeviceListUpdateSerialiser.doQueuedQueries called with request active"); - } - const downloadUsers = Object.keys(this.keyDownloadsQueuedByUser); - this.keyDownloadsQueuedByUser = {}; - const deferred = this.queuedQueryDeferred; - this.queuedQueryDeferred = undefined; - logger_1.logger.log("Starting key download for", downloadUsers); - this.downloadInProgress = true; - const opts = {}; - if (this.syncToken) { - opts.token = this.syncToken; - } - const factories = []; - for (let i = 0; i < downloadUsers.length; i += this.deviceList.keyDownloadChunkSize) { - const userSlice = downloadUsers.slice(i, i + this.deviceList.keyDownloadChunkSize); - factories.push(() => this.baseApis.downloadKeysForUsers(userSlice, opts)); - } - (0, utils_1.chunkPromises)(factories, 3) - .then((responses) => __awaiter(this, void 0, void 0, function* () { - const dk = Object.assign({}, ...responses.map((res) => res.device_keys || {})); - const masterKeys = Object.assign({}, ...responses.map((res) => res.master_keys || {})); - const ssks = Object.assign({}, ...responses.map((res) => res.self_signing_keys || {})); - const usks = Object.assign({}, ...responses.map((res) => res.user_signing_keys || {})); - // yield to other things that want to execute in between users, to - // avoid wedging the CPU - // (https://github.com/vector-im/element-web/issues/3158) - // - // of course we ought to do this in a web worker or similar, but - // this serves as an easy solution for now. - for (const userId of downloadUsers) { - yield (0, utils_1.sleep)(5); - try { - yield this.processQueryResponseForUser(userId, dk[userId], { - master: masterKeys === null || masterKeys === void 0 ? void 0 : masterKeys[userId], - self_signing: ssks === null || ssks === void 0 ? void 0 : ssks[userId], - user_signing: usks === null || usks === void 0 ? void 0 : usks[userId], - }); - } - catch (e) { - // log the error but continue, so that one bad key - // doesn't kill the whole process - logger_1.logger.error(`Error processing keys for ${userId}:`, e); - } - } - })) - .then(() => { - logger_1.logger.log("Completed key download for " + downloadUsers); - this.downloadInProgress = false; - deferred === null || deferred === void 0 ? void 0 : deferred.resolve(); - // if we have queued users, fire off another request. - if (this.queuedQueryDeferred) { - this.doQueuedQueries(); - } - }, (e) => { - logger_1.logger.warn("Error downloading keys for " + downloadUsers + ":", e); - this.downloadInProgress = false; - deferred === null || deferred === void 0 ? void 0 : deferred.reject(e); - }); - return deferred.promise; - } - processQueryResponseForUser(userId, dkResponse, crossSigningResponse) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log("got device keys for " + userId + ":", dkResponse); - logger_1.logger.log("got cross-signing keys for " + userId + ":", crossSigningResponse); - { - // map from deviceid -> deviceinfo for this user - const userStore = {}; - const devs = this.deviceList.getRawStoredDevicesForUser(userId); - if (devs) { - Object.keys(devs).forEach((deviceId) => { - const d = deviceinfo_1.DeviceInfo.fromStorage(devs[deviceId], deviceId); - userStore[deviceId] = d; - }); - } - yield updateStoredDeviceKeysForUser(this.olmDevice, userId, userStore, dkResponse || {}, this.baseApis.getUserId(), this.baseApis.deviceId); - // put the updates into the object that will be returned as our results - const storage = {}; - Object.keys(userStore).forEach((deviceId) => { - storage[deviceId] = userStore[deviceId].toStorage(); - }); - this.deviceList.setRawStoredDevicesForUser(userId, storage); - } - // now do the same for the cross-signing keys - { - // FIXME: should we be ignoring empty cross-signing responses, or - // should we be dropping the keys? - if (crossSigningResponse && - (crossSigningResponse.master || crossSigningResponse.self_signing || crossSigningResponse.user_signing)) { - const crossSigning = this.deviceList.getStoredCrossSigningForUser(userId) || new CrossSigning_1.CrossSigningInfo(userId); - crossSigning.setKeys(crossSigningResponse); - this.deviceList.setRawStoredCrossSigningForUser(userId, crossSigning.toStorage()); - // NB. Unlike most events in the js-sdk, this one is internal to the - // js-sdk and is not re-emitted - this.deviceList.emit(index_1.CryptoEvent.UserCrossSigningUpdated, userId); - } - } - }); - } -} -function updateStoredDeviceKeysForUser(olmDevice, userId, userStore, userResult, localUserId, localDeviceId) { - return __awaiter(this, void 0, void 0, function* () { - let updated = false; - // remove any devices in the store which aren't in the response - for (const deviceId in userStore) { - if (!userStore.hasOwnProperty(deviceId)) { - continue; - } - if (!(deviceId in userResult)) { - if (userId === localUserId && deviceId === localDeviceId) { - logger_1.logger.warn(`Local device ${deviceId} missing from sync, skipping removal`); - continue; - } - logger_1.logger.log("Device " + userId + ":" + deviceId + " has been removed"); - delete userStore[deviceId]; - updated = true; - } - } - for (const deviceId in userResult) { - if (!userResult.hasOwnProperty(deviceId)) { - continue; - } - const deviceResult = userResult[deviceId]; - // check that the user_id and device_id in the response object are - // correct - if (deviceResult.user_id !== userId) { - logger_1.logger.warn("Mismatched user_id " + deviceResult.user_id + " in keys from " + userId + ":" + deviceId); - continue; - } - if (deviceResult.device_id !== deviceId) { - logger_1.logger.warn("Mismatched device_id " + deviceResult.device_id + " in keys from " + userId + ":" + deviceId); - continue; - } - if (yield storeDeviceKeys(olmDevice, userStore, deviceResult)) { - updated = true; - } - } - return updated; - }); -} -/* - * Process a device in a /query response, and add it to the userStore - * - * returns (a promise for) true if a change was made, else false - */ -function storeDeviceKeys(olmDevice, userStore, deviceResult) { - return __awaiter(this, void 0, void 0, function* () { - if (!deviceResult.keys) { - // no keys? - return false; - } - const deviceId = deviceResult.device_id; - const userId = deviceResult.user_id; - const signKeyId = "ed25519:" + deviceId; - const signKey = deviceResult.keys[signKeyId]; - if (!signKey) { - logger_1.logger.warn("Device " + userId + ":" + deviceId + " has no ed25519 key"); - return false; - } - const unsigned = deviceResult.unsigned || {}; - const signatures = deviceResult.signatures || {}; - try { - yield olmlib.verifySignature(olmDevice, deviceResult, userId, deviceId, signKey); - } - catch (e) { - logger_1.logger.warn("Unable to verify signature on device " + userId + ":" + deviceId + ":" + e); - return false; - } - // DeviceInfo - let deviceStore; - if (deviceId in userStore) { - // already have this device. - deviceStore = userStore[deviceId]; - if (deviceStore.getFingerprint() != signKey) { - // this should only happen if the list has been MITMed; we are - // best off sticking with the original keys. - // - // Should we warn the user about it somehow? - logger_1.logger.warn("Ed25519 key for device " + userId + ":" + deviceId + " has changed"); - return false; - } - } - else { - userStore[deviceId] = deviceStore = new deviceinfo_1.DeviceInfo(deviceId); - } - deviceStore.keys = deviceResult.keys || {}; - deviceStore.algorithms = deviceResult.algorithms || []; - deviceStore.unsigned = unsigned; - deviceStore.signatures = signatures; - return true; - }); -} - -},{"../logger":374,"../models/typed-event-emitter":395,"../utils":416,"./CrossSigning":324,"./deviceinfo":340,"./index":341,"./olmlib":343,"./store/indexeddb-crypto-store":346}],326:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EncryptionSetupOperation = exports.EncryptionSetupBuilder = void 0; -const logger_1 = require("../logger"); -const event_1 = require("../models/event"); -const CrossSigning_1 = require("./CrossSigning"); -const indexeddb_crypto_store_1 = require("./store/indexeddb-crypto-store"); -const http_api_1 = require("../http-api"); -const client_1 = require("../client"); -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -/** - * Builds an EncryptionSetupOperation by calling any of the add.. methods. - * Once done, `buildOperation()` can be called which allows to apply to operation. - * - * This is used as a helper by Crypto to keep track of all the network requests - * and other side-effects of bootstrapping, so it can be applied in one go (and retried in the future) - * Also keeps track of all the private keys created during bootstrapping, so we don't need to prompt for them - * more than once. - */ -class EncryptionSetupBuilder { - /** - * @param accountData - pre-existing account data, will only be read, not written. - * @param delegateCryptoCallbacks - crypto callbacks to delegate to if the key isn't in cache yet - */ - constructor(accountData, delegateCryptoCallbacks) { - this.accountDataClientAdapter = new AccountDataClientAdapter(accountData); - this.crossSigningCallbacks = new CrossSigningCallbacks(); - this.ssssCryptoCallbacks = new SSSSCryptoCallbacks(delegateCryptoCallbacks); - } - /** - * Adds new cross-signing public keys - * - * @param authUpload - Function called to await an interactive auth - * flow when uploading device signing keys. - * Args: - * A function that makes the request requiring auth. Receives - * the auth data as an object. Can be called multiple times, first with - * an empty authDict, to obtain the flows. - * @param keys - the new keys - */ - addCrossSigningKeys(authUpload, keys) { - this.crossSigningKeys = { authUpload, keys }; - } - /** - * Adds the key backup info to be updated on the server - * - * Used either to create a new key backup, or add signatures - * from the new MSK. - * - * @param keyBackupInfo - as received from/sent to the server - */ - addSessionBackup(keyBackupInfo) { - this.keyBackupInfo = keyBackupInfo; - } - /** - * Adds the session backup private key to be updated in the local cache - * - * Used after fixing the format of the key - * - */ - addSessionBackupPrivateKeyToCache(privateKey) { - this.sessionBackupPrivateKey = privateKey; - } - /** - * Add signatures from a given user and device/x-sign key - * Used to sign the new cross-signing key with the device key - * - */ - addKeySignature(userId, deviceId, signature) { - if (!this.keySignatures) { - this.keySignatures = {}; - } - const userSignatures = this.keySignatures[userId] || {}; - this.keySignatures[userId] = userSignatures; - userSignatures[deviceId] = signature; - } - setAccountData(type, content) { - return __awaiter(this, void 0, void 0, function* () { - yield this.accountDataClientAdapter.setAccountData(type, content); - }); - } - /** - * builds the operation containing all the parts that have been added to the builder - */ - buildOperation() { - const accountData = this.accountDataClientAdapter.values; - return new EncryptionSetupOperation(accountData, this.crossSigningKeys, this.keyBackupInfo, this.keySignatures); - } - /** - * Stores the created keys locally. - * - * This does not yet store the operation in a way that it can be restored, - * but that is the idea in the future. - */ - persist(crypto) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // store private keys in cache - if (this.crossSigningKeys) { - const cacheCallbacks = (0, CrossSigning_1.createCryptoStoreCacheCallbacks)(crypto.cryptoStore, crypto.olmDevice); - for (const type of ["master", "self_signing", "user_signing"]) { - logger_1.logger.log(`Cache ${type} cross-signing private key locally`); - const privateKey = this.crossSigningCallbacks.privateKeys.get(type); - yield ((_a = cacheCallbacks.storeCrossSigningKeyCache) === null || _a === void 0 ? void 0 : _a.call(cacheCallbacks, type, privateKey)); - } - // store own cross-sign pubkeys as trusted - yield crypto.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - crypto.cryptoStore.storeCrossSigningKeys(txn, this.crossSigningKeys.keys); - }); - } - // store session backup key in cache - if (this.sessionBackupPrivateKey) { - yield crypto.storeSessionBackupPrivateKey(this.sessionBackupPrivateKey); - } - }); - } -} -exports.EncryptionSetupBuilder = EncryptionSetupBuilder; -/** - * Can be created from EncryptionSetupBuilder, or - * (in a follow-up PR, not implemented yet) restored from storage, to retry. - * - * It does not have knowledge of any private keys, unlike the builder. - */ -class EncryptionSetupOperation { - /** - */ - constructor(accountData, crossSigningKeys, keyBackupInfo, keySignatures) { - this.accountData = accountData; - this.crossSigningKeys = crossSigningKeys; - this.keyBackupInfo = keyBackupInfo; - this.keySignatures = keySignatures; - } - /** - * Runs the (remaining part of, in the future) operation by sending requests to the server. - */ - apply(crypto) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - const baseApis = crypto.baseApis; - // upload cross-signing keys - if (this.crossSigningKeys) { - const keys = {}; - for (const [name, key] of Object.entries(this.crossSigningKeys.keys)) { - keys[(name + "_key")] = key; - } - // We must only call `uploadDeviceSigningKeys` from inside this auth - // helper to ensure we properly handle auth errors. - yield ((_b = (_a = this.crossSigningKeys).authUpload) === null || _b === void 0 ? void 0 : _b.call(_a, (authDict) => { - return baseApis.uploadDeviceSigningKeys(authDict, keys); - })); - // pass the new keys to the main instance of our own CrossSigningInfo. - crypto.crossSigningInfo.setKeys(this.crossSigningKeys.keys); - } - // set account data - if (this.accountData) { - for (const [type, content] of this.accountData) { - yield baseApis.setAccountData(type, content); - } - } - // upload first cross-signing signatures with the new key - // (e.g. signing our own device) - if (this.keySignatures) { - yield baseApis.uploadKeySignatures(this.keySignatures); - } - // need to create/update key backup info - if (this.keyBackupInfo) { - if (this.keyBackupInfo.version) { - // session backup signature - // The backup is trusted because the user provided the private key. - // Sign the backup with the cross signing key so the key backup can - // be trusted via cross-signing. - yield baseApis.http.authedRequest(http_api_1.Method.Put, "/room_keys/version/" + this.keyBackupInfo.version, undefined, { - algorithm: this.keyBackupInfo.algorithm, - auth_data: this.keyBackupInfo.auth_data, - }, { prefix: http_api_1.ClientPrefix.V3 }); - } - else { - // add new key backup - yield baseApis.http.authedRequest(http_api_1.Method.Post, "/room_keys/version", undefined, this.keyBackupInfo, { - prefix: http_api_1.ClientPrefix.V3, - }); - } - } - }); - } -} -exports.EncryptionSetupOperation = EncryptionSetupOperation; -/** - * Catches account data set by SecretStorage during bootstrapping by - * implementing the methods related to account data in MatrixClient - */ -class AccountDataClientAdapter extends typed_event_emitter_1.TypedEventEmitter { - /** - * @param existingValues - existing account data - */ - constructor(existingValues) { - super(); - this.existingValues = existingValues; - // - this.values = new Map(); - } - /** - * @returns the content of the account data - */ - getAccountDataFromServer(type) { - return Promise.resolve(this.getAccountData(type)); - } - /** - * @returns the content of the account data - */ - getAccountData(type) { - const modifiedValue = this.values.get(type); - if (modifiedValue) { - return modifiedValue; - } - const existingValue = this.existingValues.get(type); - if (existingValue) { - return existingValue.getContent(); - } - return null; - } - setAccountData(type, content) { - const lastEvent = this.values.get(type); - this.values.set(type, content); - // ensure accountData is emitted on the next tick, - // as SecretStorage listens for it while calling this method - // and it seems to rely on this. - return Promise.resolve().then(() => { - const event = new event_1.MatrixEvent({ type, content }); - this.emit(client_1.ClientEvent.AccountData, event, lastEvent); - return {}; - }); - } -} -/** - * Catches the private cross-signing keys set during bootstrapping - * by both cache callbacks (see createCryptoStoreCacheCallbacks) as non-cache callbacks. - * See CrossSigningInfo constructor - */ -class CrossSigningCallbacks { - constructor() { - this.privateKeys = new Map(); - } - // cache callbacks - getCrossSigningKeyCache(type, expectedPublicKey) { - return this.getCrossSigningKey(type, expectedPublicKey); - } - storeCrossSigningKeyCache(type, key) { - this.privateKeys.set(type, key); - return Promise.resolve(); - } - // non-cache callbacks - getCrossSigningKey(type, expectedPubkey) { - var _a; - return Promise.resolve((_a = this.privateKeys.get(type)) !== null && _a !== void 0 ? _a : null); - } - saveCrossSigningKeys(privateKeys) { - for (const [type, privateKey] of Object.entries(privateKeys)) { - this.privateKeys.set(type, privateKey); - } - } -} -/** - * Catches the 4S private key set during bootstrapping by implementing - * the SecretStorage crypto callbacks - */ -class SSSSCryptoCallbacks { - constructor(delegateCryptoCallbacks) { - this.delegateCryptoCallbacks = delegateCryptoCallbacks; - this.privateKeys = new Map(); - } - getSecretStorageKey({ keys }, name) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - for (const keyId of Object.keys(keys)) { - const privateKey = this.privateKeys.get(keyId); - if (privateKey) { - return [keyId, privateKey]; - } - } - // if we don't have the key cached yet, ask - // for it to the general crypto callbacks and cache it - if ((_a = this === null || this === void 0 ? void 0 : this.delegateCryptoCallbacks) === null || _a === void 0 ? void 0 : _a.getSecretStorageKey) { - const result = yield this.delegateCryptoCallbacks.getSecretStorageKey({ keys }, name); - if (result) { - const [keyId, privateKey] = result; - this.privateKeys.set(keyId, privateKey); - } - return result; - } - return null; - }); - } - addPrivateKey(keyId, keyInfo, privKey) { - var _a, _b; - this.privateKeys.set(keyId, privKey); - // Also pass along to application to cache if it wishes - (_b = (_a = this.delegateCryptoCallbacks) === null || _a === void 0 ? void 0 : _a.cacheSecretStorageKey) === null || _b === void 0 ? void 0 : _b.call(_a, keyId, keyInfo, privKey); - } -} - -},{"../client":321,"../http-api":367,"../logger":374,"../models/event":383,"../models/typed-event-emitter":395,"./CrossSigning":324,"./store/indexeddb-crypto-store":346}],327:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WITHHELD_MESSAGES = exports.OlmDevice = exports.PayloadTooLargeError = void 0; -const logger_1 = require("../logger"); -const indexeddb_crypto_store_1 = require("./store/indexeddb-crypto-store"); -const algorithms = __importStar(require("./algorithms")); -// The maximum size of an event is 65K, and we base64 the content, so this is a -// reasonable approximation to the biggest plaintext we can encrypt. -const MAX_PLAINTEXT_LENGTH = (65536 * 3) / 4; -class PayloadTooLargeError extends Error { - constructor() { - super(...arguments); - this.data = { - errcode: "M_TOO_LARGE", - error: "Payload too large for encrypted message", - }; - } -} -exports.PayloadTooLargeError = PayloadTooLargeError; -function checkPayloadLength(payloadString) { - if (payloadString === undefined) { - throw new Error("payloadString undefined"); - } - if (payloadString.length > MAX_PLAINTEXT_LENGTH) { - // might as well fail early here rather than letting the olm library throw - // a cryptic memory allocation error. - // - // Note that even if we manage to do the encryption, the message send may fail, - // because by the time we've wrapped the ciphertext in the event object, it may - // exceed 65K. But at least we won't just fail with "abort()" in that case. - throw new PayloadTooLargeError(`Message too long (${payloadString.length} bytes). ` + - `The maximum for an encrypted message is ${MAX_PLAINTEXT_LENGTH} bytes.`); - } -} -/** - * Manages the olm cryptography functions. Each OlmDevice has a single - * OlmAccount and a number of OlmSessions. - * - * Accounts and sessions are kept pickled in the cryptoStore. - */ -class OlmDevice { - constructor(cryptoStore) { - this.cryptoStore = cryptoStore; - this.pickleKey = "DEFAULT_KEY"; // set by consumers - /** Curve25519 key for the account, unknown until we load the account from storage in init() */ - this.deviceCurve25519Key = null; - /** Ed25519 key for the account, unknown until we load the account from storage in init() */ - this.deviceEd25519Key = null; - this.maxOneTimeKeys = null; - // we don't bother stashing outboundgroupsessions in the cryptoStore - - // instead we keep them here. - this.outboundGroupSessionStore = {}; - // Store a set of decrypted message indexes for each group session. - // This partially mitigates a replay attack where a MITM resends a group - // message into the room. - // - // When we decrypt a message and the message index matches a previously - // decrypted message, one possible cause of that is that we are decrypting - // the same event, and may not indicate an actual replay attack. For - // example, this could happen if we receive events, forget about them, and - // then re-fetch them when we backfill. So we store the event ID and - // timestamp corresponding to each message index when we first decrypt it, - // and compare these against the event ID and timestamp every time we use - // that same index. If they match, then we're probably decrypting the same - // event and we don't consider it a replay attack. - // - // Keys are strings of form "||" - // Values are objects of the form "{id: , timestamp: }" - this.inboundGroupSessionMessageIndexes = {}; - // Keep track of sessions that we're starting, so that we don't start - // multiple sessions for the same device at the same time. - this.sessionsInProgress = {}; // set by consumers - // Used by olm to serialise prekey message decryptions - this.olmPrekeyPromise = Promise.resolve(); // set by consumers - } - /** - * @returns The version of Olm. - */ - static getOlmVersion() { - return global.Olm.get_library_version(); - } - /** - * Initialise the OlmAccount. This must be called before any other operations - * on the OlmDevice. - * - * Data from an exported Olm device can be provided - * in order to re-create this device. - * - * Attempts to load the OlmAccount from the crypto store, or creates one if none is - * found. - * - * Reads the device keys from the OlmAccount object. - * - * @param fromExportedDevice - (Optional) data from exported device - * that must be re-created. - * If present, opts.pickleKey is ignored - * (exported data already provides a pickle key) - * @param pickleKey - (Optional) pickle key to set instead of default one - */ - init({ pickleKey, fromExportedDevice } = {}) { - return __awaiter(this, void 0, void 0, function* () { - let e2eKeys; - const account = new global.Olm.Account(); - try { - if (fromExportedDevice) { - if (pickleKey) { - logger_1.logger.warn("ignoring opts.pickleKey" + " because opts.fromExportedDevice is present."); - } - this.pickleKey = fromExportedDevice.pickleKey; - yield this.initialiseFromExportedDevice(fromExportedDevice, account); - } - else { - if (pickleKey) { - this.pickleKey = pickleKey; - } - yield this.initialiseAccount(account); - } - e2eKeys = JSON.parse(account.identity_keys()); - this.maxOneTimeKeys = account.max_number_of_one_time_keys(); - } - finally { - account.free(); - } - this.deviceCurve25519Key = e2eKeys.curve25519; - this.deviceEd25519Key = e2eKeys.ed25519; - }); - } - /** - * Populates the crypto store using data that was exported from an existing device. - * Note that for now only the “account” and “sessions” stores are populated; - * Other stores will be as with a new device. - * - * @param exportedData - Data exported from another device - * through the “export” method. - * @param account - an olm account to initialize - */ - initialiseFromExportedDevice(exportedData, account) { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT, indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.cryptoStore.storeAccount(txn, exportedData.pickledAccount); - exportedData.sessions.forEach((session) => { - const { deviceKey, sessionId } = session; - const sessionInfo = { - session: session.session, - lastReceivedMessageTs: session.lastReceivedMessageTs, - }; - this.cryptoStore.storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn); - }); - }); - account.unpickle(this.pickleKey, exportedData.pickledAccount); - }); - } - initialiseAccount(account) { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.cryptoStore.getAccount(txn, (pickledAccount) => { - if (pickledAccount !== null) { - account.unpickle(this.pickleKey, pickledAccount); - } - else { - account.create(); - pickledAccount = account.pickle(this.pickleKey); - this.cryptoStore.storeAccount(txn, pickledAccount); - } - }); - }); - }); - } - /** - * extract our OlmAccount from the crypto store and call the given function - * with the account object - * The `account` object is usable only within the callback passed to this - * function and will be freed as soon the callback returns. It is *not* - * usable for the rest of the lifetime of the transaction. - * This function requires a live transaction object from cryptoStore.doTxn() - * and therefore may only be called in a doTxn() callback. - * - * @param txn - Opaque transaction object from cryptoStore.doTxn() - * @internal - */ - getAccount(txn, func) { - this.cryptoStore.getAccount(txn, (pickledAccount) => { - const account = new global.Olm.Account(); - try { - account.unpickle(this.pickleKey, pickledAccount); - func(account); - } - finally { - account.free(); - } - }); - } - /* - * Saves an account to the crypto store. - * This function requires a live transaction object from cryptoStore.doTxn() - * and therefore may only be called in a doTxn() callback. - * - * @param txn - Opaque transaction object from cryptoStore.doTxn() - * @param Olm.Account object - * @internal - */ - storeAccount(txn, account) { - this.cryptoStore.storeAccount(txn, account.pickle(this.pickleKey)); - } - /** - * Export data for re-creating the Olm device later. - * TODO export data other than just account and (P2P) sessions. - * - * @returns The exported data - */ - export() { - return __awaiter(this, void 0, void 0, function* () { - const result = { - pickleKey: this.pickleKey, - }; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT, indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.cryptoStore.getAccount(txn, (pickledAccount) => { - result.pickledAccount = pickledAccount; - }); - result.sessions = []; - // Note that the pickledSession object we get in the callback - // is not exactly the same thing you get in method _getSession - // see documentation of IndexedDBCryptoStore.getAllEndToEndSessions - this.cryptoStore.getAllEndToEndSessions(txn, (pickledSession) => { - result.sessions.push(pickledSession); - }); - }); - return result; - }); - } - /** - * extract an OlmSession from the session store and call the given function - * The session is usable only within the callback passed to this - * function and will be freed as soon the callback returns. It is *not* - * usable for the rest of the lifetime of the transaction. - * - * @param txn - Opaque transaction object from cryptoStore.doTxn() - * @internal - */ - getSession(deviceKey, sessionId, txn, func) { - this.cryptoStore.getEndToEndSession(deviceKey, sessionId, txn, (sessionInfo) => { - this.unpickleSession(sessionInfo, func); - }); - } - /** - * Creates a session object from a session pickle and executes the given - * function with it. The session object is destroyed once the function - * returns. - * - * @internal - */ - unpickleSession(sessionInfo, func) { - const session = new global.Olm.Session(); - try { - session.unpickle(this.pickleKey, sessionInfo.session); - const unpickledSessInfo = Object.assign({}, sessionInfo, { session }); - func(unpickledSessInfo); - } - finally { - session.free(); - } - } - /** - * store our OlmSession in the session store - * - * @param sessionInfo - `{session: OlmSession, lastReceivedMessageTs: int}` - * @param txn - Opaque transaction object from cryptoStore.doTxn() - * @internal - */ - saveSession(deviceKey, sessionInfo, txn) { - const sessionId = sessionInfo.session.session_id(); - logger_1.logger.debug(`Saving Olm session ${sessionId} with device ${deviceKey}: ${sessionInfo.session.describe()}`); - // Why do we re-use the input object for this, overwriting the same key with a different - // type? Is it because we want to erase the unpickled session to enforce that it's no longer - // used? A comment would be great. - const pickledSessionInfo = Object.assign(sessionInfo, { - session: sessionInfo.session.pickle(this.pickleKey), - }); - this.cryptoStore.storeEndToEndSession(deviceKey, sessionId, pickledSessionInfo, txn); - } - /** - * get an OlmUtility and call the given function - * - * @returns result of func - * @internal - */ - getUtility(func) { - const utility = new global.Olm.Utility(); - try { - return func(utility); - } - finally { - utility.free(); - } - } - /** - * Signs a message with the ed25519 key for this account. - * - * @param message - message to be signed - * @returns base64-encoded signature - */ - sign(message) { - return __awaiter(this, void 0, void 0, function* () { - let result; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - result = account.sign(message); - }); - }); - return result; - }); - } - /** - * Get the current (unused, unpublished) one-time keys for this account. - * - * @returns one time keys; an object with the single property - * curve25519, which is itself an object mapping key id to Curve25519 - * key. - */ - getOneTimeKeys() { - return __awaiter(this, void 0, void 0, function* () { - let result; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - result = JSON.parse(account.one_time_keys()); - }); - }); - return result; - }); - } - /** - * Get the maximum number of one-time keys we can store. - * - * @returns number of keys - */ - maxNumberOfOneTimeKeys() { - var _a; - return (_a = this.maxOneTimeKeys) !== null && _a !== void 0 ? _a : -1; - } - /** - * Marks all of the one-time keys as published. - */ - markKeysAsPublished() { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - account.mark_keys_as_published(); - this.storeAccount(txn, account); - }); - }); - }); - } - /** - * Generate some new one-time keys - * - * @param numKeys - number of keys to generate - * @returns Resolved once the account is saved back having generated the keys - */ - generateOneTimeKeys(numKeys) { - return this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - account.generate_one_time_keys(numKeys); - this.storeAccount(txn, account); - }); - }); - } - /** - * Generate a new fallback keys - * - * @returns Resolved once the account is saved back having generated the key - */ - generateFallbackKey() { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - account.generate_fallback_key(); - this.storeAccount(txn, account); - }); - }); - }); - } - getFallbackKey() { - return __awaiter(this, void 0, void 0, function* () { - let result; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - result = JSON.parse(account.unpublished_fallback_key()); - }); - }); - return result; - }); - } - forgetOldFallbackKey() { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.getAccount(txn, (account) => { - account.forget_old_fallback_key(); - this.storeAccount(txn, account); - }); - }); - }); - } - /** - * Generate a new outbound session - * - * The new session will be stored in the cryptoStore. - * - * @param theirIdentityKey - remote user's Curve25519 identity key - * @param theirOneTimeKey - remote user's one-time Curve25519 key - * @returns sessionId for the outbound session. - */ - createOutboundSession(theirIdentityKey, theirOneTimeKey) { - return __awaiter(this, void 0, void 0, function* () { - let newSessionId; - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT, indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.getAccount(txn, (account) => { - const session = new global.Olm.Session(); - try { - session.create_outbound(account, theirIdentityKey, theirOneTimeKey); - newSessionId = session.session_id(); - this.storeAccount(txn, account); - const sessionInfo = { - session, - // Pretend we've received a message at this point, otherwise - // if we try to send a message to the device, it won't use - // this session - lastReceivedMessageTs: Date.now(), - }; - this.saveSession(theirIdentityKey, sessionInfo, txn); - } - finally { - session.free(); - } - }); - }, logger_1.logger.withPrefix("[createOutboundSession]")); - return newSessionId; - }); - } - /** - * Generate a new inbound session, given an incoming message - * - * @param theirDeviceIdentityKey - remote user's Curve25519 identity key - * @param messageType - messageType field from the received message (must be 0) - * @param ciphertext - base64-encoded body from the received message - * - * @returns decrypted payload, and - * session id of new session - * - * @throws Error if the received message was not valid (for instance, it didn't use a valid one-time key). - */ - createInboundSession(theirDeviceIdentityKey, messageType, ciphertext) { - return __awaiter(this, void 0, void 0, function* () { - if (messageType !== 0) { - throw new Error("Need messageType == 0 to create inbound session"); - } - let result; // eslint-disable-line camelcase - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT, indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.getAccount(txn, (account) => { - const session = new global.Olm.Session(); - try { - session.create_inbound_from(account, theirDeviceIdentityKey, ciphertext); - account.remove_one_time_keys(session); - this.storeAccount(txn, account); - const payloadString = session.decrypt(messageType, ciphertext); - const sessionInfo = { - session, - // this counts as a received message: set last received message time - // to now - lastReceivedMessageTs: Date.now(), - }; - this.saveSession(theirDeviceIdentityKey, sessionInfo, txn); - result = { - payload: payloadString, - session_id: session.session_id(), - }; - } - finally { - session.free(); - } - }); - }, logger_1.logger.withPrefix("[createInboundSession]")); - return result; - }); - } - /** - * Get a list of known session IDs for the given device - * - * @param theirDeviceIdentityKey - Curve25519 identity key for the - * remote device - * @returns a list of known session ids for the device - */ - getSessionIdsForDevice(theirDeviceIdentityKey) { - return __awaiter(this, void 0, void 0, function* () { - const log = logger_1.logger.withPrefix("[getSessionIdsForDevice]"); - if (theirDeviceIdentityKey in this.sessionsInProgress) { - log.debug(`Waiting for Olm session for ${theirDeviceIdentityKey} to be created`); - try { - yield this.sessionsInProgress[theirDeviceIdentityKey]; - } - catch (e) { - // if the session failed to be created, just fall through and - // return an empty result - } - } - let sessionIds; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.cryptoStore.getEndToEndSessions(theirDeviceIdentityKey, txn, (sessions) => { - sessionIds = Object.keys(sessions); - }); - }, log); - return sessionIds; - }); - } - /** - * Get the right olm session id for encrypting messages to the given identity key - * - * @param theirDeviceIdentityKey - Curve25519 identity key for the - * remote device - * @param nowait - Don't wait for an in-progress session to complete. - * This should only be set to true of the calling function is the function - * that marked the session as being in-progress. - * @param log - A possibly customised log - * @returns session id, or null if no established session - */ - getSessionIdForDevice(theirDeviceIdentityKey, nowait = false, log) { - return __awaiter(this, void 0, void 0, function* () { - const sessionInfos = yield this.getSessionInfoForDevice(theirDeviceIdentityKey, nowait, log); - if (sessionInfos.length === 0) { - return null; - } - // Use the session that has most recently received a message - let idxOfBest = 0; - for (let i = 1; i < sessionInfos.length; i++) { - const thisSessInfo = sessionInfos[i]; - const thisLastReceived = thisSessInfo.lastReceivedMessageTs === undefined ? 0 : thisSessInfo.lastReceivedMessageTs; - const bestSessInfo = sessionInfos[idxOfBest]; - const bestLastReceived = bestSessInfo.lastReceivedMessageTs === undefined ? 0 : bestSessInfo.lastReceivedMessageTs; - if (thisLastReceived > bestLastReceived || - (thisLastReceived === bestLastReceived && thisSessInfo.sessionId < bestSessInfo.sessionId)) { - idxOfBest = i; - } - } - return sessionInfos[idxOfBest].sessionId; - }); - } - /** - * Get information on the active Olm sessions for a device. - *

- * Returns an array, with an entry for each active session. The first entry in - * the result will be the one used for outgoing messages. Each entry contains - * the keys 'hasReceivedMessage' (true if the session has received an incoming - * message and is therefore past the pre-key stage), and 'sessionId'. - * - * @param deviceIdentityKey - Curve25519 identity key for the device - * @param nowait - Don't wait for an in-progress session to complete. - * This should only be set to true of the calling function is the function - * that marked the session as being in-progress. - * @param log - A possibly customised log - */ - getSessionInfoForDevice(deviceIdentityKey, nowait = false, log = logger_1.logger) { - return __awaiter(this, void 0, void 0, function* () { - log = log.withPrefix("[getSessionInfoForDevice]"); - if (deviceIdentityKey in this.sessionsInProgress && !nowait) { - log.debug(`Waiting for Olm session for ${deviceIdentityKey} to be created`); - try { - yield this.sessionsInProgress[deviceIdentityKey]; - } - catch (e) { - // if the session failed to be created, then just fall through and - // return an empty result - } - } - const info = []; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.cryptoStore.getEndToEndSessions(deviceIdentityKey, txn, (sessions) => { - const sessionIds = Object.keys(sessions).sort(); - for (const sessionId of sessionIds) { - this.unpickleSession(sessions[sessionId], (sessInfo) => { - info.push({ - lastReceivedMessageTs: sessInfo.lastReceivedMessageTs, - hasReceivedMessage: sessInfo.session.has_received_message(), - sessionId, - }); - }); - } - }); - }, log); - return info; - }); - } - /** - * Encrypt an outgoing message using an existing session - * - * @param theirDeviceIdentityKey - Curve25519 identity key for the - * remote device - * @param sessionId - the id of the active session - * @param payloadString - payload to be encrypted and sent - * - * @returns ciphertext - */ - encryptMessage(theirDeviceIdentityKey, sessionId, payloadString) { - return __awaiter(this, void 0, void 0, function* () { - checkPayloadLength(payloadString); - let res; - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => { - const sessionDesc = sessionInfo.session.describe(); - logger_1.logger.log("encryptMessage: Olm Session ID " + - sessionId + - " to " + - theirDeviceIdentityKey + - ": " + - sessionDesc); - res = sessionInfo.session.encrypt(payloadString); - this.saveSession(theirDeviceIdentityKey, sessionInfo, txn); - }); - }, logger_1.logger.withPrefix("[encryptMessage]")); - return res; - }); - } - /** - * Decrypt an incoming message using an existing session - * - * @param theirDeviceIdentityKey - Curve25519 identity key for the - * remote device - * @param sessionId - the id of the active session - * @param messageType - messageType field from the received message - * @param ciphertext - base64-encoded body from the received message - * - * @returns decrypted payload. - */ - decryptMessage(theirDeviceIdentityKey, sessionId, messageType, ciphertext) { - return __awaiter(this, void 0, void 0, function* () { - let payloadString; - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => { - const sessionDesc = sessionInfo.session.describe(); - logger_1.logger.log("decryptMessage: Olm Session ID " + - sessionId + - " from " + - theirDeviceIdentityKey + - ": " + - sessionDesc); - payloadString = sessionInfo.session.decrypt(messageType, ciphertext); - sessionInfo.lastReceivedMessageTs = Date.now(); - this.saveSession(theirDeviceIdentityKey, sessionInfo, txn); - }); - }, logger_1.logger.withPrefix("[decryptMessage]")); - return payloadString; - }); - } - /** - * Determine if an incoming messages is a prekey message matching an existing session - * - * @param theirDeviceIdentityKey - Curve25519 identity key for the - * remote device - * @param sessionId - the id of the active session - * @param messageType - messageType field from the received message - * @param ciphertext - base64-encoded body from the received message - * - * @returns true if the received message is a prekey message which matches - * the given session. - */ - matchesSession(theirDeviceIdentityKey, sessionId, messageType, ciphertext) { - return __awaiter(this, void 0, void 0, function* () { - if (messageType !== 0) { - return false; - } - let matches; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SESSIONS], (txn) => { - this.getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => { - matches = sessionInfo.session.matches_inbound(ciphertext); - }); - }, logger_1.logger.withPrefix("[matchesSession]")); - return matches; - }); - } - recordSessionProblem(deviceKey, type, fixed) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info(`Recording problem on olm session with ${deviceKey} of type ${type}. Recreating: ${fixed}`); - yield this.cryptoStore.storeEndToEndSessionProblem(deviceKey, type, fixed); - }); - } - sessionMayHaveProblems(deviceKey, timestamp) { - return this.cryptoStore.getEndToEndSessionProblem(deviceKey, timestamp); - } - filterOutNotifiedErrorDevices(devices) { - return this.cryptoStore.filterOutNotifiedErrorDevices(devices); - } - // Outbound group session - // ====================== - /** - * store an OutboundGroupSession in outboundGroupSessionStore - * - * @internal - */ - saveOutboundGroupSession(session) { - this.outboundGroupSessionStore[session.session_id()] = session.pickle(this.pickleKey); - } - /** - * extract an OutboundGroupSession from outboundGroupSessionStore and call the - * given function - * - * @returns result of func - * @internal - */ - getOutboundGroupSession(sessionId, func) { - const pickled = this.outboundGroupSessionStore[sessionId]; - if (pickled === undefined) { - throw new Error("Unknown outbound group session " + sessionId); - } - const session = new global.Olm.OutboundGroupSession(); - try { - session.unpickle(this.pickleKey, pickled); - return func(session); - } - finally { - session.free(); - } - } - /** - * Generate a new outbound group session - * - * @returns sessionId for the outbound session. - */ - createOutboundGroupSession() { - const session = new global.Olm.OutboundGroupSession(); - try { - session.create(); - this.saveOutboundGroupSession(session); - return session.session_id(); - } - finally { - session.free(); - } - } - /** - * Encrypt an outgoing message with an outbound group session - * - * @param sessionId - the id of the outboundgroupsession - * @param payloadString - payload to be encrypted and sent - * - * @returns ciphertext - */ - encryptGroupMessage(sessionId, payloadString) { - logger_1.logger.log(`encrypting msg with megolm session ${sessionId}`); - checkPayloadLength(payloadString); - return this.getOutboundGroupSession(sessionId, (session) => { - const res = session.encrypt(payloadString); - this.saveOutboundGroupSession(session); - return res; - }); - } - /** - * Get the session keys for an outbound group session - * - * @param sessionId - the id of the outbound group session - * - * @returns current chain index, and - * base64-encoded secret key. - */ - getOutboundGroupSessionKey(sessionId) { - return this.getOutboundGroupSession(sessionId, function (session) { - return { - chain_index: session.message_index(), - key: session.session_key(), - }; - }); - } - // Inbound group session - // ===================== - /** - * Unpickle a session from a sessionData object and invoke the given function. - * The session is valid only until func returns. - * - * @param sessionData - Object describing the session. - * @param func - Invoked with the unpickled session - * @returns result of func - */ - unpickleInboundGroupSession(sessionData, func) { - const session = new global.Olm.InboundGroupSession(); - try { - session.unpickle(this.pickleKey, sessionData.session); - return func(session); - } - finally { - session.free(); - } - } - /** - * extract an InboundGroupSession from the crypto store and call the given function - * - * @param roomId - The room ID to extract the session for, or null to fetch - * sessions for any room. - * @param txn - Opaque transaction object from cryptoStore.doTxn() - * @param func - function to call. - * - * @internal - */ - getInboundGroupSession(roomId, senderKey, sessionId, txn, func) { - this.cryptoStore.getEndToEndInboundGroupSession(senderKey, sessionId, txn, (sessionData, withheld) => { - if (sessionData === null) { - func(null, null, withheld); - return; - } - // if we were given a room ID, check that the it matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (roomId !== null && roomId !== sessionData.room_id) { - throw new Error("Mismatched room_id for inbound group session (expected " + - sessionData.room_id + - ", was " + - roomId + - ")"); - } - this.unpickleInboundGroupSession(sessionData, (session) => { - func(session, sessionData, withheld); - }); - }); - } - /** - * Add an inbound group session to the session store - * - * @param roomId - room in which this session will be used - * @param senderKey - base64-encoded curve25519 key of the sender - * @param forwardingCurve25519KeyChain - Devices involved in forwarding - * this session to us. - * @param sessionId - session identifier - * @param sessionKey - base64-encoded secret key - * @param keysClaimed - Other keys the sender claims. - * @param exportFormat - true if the megolm keys are in export format - * (ie, they lack an ed25519 signature) - * @param extraSessionData - any other data to be include with the session - */ - addInboundGroupSession(roomId, senderKey, forwardingCurve25519KeyChain, sessionId, sessionKey, keysClaimed, exportFormat, extraSessionData = {}) { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [ - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS, - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD, - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SHARED_HISTORY_INBOUND_GROUP_SESSIONS, - ], (txn) => { - /* if we already have this session, consider updating it */ - this.getInboundGroupSession(roomId, senderKey, sessionId, txn, (existingSession, existingSessionData) => { - // new session. - const session = new global.Olm.InboundGroupSession(); - try { - if (exportFormat) { - session.import_session(sessionKey); - } - else { - session.create(sessionKey); - } - if (sessionId != session.session_id()) { - throw new Error("Mismatched group session ID from senderKey: " + senderKey); - } - if (existingSession) { - logger_1.logger.log(`Update for megolm session ${senderKey}|${sessionId}`); - if (existingSession.first_known_index() <= session.first_known_index()) { - if (!existingSessionData.untrusted || extraSessionData.untrusted) { - // existing session has less-than-or-equal index - // (i.e. can decrypt at least as much), and the - // new session's trust does not win over the old - // session's trust, so keep it - logger_1.logger.log(`Keeping existing megolm session ${senderKey}|${sessionId}`); - return; - } - if (existingSession.first_known_index() < session.first_known_index()) { - // We want to upgrade the existing session's trust, - // but we can't just use the new session because we'll - // lose the lower index. Check that the sessions connect - // properly, and then manually set the existing session - // as trusted. - if (existingSession.export_session(session.first_known_index()) === - session.export_session(session.first_known_index())) { - logger_1.logger.info("Upgrading trust of existing megolm session " + - `${senderKey}|${sessionId} based on newly-received trusted session`); - existingSessionData.untrusted = false; - this.cryptoStore.storeEndToEndInboundGroupSession(senderKey, sessionId, existingSessionData, txn); - } - else { - logger_1.logger.warn(`Newly-received megolm session ${senderKey}|$sessionId}` + - " does not match existing session! Keeping existing session"); - } - return; - } - // If the sessions have the same index, go ahead and store the new trusted one. - } - } - logger_1.logger.info(`Storing megolm session ${senderKey}|${sessionId} with first index ` + - session.first_known_index()); - const sessionData = Object.assign({}, extraSessionData, { - room_id: roomId, - session: session.pickle(this.pickleKey), - keysClaimed: keysClaimed, - forwardingCurve25519KeyChain: forwardingCurve25519KeyChain, - }); - this.cryptoStore.storeEndToEndInboundGroupSession(senderKey, sessionId, sessionData, txn); - if (!existingSession && extraSessionData.sharedHistory) { - this.cryptoStore.addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId, txn); - } - } - finally { - session.free(); - } - }); - }, logger_1.logger.withPrefix("[addInboundGroupSession]")); - }); - } - /** - * Record in the data store why an inbound group session was withheld. - * - * @param roomId - room that the session belongs to - * @param senderKey - base64-encoded curve25519 key of the sender - * @param sessionId - session identifier - * @param code - reason code - * @param reason - human-readable version of `code` - */ - addInboundGroupSessionWithheld(roomId, senderKey, sessionId, code, reason) { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD], (txn) => { - this.cryptoStore.storeEndToEndInboundGroupSessionWithheld(senderKey, sessionId, { - room_id: roomId, - code: code, - reason: reason, - }, txn); - }); - }); - } - /** - * Decrypt a received message with an inbound group session - * - * @param roomId - room in which the message was received - * @param senderKey - base64-encoded curve25519 key of the sender - * @param sessionId - session identifier - * @param body - base64-encoded body of the encrypted message - * @param eventId - ID of the event being decrypted - * @param timestamp - timestamp of the event being decrypted - * - * @returns null if the sessionId is unknown - */ - decryptGroupMessage(roomId, senderKey, sessionId, body, eventId, timestamp) { - return __awaiter(this, void 0, void 0, function* () { - let result = null; - // when the localstorage crypto store is used as an indexeddb backend, - // exceptions thrown from within the inner function are not passed through - // to the top level, so we store exceptions in a variable and raise them at - // the end - let error; - yield this.cryptoStore.doTxn("readwrite", [ - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS, - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD, - ], (txn) => { - this.getInboundGroupSession(roomId, senderKey, sessionId, txn, (session, sessionData, withheld) => { - if (session === null || sessionData === null) { - if (withheld) { - error = new algorithms.DecryptionError("MEGOLM_UNKNOWN_INBOUND_SESSION_ID", calculateWithheldMessage(withheld), { - session: senderKey + "|" + sessionId, - }); - } - result = null; - return; - } - let res; - try { - res = session.decrypt(body); - } - catch (e) { - if ((e === null || e === void 0 ? void 0 : e.message) === "OLM.UNKNOWN_MESSAGE_INDEX" && withheld) { - error = new algorithms.DecryptionError("MEGOLM_UNKNOWN_INBOUND_SESSION_ID", calculateWithheldMessage(withheld), { - session: senderKey + "|" + sessionId, - }); - } - else { - error = e; - } - return; - } - let plaintext = res.plaintext; - if (plaintext === undefined) { - // @ts-ignore - Compatibility for older olm versions. - plaintext = res; - } - else { - // Check if we have seen this message index before to detect replay attacks. - // If the event ID and timestamp are specified, and the match the event ID - // and timestamp from the last time we used this message index, then we - // don't consider it a replay attack. - const messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index; - if (messageIndexKey in this.inboundGroupSessionMessageIndexes) { - const msgInfo = this.inboundGroupSessionMessageIndexes[messageIndexKey]; - if (msgInfo.id !== eventId || msgInfo.timestamp !== timestamp) { - error = new Error("Duplicate message index, possible replay attack: " + messageIndexKey); - return; - } - } - this.inboundGroupSessionMessageIndexes[messageIndexKey] = { - id: eventId, - timestamp: timestamp, - }; - } - sessionData.session = session.pickle(this.pickleKey); - this.cryptoStore.storeEndToEndInboundGroupSession(senderKey, sessionId, sessionData, txn); - result = { - result: plaintext, - keysClaimed: sessionData.keysClaimed || {}, - senderKey: senderKey, - forwardingCurve25519KeyChain: sessionData.forwardingCurve25519KeyChain || [], - untrusted: !!sessionData.untrusted, - }; - }); - }, logger_1.logger.withPrefix("[decryptGroupMessage]")); - if (error) { - throw error; - } - return result; - }); - } - /** - * Determine if we have the keys for a given megolm session - * - * @param roomId - room in which the message was received - * @param senderKey - base64-encoded curve25519 key of the sender - * @param sessionId - session identifier - * - * @returns true if we have the keys to this session - */ - hasInboundSessionKeys(roomId, senderKey, sessionId) { - return __awaiter(this, void 0, void 0, function* () { - let result; - yield this.cryptoStore.doTxn("readonly", [ - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS, - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD, - ], (txn) => { - this.cryptoStore.getEndToEndInboundGroupSession(senderKey, sessionId, txn, (sessionData) => { - if (sessionData === null) { - result = false; - return; - } - if (roomId !== sessionData.room_id) { - logger_1.logger.warn(`requested keys for inbound group session ${senderKey}|` + - `${sessionId}, with incorrect room_id ` + - `(expected ${sessionData.room_id}, ` + - `was ${roomId})`); - result = false; - } - else { - result = true; - } - }); - }, logger_1.logger.withPrefix("[hasInboundSessionKeys]")); - return result; - }); - } - /** - * Extract the keys to a given megolm session, for sharing - * - * @param roomId - room in which the message was received - * @param senderKey - base64-encoded curve25519 key of the sender - * @param sessionId - session identifier - * @param chainIndex - The chain index at which to export the session. - * If omitted, export at the first index we know about. - * - * @returns - * details of the session key. The key is a base64-encoded megolm key in - * export format. - * - * @throws Error If the given chain index could not be obtained from the known - * index (ie. the given chain index is before the first we have). - */ - getInboundGroupSessionKey(roomId, senderKey, sessionId, chainIndex) { - return __awaiter(this, void 0, void 0, function* () { - let result = null; - yield this.cryptoStore.doTxn("readonly", [ - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS, - indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD, - ], (txn) => { - this.getInboundGroupSession(roomId, senderKey, sessionId, txn, (session, sessionData) => { - if (session === null || sessionData === null) { - result = null; - return; - } - if (chainIndex === undefined) { - chainIndex = session.first_known_index(); - } - const exportedSession = session.export_session(chainIndex); - const claimedKeys = sessionData.keysClaimed || {}; - const senderEd25519Key = claimedKeys.ed25519 || null; - const forwardingKeyChain = sessionData.forwardingCurve25519KeyChain || []; - // older forwarded keys didn't set the "untrusted" - // property, but can be identified by having a - // non-empty forwarding key chain. These keys should - // be marked as untrusted since we don't know that they - // can be trusted - const untrusted = "untrusted" in sessionData ? sessionData.untrusted : forwardingKeyChain.length > 0; - result = { - chain_index: chainIndex, - key: exportedSession, - forwarding_curve25519_key_chain: forwardingKeyChain, - sender_claimed_ed25519_key: senderEd25519Key, - shared_history: sessionData.sharedHistory || false, - untrusted: untrusted, - }; - }); - }, logger_1.logger.withPrefix("[getInboundGroupSessionKey]")); - return result; - }); - } - /** - * Export an inbound group session - * - * @param senderKey - base64-encoded curve25519 key of the sender - * @param sessionId - session identifier - * @param sessionData - The session object from the store - * @returns exported session data - */ - exportInboundGroupSession(senderKey, sessionId, sessionData) { - return this.unpickleInboundGroupSession(sessionData, (session) => { - const messageIndex = session.first_known_index(); - return { - "sender_key": senderKey, - "sender_claimed_keys": sessionData.keysClaimed, - "room_id": sessionData.room_id, - "session_id": sessionId, - "session_key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain || [], - "first_known_index": session.first_known_index(), - "org.matrix.msc3061.shared_history": sessionData.sharedHistory || false, - }; - }); - } - getSharedHistoryInboundGroupSessions(roomId) { - return __awaiter(this, void 0, void 0, function* () { - let result; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_SHARED_HISTORY_INBOUND_GROUP_SESSIONS], (txn) => { - result = this.cryptoStore.getSharedHistoryInboundGroupSessions(roomId, txn); - }, logger_1.logger.withPrefix("[getSharedHistoryInboundGroupSessionsForRoom]")); - return result; - }); - } - // Utilities - // ========= - /** - * Verify an ed25519 signature. - * - * @param key - ed25519 key - * @param message - message which was signed - * @param signature - base64-encoded signature to be checked - * - * @throws Error if there is a problem with the verification. If the key was - * too small then the message will be "OLM.INVALID_BASE64". If the signature - * was invalid then the message will be "OLM.BAD_MESSAGE_MAC". - */ - verifySignature(key, message, signature) { - this.getUtility(function (util) { - util.ed25519_verify(key, message, signature); - }); - } -} -exports.OlmDevice = OlmDevice; -exports.WITHHELD_MESSAGES = { - "m.unverified": "The sender has disabled encrypting to unverified devices.", - "m.blacklisted": "The sender has blocked you.", - "m.unauthorised": "You are not authorised to read the message.", - "m.no_olm": "Unable to establish a secure channel.", -}; -/** - * Calculate the message to use for the exception when a session key is withheld. - * - * @param withheld - An object that describes why the key was withheld. - * - * @returns the message - * - * @internal - */ -function calculateWithheldMessage(withheld) { - if (withheld.code && withheld.code in exports.WITHHELD_MESSAGES) { - return exports.WITHHELD_MESSAGES[withheld.code]; - } - else if (withheld.reason) { - return withheld.reason; - } - else { - return "decryption key withheld"; - } -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../logger":374,"./algorithms":333,"./store/indexeddb-crypto-store":346}],328:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OutgoingRoomKeyRequestManager = exports.RoomKeyRequestState = void 0; -const uuid_1 = require("uuid"); -const logger_1 = require("../logger"); -const event_1 = require("../@types/event"); -const utils_1 = require("../utils"); -/** - * Internal module. Management of outgoing room key requests. - * - * See https://docs.google.com/document/d/1m4gQkcnJkxNuBmb5NoFCIadIY-DyqqNAS3lloE73BlQ - * for draft documentation on what we're supposed to be implementing here. - */ -// delay between deciding we want some keys, and sending out the request, to -// allow for (a) it turning up anyway, (b) grouping requests together -const SEND_KEY_REQUESTS_DELAY_MS = 500; -/** - * possible states for a room key request - * - * The state machine looks like: - * ``` - * - * | (cancellation sent) - * | .-------------------------------------------------. - * | | | - * V V (cancellation requested) | - * UNSENT -----------------------------+ | - * | | | - * | | | - * | (send successful) | CANCELLATION_PENDING_AND_WILL_RESEND - * V | Λ - * SENT | | - * |-------------------------------- | --------------' - * | | (cancellation requested with intent - * | | to resend the original request) - * | | - * | (cancellation requested) | - * V | - * CANCELLATION_PENDING | - * | | - * | (cancellation sent) | - * V | - * (deleted) <---------------------------+ - * ``` - */ -var RoomKeyRequestState; -(function (RoomKeyRequestState) { - /** request not yet sent */ - RoomKeyRequestState[RoomKeyRequestState["Unsent"] = 0] = "Unsent"; - /** request sent, awaiting reply */ - RoomKeyRequestState[RoomKeyRequestState["Sent"] = 1] = "Sent"; - /** reply received, cancellation not yet sent */ - RoomKeyRequestState[RoomKeyRequestState["CancellationPending"] = 2] = "CancellationPending"; - /** - * Cancellation not yet sent and will transition to UNSENT instead of - * being deleted once the cancellation has been sent. - */ - RoomKeyRequestState[RoomKeyRequestState["CancellationPendingAndWillResend"] = 3] = "CancellationPendingAndWillResend"; -})(RoomKeyRequestState = exports.RoomKeyRequestState || (exports.RoomKeyRequestState = {})); -class OutgoingRoomKeyRequestManager { - constructor(baseApis, deviceId, cryptoStore) { - this.baseApis = baseApis; - this.deviceId = deviceId; - this.cryptoStore = cryptoStore; - // sanity check to ensure that we don't end up with two concurrent runs - // of sendOutgoingRoomKeyRequests - this.sendOutgoingRoomKeyRequestsRunning = false; - this.clientRunning = true; - } - /** - * Called when the client is stopped. Stops any running background processes. - */ - stop() { - logger_1.logger.log("stopping OutgoingRoomKeyRequestManager"); - // stop the timer on the next run - this.clientRunning = false; - } - /** - * Send any requests that have been queued - */ - sendQueuedRequests() { - this.startTimer(); - } - /** - * Queue up a room key request, if we haven't already queued or sent one. - * - * The `requestBody` is compared (with a deep-equality check) against - * previous queued or sent requests and if it matches, no change is made. - * Otherwise, a request is added to the pending list, and a job is started - * in the background to send it. - * - * @param resend - whether to resend the key request if there is - * already one - * - * @returns resolves when the request has been added to the - * pending list (or we have established that a similar request already - * exists) - */ - queueRoomKeyRequest(requestBody, recipients, resend = false) { - return __awaiter(this, void 0, void 0, function* () { - const req = yield this.cryptoStore.getOutgoingRoomKeyRequest(requestBody); - if (!req) { - yield this.cryptoStore.getOrAddOutgoingRoomKeyRequest({ - requestBody: requestBody, - recipients: recipients, - requestId: this.baseApis.makeTxnId(), - state: RoomKeyRequestState.Unsent, - }); - } - else { - switch (req.state) { - case RoomKeyRequestState.CancellationPendingAndWillResend: - case RoomKeyRequestState.Unsent: - // nothing to do here, since we're going to send a request anyways - return; - case RoomKeyRequestState.CancellationPending: { - // existing request is about to be cancelled. If we want to - // resend, then change the state so that it resends after - // cancelling. Otherwise, just cancel the cancellation. - const state = resend - ? RoomKeyRequestState.CancellationPendingAndWillResend - : RoomKeyRequestState.Sent; - yield this.cryptoStore.updateOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.CancellationPending, { - state, - cancellationTxnId: this.baseApis.makeTxnId(), - }); - break; - } - case RoomKeyRequestState.Sent: { - // a request has already been sent. If we don't want to - // resend, then do nothing. If we do want to, then cancel the - // existing request and send a new one. - if (resend) { - const state = RoomKeyRequestState.CancellationPendingAndWillResend; - const updatedReq = yield this.cryptoStore.updateOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.Sent, { - state, - cancellationTxnId: this.baseApis.makeTxnId(), - // need to use a new transaction ID so that - // the request gets sent - requestTxnId: this.baseApis.makeTxnId(), - }); - if (!updatedReq) { - // updateOutgoingRoomKeyRequest couldn't find the request - // in state ROOM_KEY_REQUEST_STATES.SENT, so we must have - // raced with another tab to mark the request cancelled. - // Try again, to make sure the request is resent. - return this.queueRoomKeyRequest(requestBody, recipients, resend); - } - // We don't want to wait for the timer, so we send it - // immediately. (We might actually end up racing with the timer, - // but that's ok: even if we make the request twice, we'll do it - // with the same transaction_id, so only one message will get - // sent). - // - // (We also don't want to wait for the response from the server - // here, as it will slow down processing of received keys if we - // do.) - try { - yield this.sendOutgoingRoomKeyRequestCancellation(updatedReq, true); - } - catch (e) { - logger_1.logger.error("Error sending room key request cancellation;" + " will retry later.", e); - } - // The request has transitioned from - // CANCELLATION_PENDING_AND_WILL_RESEND to UNSENT. We - // still need to resend the request which is now UNSENT, so - // start the timer if it isn't already started. - } - break; - } - default: - throw new Error("unhandled state: " + req.state); - } - } - }); - } - /** - * Cancel room key requests, if any match the given requestBody - * - * - * @returns resolves when the request has been updated in our - * pending list. - */ - cancelRoomKeyRequest(requestBody) { - return this.cryptoStore.getOutgoingRoomKeyRequest(requestBody).then((req) => { - if (!req) { - // no request was made for this key - return; - } - switch (req.state) { - case RoomKeyRequestState.CancellationPending: - case RoomKeyRequestState.CancellationPendingAndWillResend: - // nothing to do here - return; - case RoomKeyRequestState.Unsent: - // just delete it - // FIXME: ghahah we may have attempted to send it, and - // not yet got a successful response. So the server - // may have seen it, so we still need to send a cancellation - // in that case :/ - logger_1.logger.log("deleting unnecessary room key request for " + stringifyRequestBody(requestBody)); - return this.cryptoStore.deleteOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.Unsent); - case RoomKeyRequestState.Sent: { - // send a cancellation. - return this.cryptoStore - .updateOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.Sent, { - state: RoomKeyRequestState.CancellationPending, - cancellationTxnId: this.baseApis.makeTxnId(), - }) - .then((updatedReq) => { - if (!updatedReq) { - // updateOutgoingRoomKeyRequest couldn't find the - // request in state ROOM_KEY_REQUEST_STATES.SENT, - // so we must have raced with another tab to mark - // the request cancelled. There is no point in - // sending another cancellation since the other tab - // will do it. - logger_1.logger.log("Tried to cancel room key request for " + - stringifyRequestBody(requestBody) + - " but it was already cancelled in another tab"); - return; - } - // We don't want to wait for the timer, so we send it - // immediately. (We might actually end up racing with the timer, - // but that's ok: even if we make the request twice, we'll do it - // with the same transaction_id, so only one message will get - // sent). - // - // (We also don't want to wait for the response from the server - // here, as it will slow down processing of received keys if we - // do.) - this.sendOutgoingRoomKeyRequestCancellation(updatedReq).catch((e) => { - logger_1.logger.error("Error sending room key request cancellation;" + " will retry later.", e); - this.startTimer(); - }); - }); - } - default: - throw new Error("unhandled state: " + req.state); - } - }); - } - /** - * Look for room key requests by target device and state - * - * @param userId - Target user ID - * @param deviceId - Target device ID - * - * @returns resolves to a list of all the {@link OutgoingRoomKeyRequest} - */ - getOutgoingSentRoomKeyRequest(userId, deviceId) { - return this.cryptoStore.getOutgoingRoomKeyRequestsByTarget(userId, deviceId, [RoomKeyRequestState.Sent]); - } - /** - * Find anything in `sent` state, and kick it around the loop again. - * This is intended for situations where something substantial has changed, and we - * don't really expect the other end to even care about the cancellation. - * For example, after initialization or self-verification. - * @returns An array of `queueRoomKeyRequest` outputs. - */ - cancelAndResendAllOutgoingRequests() { - return __awaiter(this, void 0, void 0, function* () { - const outgoings = yield this.cryptoStore.getAllOutgoingRoomKeyRequestsByState(RoomKeyRequestState.Sent); - return Promise.all(outgoings.map(({ requestBody, recipients }) => this.queueRoomKeyRequest(requestBody, recipients, true))); - }); - } - // start the background timer to send queued requests, if the timer isn't - // already running - startTimer() { - if (this.sendOutgoingRoomKeyRequestsTimer) { - return; - } - const startSendingOutgoingRoomKeyRequests = () => { - if (this.sendOutgoingRoomKeyRequestsRunning) { - throw new Error("RoomKeyRequestSend already in progress!"); - } - this.sendOutgoingRoomKeyRequestsRunning = true; - this.sendOutgoingRoomKeyRequests() - .finally(() => { - this.sendOutgoingRoomKeyRequestsRunning = false; - }) - .catch((e) => { - // this should only happen if there is an indexeddb error, - // in which case we're a bit stuffed anyway. - logger_1.logger.warn(`error in OutgoingRoomKeyRequestManager: ${e}`); - }); - }; - this.sendOutgoingRoomKeyRequestsTimer = setTimeout(startSendingOutgoingRoomKeyRequests, SEND_KEY_REQUESTS_DELAY_MS); - } - // look for and send any queued requests. Runs itself recursively until - // there are no more requests, or there is an error (in which case, the - // timer will be restarted before the promise resolves). - sendOutgoingRoomKeyRequests() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.clientRunning) { - this.sendOutgoingRoomKeyRequestsTimer = undefined; - return; - } - const req = yield this.cryptoStore.getOutgoingRoomKeyRequestByState([ - RoomKeyRequestState.CancellationPending, - RoomKeyRequestState.CancellationPendingAndWillResend, - RoomKeyRequestState.Unsent, - ]); - if (!req) { - this.sendOutgoingRoomKeyRequestsTimer = undefined; - return; - } - try { - switch (req.state) { - case RoomKeyRequestState.Unsent: - yield this.sendOutgoingRoomKeyRequest(req); - break; - case RoomKeyRequestState.CancellationPending: - yield this.sendOutgoingRoomKeyRequestCancellation(req); - break; - case RoomKeyRequestState.CancellationPendingAndWillResend: - yield this.sendOutgoingRoomKeyRequestCancellation(req, true); - break; - } - // go around the loop again - return this.sendOutgoingRoomKeyRequests(); - } - catch (e) { - logger_1.logger.error("Error sending room key request; will retry later.", e); - this.sendOutgoingRoomKeyRequestsTimer = undefined; - } - }); - } - // given a RoomKeyRequest, send it and update the request record - sendOutgoingRoomKeyRequest(req) { - logger_1.logger.log(`Requesting keys for ${stringifyRequestBody(req.requestBody)}` + - ` from ${stringifyRecipientList(req.recipients)}` + - `(id ${req.requestId})`); - const requestMessage = { - action: "request", - requesting_device_id: this.deviceId, - request_id: req.requestId, - body: req.requestBody, - }; - return this.sendMessageToDevices(requestMessage, req.recipients, req.requestTxnId || req.requestId).then(() => { - return this.cryptoStore.updateOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.Unsent, { - state: RoomKeyRequestState.Sent, - }); - }); - } - // Given a RoomKeyRequest, cancel it and delete the request record unless - // andResend is set, in which case transition to UNSENT. - sendOutgoingRoomKeyRequestCancellation(req, andResend = false) { - logger_1.logger.log(`Sending cancellation for key request for ` + - `${stringifyRequestBody(req.requestBody)} to ` + - `${stringifyRecipientList(req.recipients)} ` + - `(cancellation id ${req.cancellationTxnId})`); - const requestMessage = { - action: "request_cancellation", - requesting_device_id: this.deviceId, - request_id: req.requestId, - }; - return this.sendMessageToDevices(requestMessage, req.recipients, req.cancellationTxnId).then(() => { - if (andResend) { - // We want to resend, so transition to UNSENT - return this.cryptoStore.updateOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.CancellationPendingAndWillResend, { state: RoomKeyRequestState.Unsent }); - } - return this.cryptoStore.deleteOutgoingRoomKeyRequest(req.requestId, RoomKeyRequestState.CancellationPending); - }); - } - // send a RoomKeyRequest to a list of recipients - sendMessageToDevices(message, recipients, txnId) { - const contentMap = new utils_1.MapWithDefault(() => new Map()); - for (const recip of recipients) { - const userDeviceMap = contentMap.getOrCreate(recip.userId); - userDeviceMap.set(recip.deviceId, Object.assign(Object.assign({}, message), { [event_1.ToDeviceMessageId]: (0, uuid_1.v4)() })); - } - return this.baseApis.sendToDevice(event_1.EventType.RoomKeyRequest, contentMap, txnId); - } -} -exports.OutgoingRoomKeyRequestManager = OutgoingRoomKeyRequestManager; -function stringifyRequestBody(requestBody) { - // we assume that the request is for megolm keys, which are identified by - // room id and session id - return requestBody.room_id + " / " + requestBody.session_id; -} -function stringifyRecipientList(recipients) { - return `[${recipients.map((r) => `${r.userId}:${r.deviceId}`).join(",")}]`; -} - -},{"../@types/event":306,"../logger":374,"../utils":416,"uuid":287}],329:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoomList = void 0; -const indexeddb_crypto_store_1 = require("./store/indexeddb-crypto-store"); -/* eslint-enable camelcase */ -class RoomList { - constructor(cryptoStore) { - this.cryptoStore = cryptoStore; - // Object of roomId -> room e2e info object (body of the m.room.encryption event) - this.roomEncryption = {}; - } - init() { - return __awaiter(this, void 0, void 0, function* () { - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ROOMS], (txn) => { - this.cryptoStore.getEndToEndRooms(txn, (result) => { - this.roomEncryption = result; - }); - }); - }); - } - getRoomEncryption(roomId) { - return this.roomEncryption[roomId] || null; - } - isRoomEncrypted(roomId) { - return Boolean(this.getRoomEncryption(roomId)); - } - setRoomEncryption(roomId, roomInfo) { - return __awaiter(this, void 0, void 0, function* () { - // important that this happens before calling into the store - // as it prevents the Crypto::setRoomEncryption from calling - // this twice for consecutive m.room.encryption events - this.roomEncryption[roomId] = roomInfo; - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ROOMS], (txn) => { - this.cryptoStore.storeEndToEndRoom(roomId, roomInfo, txn); - }); - }); - } -} -exports.RoomList = RoomList; - -},{"./store/indexeddb-crypto-store":346}],330:[function(require,module,exports){ -"use strict"; -/* -Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SecretStorage = exports.SECRET_STORAGE_ALGORITHM_V1_AES = void 0; -const uuid_1 = require("uuid"); -const logger_1 = require("../logger"); -const olmlib = __importStar(require("./olmlib")); -const randomstring_1 = require("../randomstring"); -const aes_1 = require("./aes"); -const client_1 = require("../client"); -const utils_1 = require("../utils"); -const event_1 = require("../@types/event"); -exports.SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2"; -/** - * Implements Secure Secret Storage and Sharing (MSC1946) - */ -class SecretStorage { - // In it's pure javascript days, this was relying on some proper Javascript-style - // type-abuse where sometimes we'd pass in a fake client object with just the account - // data methods implemented, which is all this class needs unless you use the secret - // sharing code, so it was fine. As a low-touch TypeScript migration, this now has - // an extra, optional param for a real matrix client, so you can not pass it as long - // as you don't request any secrets. - // A better solution would probably be to split this class up into secret storage and - // secret sharing which are really two separate things, even though they share an MSC. - constructor(accountDataAdapter, cryptoCallbacks, baseApis) { - this.accountDataAdapter = accountDataAdapter; - this.cryptoCallbacks = cryptoCallbacks; - this.baseApis = baseApis; - this.requests = new Map(); - } - getDefaultKeyId() { - return __awaiter(this, void 0, void 0, function* () { - const defaultKey = yield this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.default_key"); - if (!defaultKey) - return null; - return defaultKey.key; - }); - } - setDefaultKeyId(keyId) { - return new Promise((resolve, reject) => { - const listener = (ev) => { - if (ev.getType() === "m.secret_storage.default_key" && ev.getContent().key === keyId) { - this.accountDataAdapter.removeListener(client_1.ClientEvent.AccountData, listener); - resolve(); - } - }; - this.accountDataAdapter.on(client_1.ClientEvent.AccountData, listener); - this.accountDataAdapter.setAccountData("m.secret_storage.default_key", { key: keyId }).catch((e) => { - this.accountDataAdapter.removeListener(client_1.ClientEvent.AccountData, listener); - reject(e); - }); - }); - } - /** - * Add a key for encrypting secrets. - * - * @param algorithm - the algorithm used by the key. - * @param opts - the options for the algorithm. The properties used - * depend on the algorithm given. - * @param keyId - the ID of the key. If not given, a random - * ID will be generated. - * - * @returns An object with: - * keyId: the ID of the key - * keyInfo: details about the key (iv, mac, passphrase) - */ - addKey(algorithm, opts = {}, keyId) { - return __awaiter(this, void 0, void 0, function* () { - if (algorithm !== exports.SECRET_STORAGE_ALGORITHM_V1_AES) { - throw new Error(`Unknown key algorithm ${algorithm}`); - } - const keyInfo = { algorithm }; - if (opts.name) { - keyInfo.name = opts.name; - } - if (opts.passphrase) { - keyInfo.passphrase = opts.passphrase; - } - if (opts.key) { - const { iv, mac } = yield (0, aes_1.calculateKeyCheck)(opts.key); - keyInfo.iv = iv; - keyInfo.mac = mac; - } - if (!keyId) { - do { - keyId = (0, randomstring_1.randomString)(32); - } while (yield this.accountDataAdapter.getAccountDataFromServer(`m.secret_storage.key.${keyId}`)); - } - yield this.accountDataAdapter.setAccountData(`m.secret_storage.key.${keyId}`, keyInfo); - return { - keyId, - keyInfo, - }; - }); - } - /** - * Get the key information for a given ID. - * - * @param keyId - The ID of the key to check - * for. Defaults to the default key ID if not provided. - * @returns If the key was found, the return value is an array of - * the form [keyId, keyInfo]. Otherwise, null is returned. - * XXX: why is this an array when addKey returns an object? - */ - getKey(keyId) { - return __awaiter(this, void 0, void 0, function* () { - if (!keyId) { - keyId = yield this.getDefaultKeyId(); - } - if (!keyId) { - return null; - } - const keyInfo = yield this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId); - return keyInfo ? [keyId, keyInfo] : null; - }); - } - /** - * Check whether we have a key with a given ID. - * - * @param keyId - The ID of the key to check - * for. Defaults to the default key ID if not provided. - * @returns Whether we have the key. - */ - hasKey(keyId) { - return __awaiter(this, void 0, void 0, function* () { - return Boolean(yield this.getKey(keyId)); - }); - } - /** - * Check whether a key matches what we expect based on the key info - * - * @param key - the key to check - * @param info - the key info - * - * @returns whether or not the key matches - */ - checkKey(key, info) { - return __awaiter(this, void 0, void 0, function* () { - if (info.algorithm === exports.SECRET_STORAGE_ALGORITHM_V1_AES) { - if (info.mac) { - const { mac } = yield (0, aes_1.calculateKeyCheck)(key, info.iv); - return info.mac.replace(/=+$/g, "") === mac.replace(/=+$/g, ""); - } - else { - // if we have no information, we have to assume the key is right - return true; - } - } - else { - throw new Error("Unknown algorithm"); - } - }); - } - /** - * Store an encrypted secret on the server - * - * @param name - The name of the secret - * @param secret - The secret contents. - * @param keys - The IDs of the keys to use to encrypt the secret - * or null/undefined to use the default key. - */ - store(name, secret, keys) { - return __awaiter(this, void 0, void 0, function* () { - const encrypted = {}; - if (!keys) { - const defaultKeyId = yield this.getDefaultKeyId(); - if (!defaultKeyId) { - throw new Error("No keys specified and no default key present"); - } - keys = [defaultKeyId]; - } - if (keys.length === 0) { - throw new Error("Zero keys given to encrypt with!"); - } - for (const keyId of keys) { - // get key information from key storage - const keyInfo = yield this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId); - if (!keyInfo) { - throw new Error("Unknown key: " + keyId); - } - // encrypt secret, based on the algorithm - if (keyInfo.algorithm === exports.SECRET_STORAGE_ALGORITHM_V1_AES) { - const keys = { [keyId]: keyInfo }; - const [, encryption] = yield this.getSecretStorageKey(keys, name); - encrypted[keyId] = yield encryption.encrypt(secret); - } - else { - logger_1.logger.warn("unknown algorithm for secret storage key " + keyId + ": " + keyInfo.algorithm); - // do nothing if we don't understand the encryption algorithm - } - } - // save encrypted secret - yield this.accountDataAdapter.setAccountData(name, { encrypted }); - }); - } - /** - * Get a secret from storage. - * - * @param name - the name of the secret - * - * @returns the contents of the secret - */ - get(name) { - return __awaiter(this, void 0, void 0, function* () { - const secretInfo = yield this.accountDataAdapter.getAccountDataFromServer(name); - if (!secretInfo) { - return; - } - if (!secretInfo.encrypted) { - throw new Error("Content is not encrypted!"); - } - // get possible keys to decrypt - const keys = {}; - for (const keyId of Object.keys(secretInfo.encrypted)) { - // get key information from key storage - const keyInfo = yield this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId); - const encInfo = secretInfo.encrypted[keyId]; - // only use keys we understand the encryption algorithm of - if (keyInfo.algorithm === exports.SECRET_STORAGE_ALGORITHM_V1_AES) { - if (encInfo.iv && encInfo.ciphertext && encInfo.mac) { - keys[keyId] = keyInfo; - } - } - } - if (Object.keys(keys).length === 0) { - throw new Error(`Could not decrypt ${name} because none of ` + - `the keys it is encrypted with are for a supported algorithm`); - } - // fetch private key from app - const [keyId, decryption] = yield this.getSecretStorageKey(keys, name); - const encInfo = secretInfo.encrypted[keyId]; - return decryption.decrypt(encInfo); - }); - } - /** - * Check if a secret is stored on the server. - * - * @param name - the name of the secret - * - * @returns map of key name to key info the secret is encrypted - * with, or null if it is not present or not encrypted with a trusted - * key - */ - isStored(name) { - return __awaiter(this, void 0, void 0, function* () { - // check if secret exists - const secretInfo = yield this.accountDataAdapter.getAccountDataFromServer(name); - if (!(secretInfo === null || secretInfo === void 0 ? void 0 : secretInfo.encrypted)) - return null; - const ret = {}; - // filter secret encryption keys with supported algorithm - for (const keyId of Object.keys(secretInfo.encrypted)) { - // get key information from key storage - const keyInfo = yield this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.key." + keyId); - if (!keyInfo) - continue; - const encInfo = secretInfo.encrypted[keyId]; - // only use keys we understand the encryption algorithm of - if (keyInfo.algorithm === exports.SECRET_STORAGE_ALGORITHM_V1_AES) { - if (encInfo.iv && encInfo.ciphertext && encInfo.mac) { - ret[keyId] = keyInfo; - } - } - } - return Object.keys(ret).length ? ret : null; - }); - } - /** - * Request a secret from another device - * - * @param name - the name of the secret to request - * @param devices - the devices to request the secret from - */ - request(name, devices) { - const requestId = this.baseApis.makeTxnId(); - const deferred = (0, utils_1.defer)(); - this.requests.set(requestId, { name, devices, deferred }); - const cancel = (reason) => { - // send cancellation event - const cancelData = { - action: "request_cancellation", - requesting_device_id: this.baseApis.deviceId, - request_id: requestId, - }; - const toDevice = new Map(); - for (const device of devices) { - toDevice.set(device, cancelData); - } - this.baseApis.sendToDevice("m.secret.request", new Map([[this.baseApis.getUserId(), toDevice]])); - // and reject the promise so that anyone waiting on it will be - // notified - deferred.reject(new Error(reason || "Cancelled")); - }; - // send request to devices - const requestData = { - name, - action: "request", - requesting_device_id: this.baseApis.deviceId, - request_id: requestId, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - const toDevice = new Map(); - for (const device of devices) { - toDevice.set(device, requestData); - } - logger_1.logger.info(`Request secret ${name} from ${devices}, id ${requestId}`); - this.baseApis.sendToDevice("m.secret.request", new Map([[this.baseApis.getUserId(), toDevice]])); - return { - requestId, - promise: deferred.promise, - cancel, - }; - } - onRequestReceived(event) { - return __awaiter(this, void 0, void 0, function* () { - const sender = event.getSender(); - const content = event.getContent(); - if (sender !== this.baseApis.getUserId() || - !(content.name && content.action && content.requesting_device_id && content.request_id)) { - // ignore requests from anyone else, for now - return; - } - const deviceId = content.requesting_device_id; - // check if it's a cancel - if (content.action === "request_cancellation") { - /* - Looks like we intended to emit events when we got cancelations, but - we never put anything in the _incomingRequests object, and the request - itself doesn't use events anyway so if we were to wire up cancellations, - they probably ought to use the same callback interface. I'm leaving them - disabled for now while converting this file to typescript. - if (this._incomingRequests[deviceId] - && this._incomingRequests[deviceId][content.request_id]) { - logger.info( - "received request cancellation for secret (" + sender + - ", " + deviceId + ", " + content.request_id + ")", - ); - this.baseApis.emit("crypto.secrets.requestCancelled", { - user_id: sender, - device_id: deviceId, - request_id: content.request_id, - }); - } - */ - } - else if (content.action === "request") { - if (deviceId === this.baseApis.deviceId) { - // no point in trying to send ourself the secret - return; - } - // check if we have the secret - logger_1.logger.info("received request for secret (" + sender + ", " + deviceId + ", " + content.request_id + ")"); - if (!this.cryptoCallbacks.onSecretRequested) { - return; - } - const secret = yield this.cryptoCallbacks.onSecretRequested(sender, deviceId, content.request_id, content.name, this.baseApis.checkDeviceTrust(sender, deviceId)); - if (secret) { - logger_1.logger.info(`Preparing ${content.name} secret for ${deviceId}`); - const payload = { - type: "m.secret.send", - content: { - request_id: content.request_id, - secret: secret, - }, - }; - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.baseApis.crypto.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - yield olmlib.ensureOlmSessionsForDevices(this.baseApis.crypto.olmDevice, this.baseApis, new Map([[sender, [this.baseApis.getStoredDevice(sender, deviceId)]]])); - yield olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.baseApis.getUserId(), this.baseApis.deviceId, this.baseApis.crypto.olmDevice, sender, this.baseApis.getStoredDevice(sender, deviceId), payload); - const contentMap = new Map([[sender, new Map([[deviceId, encryptedContent]])]]); - logger_1.logger.info(`Sending ${content.name} secret for ${deviceId}`); - this.baseApis.sendToDevice("m.room.encrypted", contentMap); - } - else { - logger_1.logger.info(`Request denied for ${content.name} secret for ${deviceId}`); - } - } - }); - } - onSecretReceived(event) { - if (event.getSender() !== this.baseApis.getUserId()) { - // we shouldn't be receiving secrets from anyone else, so ignore - // because someone could be trying to send us bogus data - return; - } - if (!olmlib.isOlmEncrypted(event)) { - logger_1.logger.error("secret event not properly encrypted"); - return; - } - const content = event.getContent(); - const senderKeyUser = this.baseApis.crypto.deviceList.getUserByIdentityKey(olmlib.OLM_ALGORITHM, event.getSenderKey() || ""); - if (senderKeyUser !== event.getSender()) { - logger_1.logger.error("sending device does not belong to the user it claims to be from"); - return; - } - logger_1.logger.log("got secret share for request", content.request_id); - const requestControl = this.requests.get(content.request_id); - if (requestControl) { - // make sure that the device that sent it is one of the devices that - // we requested from - const deviceInfo = this.baseApis.crypto.deviceList.getDeviceByIdentityKey(olmlib.OLM_ALGORITHM, event.getSenderKey()); - if (!deviceInfo) { - logger_1.logger.log("secret share from unknown device with key", event.getSenderKey()); - return; - } - if (!requestControl.devices.includes(deviceInfo.deviceId)) { - logger_1.logger.log("unsolicited secret share from device", deviceInfo.deviceId); - return; - } - // unsure that the sender is trusted. In theory, this check is - // unnecessary since we only accept secret shares from devices that - // we requested from, but it doesn't hurt. - const deviceTrust = this.baseApis.crypto.checkDeviceInfoTrust(event.getSender(), deviceInfo); - if (!deviceTrust.isVerified()) { - logger_1.logger.log("secret share from unverified device"); - return; - } - logger_1.logger.log(`Successfully received secret ${requestControl.name} ` + `from ${deviceInfo.deviceId}`); - requestControl.deferred.resolve(content.secret); - } - } - getSecretStorageKey(keys, name) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.cryptoCallbacks.getSecretStorageKey) { - throw new Error("No getSecretStorageKey callback supplied"); - } - const returned = yield this.cryptoCallbacks.getSecretStorageKey({ keys }, name); - if (!returned) { - throw new Error("getSecretStorageKey callback returned falsey"); - } - if (returned.length < 2) { - throw new Error("getSecretStorageKey callback returned invalid data"); - } - const [keyId, privateKey] = returned; - if (!keys[keyId]) { - throw new Error("App returned unknown key from getSecretStorageKey!"); - } - if (keys[keyId].algorithm === exports.SECRET_STORAGE_ALGORITHM_V1_AES) { - const decryption = { - encrypt: function (secret) { - return (0, aes_1.encryptAES)(secret, privateKey, name); - }, - decrypt: function (encInfo) { - return (0, aes_1.decryptAES)(encInfo, privateKey, name); - }, - }; - return [keyId, decryption]; - } - else { - throw new Error("Unknown key type: " + keys[keyId].algorithm); - } - }); - } -} -exports.SecretStorage = SecretStorage; - -},{"../@types/event":306,"../client":321,"../logger":374,"../randomstring":398,"../utils":416,"./aes":331,"./olmlib":343,"uuid":287}],331:[function(require,module,exports){ -"use strict"; -/* -Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.calculateKeyCheck = exports.decryptAES = exports.encryptAES = void 0; -const olmlib_1 = require("./olmlib"); -const crypto_1 = require("./crypto"); -// salt for HKDF, with 8 bytes of zeros -const zeroSalt = new Uint8Array(8); -/** - * encrypt a string - * - * @param data - the plaintext to encrypt - * @param key - the encryption key to use - * @param name - the name of the secret - * @param ivStr - the initialization vector to use - */ -function encryptAES(data, key, name, ivStr) { - return __awaiter(this, void 0, void 0, function* () { - let iv; - if (ivStr) { - iv = (0, olmlib_1.decodeBase64)(ivStr); - } - else { - iv = new Uint8Array(16); - crypto_1.crypto.getRandomValues(iv); - // clear bit 63 of the IV to stop us hitting the 64-bit counter boundary - // (which would mean we wouldn't be able to decrypt on Android). The loss - // of a single bit of iv is a price we have to pay. - iv[8] &= 0x7f; - } - const [aesKey, hmacKey] = yield deriveKeys(key, name); - const encodedData = new crypto_1.TextEncoder().encode(data); - const ciphertext = yield crypto_1.subtleCrypto.encrypt({ - name: "AES-CTR", - counter: iv, - length: 64, - }, aesKey, encodedData); - const hmac = yield crypto_1.subtleCrypto.sign({ name: "HMAC" }, hmacKey, ciphertext); - return { - iv: (0, olmlib_1.encodeBase64)(iv), - ciphertext: (0, olmlib_1.encodeBase64)(ciphertext), - mac: (0, olmlib_1.encodeBase64)(hmac), - }; - }); -} -exports.encryptAES = encryptAES; -/** - * decrypt a string - * - * @param data - the encrypted data - * @param key - the encryption key to use - * @param name - the name of the secret - */ -function decryptAES(data, key, name) { - return __awaiter(this, void 0, void 0, function* () { - const [aesKey, hmacKey] = yield deriveKeys(key, name); - const ciphertext = (0, olmlib_1.decodeBase64)(data.ciphertext); - if (!(yield crypto_1.subtleCrypto.verify({ name: "HMAC" }, hmacKey, (0, olmlib_1.decodeBase64)(data.mac), ciphertext))) { - throw new Error(`Error decrypting secret ${name}: bad MAC`); - } - const plaintext = yield crypto_1.subtleCrypto.decrypt({ - name: "AES-CTR", - counter: (0, olmlib_1.decodeBase64)(data.iv), - length: 64, - }, aesKey, ciphertext); - return new TextDecoder().decode(new Uint8Array(plaintext)); - }); -} -exports.decryptAES = decryptAES; -function deriveKeys(key, name) { - return __awaiter(this, void 0, void 0, function* () { - const hkdfkey = yield crypto_1.subtleCrypto.importKey("raw", key, { name: "HKDF" }, false, ["deriveBits"]); - const keybits = yield crypto_1.subtleCrypto.deriveBits({ - name: "HKDF", - salt: zeroSalt, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/879 - info: new crypto_1.TextEncoder().encode(name), - hash: "SHA-256", - }, hkdfkey, 512); - const aesKey = keybits.slice(0, 32); - const hmacKey = keybits.slice(32); - const aesProm = crypto_1.subtleCrypto.importKey("raw", aesKey, { name: "AES-CTR" }, false, ["encrypt", "decrypt"]); - const hmacProm = crypto_1.subtleCrypto.importKey("raw", hmacKey, { - name: "HMAC", - hash: { name: "SHA-256" }, - }, false, ["sign", "verify"]); - return Promise.all([aesProm, hmacProm]); - }); -} -// string of zeroes, for calculating the key check -const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; -/** Calculate the MAC for checking the key. - * - * @param key - the key to use - * @param iv - The initialization vector as a base64-encoded string. - * If omitted, a random initialization vector will be created. - * @returns An object that contains, `mac` and `iv` properties. - */ -function calculateKeyCheck(key, iv) { - return encryptAES(ZERO_STR, key, "", iv); -} -exports.calculateKeyCheck = calculateKeyCheck; - -},{"./crypto":338,"./olmlib":343}],332:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerAlgorithm = exports.UnknownDeviceError = exports.DecryptionError = exports.DecryptionAlgorithm = exports.EncryptionAlgorithm = exports.DECRYPTION_CLASSES = exports.ENCRYPTION_CLASSES = void 0; -/** - * Map of registered encryption algorithm classes. A map from string to {@link EncryptionAlgorithm} class - */ -exports.ENCRYPTION_CLASSES = new Map(); -/** - * map of registered encryption algorithm classes. Map from string to {@link DecryptionAlgorithm} class - */ -exports.DECRYPTION_CLASSES = new Map(); -/** - * base type for encryption implementations - */ -class EncryptionAlgorithm { - /** - * @param params - parameters - */ - constructor(params) { - this.userId = params.userId; - this.deviceId = params.deviceId; - this.crypto = params.crypto; - this.olmDevice = params.olmDevice; - this.baseApis = params.baseApis; - this.roomId = params.roomId; - } - /** - * Perform any background tasks that can be done before a message is ready to - * send, in order to speed up sending of the message. - * - * @param room - the room the event is in - */ - prepareToEncrypt(room) { } - /** - * Called when the membership of a member of the room changes. - * - * @param event - event causing the change - * @param member - user whose membership changed - * @param oldMembership - previous membership - * @public - */ - onRoomMembership(event, member, oldMembership) { } -} -exports.EncryptionAlgorithm = EncryptionAlgorithm; -/** - * base type for decryption implementations - */ -class DecryptionAlgorithm { - constructor(params) { - this.userId = params.userId; - this.crypto = params.crypto; - this.olmDevice = params.olmDevice; - this.baseApis = params.baseApis; - this.roomId = params.roomId; - } - /** - * Handle a key event - * - * @param params - event key event - */ - onRoomKeyEvent(params) { - return __awaiter(this, void 0, void 0, function* () { - // ignore by default - }); - } - /** - * Import a room key - * - * @param opts - object - */ - importRoomKey(session, opts) { - return __awaiter(this, void 0, void 0, function* () { - // ignore by default - }); - } - /** - * Determine if we have the keys necessary to respond to a room key request - * - * @returns true if we have the keys and could (theoretically) share - * them; else false. - */ - hasKeysForKeyRequest(keyRequest) { - return Promise.resolve(false); - } - /** - * Send the response to a room key request - * - */ - shareKeysWithDevice(keyRequest) { - throw new Error("shareKeysWithDevice not supported for this DecryptionAlgorithm"); - } - /** - * Retry decrypting all the events from a sender that haven't been - * decrypted yet. - * - * @param senderKey - the sender's key - */ - retryDecryptionFromSender(senderKey) { - return __awaiter(this, void 0, void 0, function* () { - // ignore by default - return false; - }); - } -} -exports.DecryptionAlgorithm = DecryptionAlgorithm; -/** - * Exception thrown when decryption fails - * - * @param msg - user-visible message describing the problem - * - * @param details - key/value pairs reported in the logs but not shown - * to the user. - */ -class DecryptionError extends Error { - constructor(code, msg, details) { - super(msg); - this.code = code; - this.code = code; - this.name = "DecryptionError"; - this.detailedString = detailedStringForDecryptionError(this, details); - } -} -exports.DecryptionError = DecryptionError; -function detailedStringForDecryptionError(err, details) { - let result = err.name + "[msg: " + err.message; - if (details) { - result += - ", " + - Object.keys(details) - .map((k) => k + ": " + details[k]) - .join(", "); - } - result += "]"; - return result; -} -class UnknownDeviceError extends Error { - /** - * Exception thrown specifically when we want to warn the user to consider - * the security of their conversation before continuing - * - * @param msg - message describing the problem - * @param devices - set of unknown devices per user we're warning about - */ - constructor(msg, devices, event) { - super(msg); - this.devices = devices; - this.event = event; - this.name = "UnknownDeviceError"; - this.devices = devices; - } -} -exports.UnknownDeviceError = UnknownDeviceError; -/** - * Registers an encryption/decryption class for a particular algorithm - * - * @param algorithm - algorithm tag to register for - * - * @param encryptor - {@link EncryptionAlgorithm} implementation - * - * @param decryptor - {@link DecryptionAlgorithm} implementation - */ -function registerAlgorithm(algorithm, encryptor, decryptor) { - exports.ENCRYPTION_CLASSES.set(algorithm, encryptor); - exports.DECRYPTION_CLASSES.set(algorithm, decryptor); -} -exports.registerAlgorithm = registerAlgorithm; - -},{}],333:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -require("./olm"); -require("./megolm"); -__exportStar(require("./base"), exports); - -},{"./base":332,"./megolm":334,"./olm":335}],334:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021, 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MegolmDecryption = exports.MegolmEncryption = exports.isRoomSharedHistory = void 0; -/** - * Defines m.olm encryption/decryption - */ -const uuid_1 = require("uuid"); -const logger_1 = require("../../logger"); -const olmlib = __importStar(require("../olmlib")); -const base_1 = require("./base"); -const OlmDevice_1 = require("../OlmDevice"); -const event_1 = require("../../@types/event"); -const OutgoingRoomKeyRequestManager_1 = require("../OutgoingRoomKeyRequestManager"); -const utils_1 = require("../../utils"); -// determine whether the key can be shared with invitees -function isRoomSharedHistory(room) { - var _a, _b; - const visibilityEvent = (_a = room === null || room === void 0 ? void 0 : room.currentState) === null || _a === void 0 ? void 0 : _a.getStateEvents("m.room.history_visibility", ""); - // NOTE: if the room visibility is unset, it would normally default to - // "world_readable". - // (https://spec.matrix.org/unstable/client-server-api/#server-behaviour-5) - // But we will be paranoid here, and treat it as a situation where the room - // is not shared-history - const visibility = (_b = visibilityEvent === null || visibilityEvent === void 0 ? void 0 : visibilityEvent.getContent()) === null || _b === void 0 ? void 0 : _b.history_visibility; - return ["world_readable", "shared"].includes(visibility); -} -exports.isRoomSharedHistory = isRoomSharedHistory; -/** - * Tests whether an encrypted content has a ciphertext. - * Ciphertext can be a string or object depending on the content type {@link IEncryptedContent}. - * - * @param content - Encrypted content - * @returns true: has ciphertext, else false - */ -const hasCiphertext = (content) => { - return typeof content.ciphertext === "string" - ? !!content.ciphertext.length - : !!Object.keys(content.ciphertext).length; -}; -/** - * @internal - */ -class OutboundSessionInfo { - /** - * @param sharedHistory - whether the session can be freely shared with - * other group members, according to the room history visibility settings - */ - constructor(sessionId, sharedHistory = false) { - this.sessionId = sessionId; - this.sharedHistory = sharedHistory; - /** number of times this session has been used */ - this.useCount = 0; - /** devices with which we have shared the session key `userId -> {deviceId -> SharedWithData}` */ - this.sharedWithDevices = new utils_1.MapWithDefault(() => new Map()); - this.blockedDevicesNotified = new utils_1.MapWithDefault(() => new Map()); - this.creationTime = new Date().getTime(); - } - /** - * Check if it's time to rotate the session - */ - needsRotation(rotationPeriodMsgs, rotationPeriodMs) { - const sessionLifetime = new Date().getTime() - this.creationTime; - if (this.useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) { - logger_1.logger.log("Rotating megolm session after " + this.useCount + " messages, " + sessionLifetime + "ms"); - return true; - } - return false; - } - markSharedWithDevice(userId, deviceId, deviceKey, chainIndex) { - this.sharedWithDevices.getOrCreate(userId).set(deviceId, { deviceKey, messageIndex: chainIndex }); - } - markNotifiedBlockedDevice(userId, deviceId) { - this.blockedDevicesNotified.getOrCreate(userId).set(deviceId, true); - } - /** - * Determine if this session has been shared with devices which it shouldn't - * have been. - * - * @param devicesInRoom - `userId -> {deviceId -> object}` - * devices we should shared the session with. - * - * @returns true if we have shared the session with devices which aren't - * in devicesInRoom. - */ - sharedWithTooManyDevices(devicesInRoom) { - var _a; - for (const [userId, devices] of this.sharedWithDevices) { - if (!devicesInRoom.has(userId)) { - logger_1.logger.log("Starting new megolm session because we shared with " + userId); - return true; - } - for (const [deviceId] of devices) { - if (!((_a = devicesInRoom.get(userId)) === null || _a === void 0 ? void 0 : _a.get(deviceId))) { - logger_1.logger.log("Starting new megolm session because we shared with " + userId + ":" + deviceId); - return true; - } - } - } - return false; - } -} -/** - * Megolm encryption implementation - * - * @param params - parameters, as per {@link EncryptionAlgorithm} - */ -class MegolmEncryption extends base_1.EncryptionAlgorithm { - constructor(params) { - var _a, _b, _c, _d; - super(params); - // the most recent attempt to set up a session. This is used to serialise - // the session setups, so that we have a race-free view of which session we - // are using, and which devices we have shared the keys with. It resolves - // with an OutboundSessionInfo (or undefined, for the first message in the - // room). - this.setupPromise = Promise.resolve(null); - // Map of outbound sessions by sessions ID. Used if we need a particular - // session (the session we're currently using to send is always obtained - // using setupPromise). - this.outboundSessions = {}; - this.roomId = params.roomId; - this.prefixedLogger = logger_1.logger.withPrefix(`[${this.roomId} encryption]`); - this.sessionRotationPeriodMsgs = (_b = (_a = params.config) === null || _a === void 0 ? void 0 : _a.rotation_period_msgs) !== null && _b !== void 0 ? _b : 100; - this.sessionRotationPeriodMs = (_d = (_c = params.config) === null || _c === void 0 ? void 0 : _c.rotation_period_ms) !== null && _d !== void 0 ? _d : 7 * 24 * 3600 * 1000; - } - /** - * @internal - * - * @param devicesInRoom - The devices in this room, indexed by user ID - * @param blocked - The devices that are blocked, indexed by user ID - * @param singleOlmCreationPhase - Only perform one round of olm - * session creation - * - * This method updates the setupPromise field of the class by chaining a new - * call on top of the existing promise, and then catching and discarding any - * errors that might happen while setting up the outbound group session. This - * is done to ensure that `setupPromise` always resolves to `null` or the - * `OutboundSessionInfo`. - * - * Using `>>=` to represent the promise chaining operation, it does the - * following: - * - * ``` - * setupPromise = previousSetupPromise >>= setup >>= discardErrors - * ``` - * - * The initial value for the `setupPromise` is a promise that resolves to - * `null`. The forceDiscardSession() resets setupPromise to this initial - * promise. - * - * @returns Promise which resolves to the - * OutboundSessionInfo when setup is complete. - */ - ensureOutboundSession(room, devicesInRoom, blocked, singleOlmCreationPhase = false) { - return __awaiter(this, void 0, void 0, function* () { - // takes the previous OutboundSessionInfo, and considers whether to create - // a new one. Also shares the key with any (new) devices in the room. - // - // returns a promise which resolves once the keyshare is successful. - const setup = (oldSession) => __awaiter(this, void 0, void 0, function* () { - const sharedHistory = isRoomSharedHistory(room); - const session = yield this.prepareSession(devicesInRoom, sharedHistory, oldSession); - yield this.shareSession(devicesInRoom, sharedHistory, singleOlmCreationPhase, blocked, session); - return session; - }); - // first wait for the previous share to complete - const fallible = this.setupPromise.then(setup); - // Ensure any failures are logged for debugging and make sure that the - // promise chain remains unbroken - // - // setupPromise resolves to `null` or the `OutboundSessionInfo` whether - // or not the share succeeds - this.setupPromise = fallible.catch((e) => { - this.prefixedLogger.error(`Failed to setup outbound session`, e); - return null; - }); - // but we return a promise which only resolves if the share was successful. - return fallible; - }); - } - prepareSession(devicesInRoom, sharedHistory, session) { - return __awaiter(this, void 0, void 0, function* () { - // history visibility changed - if (session && sharedHistory !== session.sharedHistory) { - session = null; - } - // need to make a brand new session? - if (session === null || session === void 0 ? void 0 : session.needsRotation(this.sessionRotationPeriodMsgs, this.sessionRotationPeriodMs)) { - this.prefixedLogger.log("Starting new megolm session because we need to rotate."); - session = null; - } - // determine if we have shared with anyone we shouldn't have - if (session === null || session === void 0 ? void 0 : session.sharedWithTooManyDevices(devicesInRoom)) { - session = null; - } - if (!session) { - this.prefixedLogger.log("Starting new megolm session"); - session = yield this.prepareNewSession(sharedHistory); - this.prefixedLogger.log(`Started new megolm session ${session.sessionId}`); - this.outboundSessions[session.sessionId] = session; - } - return session; - }); - } - shareSession(devicesInRoom, sharedHistory, singleOlmCreationPhase, blocked, session) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // now check if we need to share with any devices - const shareMap = {}; - for (const [userId, userDevices] of devicesInRoom) { - for (const [deviceId, deviceInfo] of userDevices) { - const key = deviceInfo.getIdentityKey(); - if (key == this.olmDevice.deviceCurve25519Key) { - // don't bother sending to ourself - continue; - } - if (!((_a = session.sharedWithDevices.get(userId)) === null || _a === void 0 ? void 0 : _a.get(deviceId))) { - shareMap[userId] = shareMap[userId] || []; - shareMap[userId].push(deviceInfo); - } - } - } - const key = this.olmDevice.getOutboundGroupSessionKey(session.sessionId); - const payload = { - type: "m.room_key", - content: { - "algorithm": olmlib.MEGOLM_ALGORITHM, - "room_id": this.roomId, - "session_id": session.sessionId, - "session_key": key.key, - "chain_index": key.chain_index, - "org.matrix.msc3061.shared_history": sharedHistory, - }, - }; - const [devicesWithoutSession, olmSessions] = yield olmlib.getExistingOlmSessions(this.olmDevice, this.baseApis, shareMap); - yield Promise.all([ - (() => __awaiter(this, void 0, void 0, function* () { - // share keys with devices that we already have a session for - const olmSessionList = Array.from(olmSessions.entries()) - .map(([userId, sessionsByUser]) => Array.from(sessionsByUser.entries()).map(([deviceId, session]) => `${userId}/${deviceId}: ${session.sessionId}`)) - .flat(1); - this.prefixedLogger.debug("Sharing keys with devices with existing Olm sessions:", olmSessionList); - yield this.shareKeyWithOlmSessions(session, key, payload, olmSessions); - this.prefixedLogger.debug("Shared keys with existing Olm sessions"); - }))(), - (() => __awaiter(this, void 0, void 0, function* () { - const deviceList = Array.from(devicesWithoutSession.entries()) - .map(([userId, devicesByUser]) => devicesByUser.map((device) => `${userId}/${device.deviceId}`)) - .flat(1); - this.prefixedLogger.debug("Sharing keys (start phase 1) with devices without existing Olm sessions:", deviceList); - const errorDevices = []; - // meanwhile, establish olm sessions for devices that we don't - // already have a session for, and share keys with them. If - // we're doing two phases of olm session creation, use a - // shorter timeout when fetching one-time keys for the first - // phase. - const start = Date.now(); - const failedServers = []; - yield this.shareKeyWithDevices(session, key, payload, devicesWithoutSession, errorDevices, singleOlmCreationPhase ? 10000 : 2000, failedServers); - this.prefixedLogger.debug("Shared keys (end phase 1) with devices without existing Olm sessions"); - if (!singleOlmCreationPhase && Date.now() - start < 10000) { - // perform the second phase of olm session creation if requested, - // and if the first phase didn't take too long - (() => __awaiter(this, void 0, void 0, function* () { - // Retry sending keys to devices that we were unable to establish - // an olm session for. This time, we use a longer timeout, but we - // do this in the background and don't block anything else while we - // do this. We only need to retry users from servers that didn't - // respond the first time. - const retryDevices = new utils_1.MapWithDefault(() => []); - const failedServerMap = new Set(); - for (const server of failedServers) { - failedServerMap.add(server); - } - const failedDevices = []; - for (const { userId, deviceInfo } of errorDevices) { - const userHS = userId.slice(userId.indexOf(":") + 1); - if (failedServerMap.has(userHS)) { - retryDevices.getOrCreate(userId).push(deviceInfo); - } - else { - // if we aren't going to retry, then handle it - // as a failed device - failedDevices.push({ userId, deviceInfo }); - } - } - const retryDeviceList = Array.from(retryDevices.entries()) - .map(([userId, devicesByUser]) => devicesByUser.map((device) => `${userId}/${device.deviceId}`)) - .flat(1); - if (retryDeviceList.length > 0) { - this.prefixedLogger.debug("Sharing keys (start phase 2) with devices without existing Olm sessions:", retryDeviceList); - yield this.shareKeyWithDevices(session, key, payload, retryDevices, failedDevices, 30000); - this.prefixedLogger.debug("Shared keys (end phase 2) with devices without existing Olm sessions"); - } - yield this.notifyFailedOlmDevices(session, key, failedDevices); - }))(); - } - else { - yield this.notifyFailedOlmDevices(session, key, errorDevices); - } - }))(), - (() => __awaiter(this, void 0, void 0, function* () { - var _b; - this.prefixedLogger.debug(`There are ${blocked.size} blocked devices:`, Array.from(blocked.entries()) - .map(([userId, blockedByUser]) => Array.from(blockedByUser.entries()).map(([deviceId, _deviceInfo]) => `${userId}/${deviceId}`)) - .flat(1)); - // also, notify newly blocked devices that they're blocked - const blockedMap = new utils_1.MapWithDefault(() => new Map()); - let blockedCount = 0; - for (const [userId, userBlockedDevices] of blocked) { - for (const [deviceId, device] of userBlockedDevices) { - if (((_b = session.blockedDevicesNotified.get(userId)) === null || _b === void 0 ? void 0 : _b.get(deviceId)) === undefined) { - blockedMap.getOrCreate(userId).set(deviceId, { device }); - blockedCount++; - } - } - } - if (blockedCount) { - this.prefixedLogger.debug(`Notifying ${blockedCount} newly blocked devices:`, Array.from(blockedMap.entries()) - .map(([userId, blockedByUser]) => Object.entries(blockedByUser).map(([deviceId, _deviceInfo]) => `${userId}/${deviceId}`)) - .flat(1)); - yield this.notifyBlockedDevices(session, blockedMap); - this.prefixedLogger.debug(`Notified ${blockedCount} newly blocked devices`); - } - }))(), - ]); - }); - } - /** - * @internal - * - * - * @returns session - */ - prepareNewSession(sharedHistory) { - return __awaiter(this, void 0, void 0, function* () { - const sessionId = this.olmDevice.createOutboundGroupSession(); - const key = this.olmDevice.getOutboundGroupSessionKey(sessionId); - yield this.olmDevice.addInboundGroupSession(this.roomId, this.olmDevice.deviceCurve25519Key, [], sessionId, key.key, { ed25519: this.olmDevice.deviceEd25519Key }, false, { sharedHistory }); - // don't wait for it to complete - this.crypto.backupManager.backupGroupSession(this.olmDevice.deviceCurve25519Key, sessionId); - return new OutboundSessionInfo(sessionId, sharedHistory); - }); - } - /** - * Determines what devices in devicesByUser don't have an olm session as given - * in devicemap. - * - * @internal - * - * @param deviceMap - the devices that have olm sessions, as returned by - * olmlib.ensureOlmSessionsForDevices. - * @param devicesByUser - a map of user IDs to array of deviceInfo - * @param noOlmDevices - an array to fill with devices that don't have - * olm sessions - * - * @returns an array of devices that don't have olm sessions. If - * noOlmDevices is specified, then noOlmDevices will be returned. - */ - getDevicesWithoutSessions(deviceMap, devicesByUser, noOlmDevices = []) { - for (const [userId, devicesToShareWith] of devicesByUser) { - const sessionResults = deviceMap.get(userId); - for (const deviceInfo of devicesToShareWith) { - const deviceId = deviceInfo.deviceId; - const sessionResult = sessionResults === null || sessionResults === void 0 ? void 0 : sessionResults.get(deviceId); - if (!(sessionResult === null || sessionResult === void 0 ? void 0 : sessionResult.sessionId)) { - // no session with this device, probably because there - // were no one-time keys. - noOlmDevices.push({ userId, deviceInfo }); - sessionResults === null || sessionResults === void 0 ? void 0 : sessionResults.delete(deviceId); - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - continue; - } - } - } - return noOlmDevices; - } - /** - * Splits the user device map into multiple chunks to reduce the number of - * devices we encrypt to per API call. - * - * @internal - * - * @param devicesByUser - map from userid to list of devices - * - * @returns the blocked devices, split into chunks - */ - splitDevices(devicesByUser) { - const maxDevicesPerRequest = 20; - // use an array where the slices of a content map gets stored - let currentSlice = []; - const mapSlices = [currentSlice]; - for (const [userId, userDevices] of devicesByUser) { - for (const deviceInfo of userDevices.values()) { - currentSlice.push({ - userId: userId, - deviceInfo: deviceInfo.device, - }); - } - // We do this in the per-user loop as we prefer that all messages to the - // same user end up in the same API call to make it easier for the - // server (e.g. only have to send one EDU if a remote user, etc). This - // does mean that if a user has many devices we may go over the desired - // limit, but its not a hard limit so that is fine. - if (currentSlice.length > maxDevicesPerRequest) { - // the current slice is filled up. Start inserting into the next slice - currentSlice = []; - mapSlices.push(currentSlice); - } - } - if (currentSlice.length === 0) { - mapSlices.pop(); - } - return mapSlices; - } - /** - * @internal - * - * - * @param chainIndex - current chain index - * - * @param userDeviceMap - mapping from userId to deviceInfo - * - * @param payload - fields to include in the encrypted payload - * - * @returns Promise which resolves once the key sharing - * for the given userDeviceMap is generated and has been sent. - */ - encryptAndSendKeysToDevices(session, chainIndex, devices, payload) { - return this.crypto - .encryptAndSendToDevices(devices, payload) - .then(() => { - // store that we successfully uploaded the keys of the current slice - for (const device of devices) { - session.markSharedWithDevice(device.userId, device.deviceInfo.deviceId, device.deviceInfo.getIdentityKey(), chainIndex); - } - }) - .catch((error) => { - this.prefixedLogger.error("failed to encryptAndSendToDevices", error); - throw error; - }); - } - /** - * @internal - * - * - * @param userDeviceMap - list of blocked devices to notify - * - * @param payload - fields to include in the notification payload - * - * @returns Promise which resolves once the notifications - * for the given userDeviceMap is generated and has been sent. - */ - sendBlockedNotificationsToDevices(session, userDeviceMap, payload) { - return __awaiter(this, void 0, void 0, function* () { - const contentMap = new utils_1.MapWithDefault(() => new Map()); - for (const val of userDeviceMap) { - const userId = val.userId; - const blockedInfo = val.deviceInfo; - const deviceInfo = blockedInfo.deviceInfo; - const deviceId = deviceInfo.deviceId; - const message = Object.assign(Object.assign({}, payload), { code: blockedInfo.code, reason: blockedInfo.reason, [event_1.ToDeviceMessageId]: (0, uuid_1.v4)() }); - if (message.code === "m.no_olm") { - delete message.room_id; - delete message.session_id; - } - contentMap.getOrCreate(userId).set(deviceId, message); - } - yield this.baseApis.sendToDevice("m.room_key.withheld", contentMap); - // record the fact that we notified these blocked devices - for (const [userId, userDeviceMap] of contentMap) { - for (const deviceId of userDeviceMap.keys()) { - session.markNotifiedBlockedDevice(userId, deviceId); - } - } - }); - } - /** - * Re-shares a megolm session key with devices if the key has already been - * sent to them. - * - * @param senderKey - The key of the originating device for the session - * @param sessionId - ID of the outbound session to share - * @param userId - ID of the user who owns the target device - * @param device - The target device - */ - reshareKeyWithDevice(senderKey, sessionId, userId, device) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const obSessionInfo = this.outboundSessions[sessionId]; - if (!obSessionInfo) { - this.prefixedLogger.debug(`megolm session ${senderKey}|${sessionId} not found: not re-sharing keys`); - return; - } - // The chain index of the key we previously sent this device - if (!obSessionInfo.sharedWithDevices.has(userId)) { - this.prefixedLogger.debug(`megolm session ${senderKey}|${sessionId} never shared with user ${userId}`); - return; - } - const sessionSharedData = (_a = obSessionInfo.sharedWithDevices.get(userId)) === null || _a === void 0 ? void 0 : _a.get(device.deviceId); - if (sessionSharedData === undefined) { - this.prefixedLogger.debug(`megolm session ${senderKey}|${sessionId} never shared with device ${userId}:${device.deviceId}`); - return; - } - if (sessionSharedData.deviceKey !== device.getIdentityKey()) { - this.prefixedLogger.warn(`Megolm session ${senderKey}|${sessionId} has been shared with device ${device.deviceId} but ` + - `with identity key ${sessionSharedData.deviceKey}. Key is now ${device.getIdentityKey()}!`); - return; - } - // get the key from the inbound session: the outbound one will already - // have been ratcheted to the next chain index. - const key = yield this.olmDevice.getInboundGroupSessionKey(this.roomId, senderKey, sessionId, sessionSharedData.messageIndex); - if (!key) { - this.prefixedLogger.warn(`No inbound session key found for megolm session ${senderKey}|${sessionId}: not re-sharing keys`); - return; - } - yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, new Map([[userId, [device]]])); - const payload = { - type: "m.forwarded_room_key", - content: { - "algorithm": olmlib.MEGOLM_ALGORITHM, - "room_id": this.roomId, - "session_id": sessionId, - "session_key": key.key, - "chain_index": key.chain_index, - "sender_key": senderKey, - "sender_claimed_ed25519_key": key.sender_claimed_ed25519_key, - "forwarding_curve25519_key_chain": key.forwarding_curve25519_key_chain, - "org.matrix.msc3061.shared_history": key.shared_history || false, - }, - }; - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - yield olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.userId, this.deviceId, this.olmDevice, userId, device, payload); - yield this.baseApis.sendToDevice("m.room.encrypted", new Map([[userId, new Map([[device.deviceId, encryptedContent]])]])); - this.prefixedLogger.debug(`Re-shared key for megolm session ${senderKey}|${sessionId} with ${userId}:${device.deviceId}`); - }); - } - /** - * @internal - * - * - * @param key - the session key as returned by - * OlmDevice.getOutboundGroupSessionKey - * - * @param payload - the base to-device message payload for sharing keys - * - * @param devicesByUser - map from userid to list of devices - * - * @param errorDevices - array that will be populated with the devices that we can't get an - * olm session for - * - * @param otkTimeout - The timeout in milliseconds when requesting - * one-time keys for establishing new olm sessions. - * - * @param failedServers - An array to fill with remote servers that - * failed to respond to one-time-key requests. - */ - shareKeyWithDevices(session, key, payload, devicesByUser, errorDevices, otkTimeout, failedServers) { - return __awaiter(this, void 0, void 0, function* () { - const devicemap = yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, false, otkTimeout, failedServers, this.prefixedLogger); - this.getDevicesWithoutSessions(devicemap, devicesByUser, errorDevices); - yield this.shareKeyWithOlmSessions(session, key, payload, devicemap); - }); - } - shareKeyWithOlmSessions(session, key, payload, deviceMap) { - return __awaiter(this, void 0, void 0, function* () { - const userDeviceMaps = this.splitDevices(deviceMap); - for (let i = 0; i < userDeviceMaps.length; i++) { - const taskDetail = `megolm keys for ${session.sessionId} (slice ${i + 1}/${userDeviceMaps.length})`; - try { - this.prefixedLogger.debug(`Sharing ${taskDetail}`, userDeviceMaps[i].map((d) => `${d.userId}/${d.deviceInfo.deviceId}`)); - yield this.encryptAndSendKeysToDevices(session, key.chain_index, userDeviceMaps[i], payload); - this.prefixedLogger.debug(`Shared ${taskDetail}`); - } - catch (e) { - this.prefixedLogger.error(`Failed to share ${taskDetail}`); - throw e; - } - } - }); - } - /** - * Notify devices that we weren't able to create olm sessions. - * - * - * - * @param failedDevices - the devices that we were unable to - * create olm sessions for, as returned by shareKeyWithDevices - */ - notifyFailedOlmDevices(session, key, failedDevices) { - return __awaiter(this, void 0, void 0, function* () { - this.prefixedLogger.debug(`Notifying ${failedDevices.length} devices we failed to create Olm sessions`); - // mark the devices that failed as "handled" because we don't want to try - // to claim a one-time-key for dead devices on every message. - for (const { userId, deviceInfo } of failedDevices) { - const deviceId = deviceInfo.deviceId; - session.markSharedWithDevice(userId, deviceId, deviceInfo.getIdentityKey(), key.chain_index); - } - const unnotifiedFailedDevices = yield this.olmDevice.filterOutNotifiedErrorDevices(failedDevices); - this.prefixedLogger.debug(`Need to notify ${unnotifiedFailedDevices.length} failed devices which haven't been notified before`); - const blockedMap = new utils_1.MapWithDefault(() => new Map()); - for (const { userId, deviceInfo } of unnotifiedFailedDevices) { - // we use a similar format to what - // olmlib.ensureOlmSessionsForDevices returns, so that - // we can use the same function to split - blockedMap.getOrCreate(userId).set(deviceInfo.deviceId, { - device: { - code: "m.no_olm", - reason: OlmDevice_1.WITHHELD_MESSAGES["m.no_olm"], - deviceInfo, - }, - }); - } - // send the notifications - yield this.notifyBlockedDevices(session, blockedMap); - this.prefixedLogger.debug(`Notified ${unnotifiedFailedDevices.length} devices we failed to create Olm sessions`); - }); - } - /** - * Notify blocked devices that they have been blocked. - * - * - * @param devicesByUser - map from userid to device ID to blocked data - */ - notifyBlockedDevices(session, devicesByUser) { - return __awaiter(this, void 0, void 0, function* () { - const payload = { - room_id: this.roomId, - session_id: session.sessionId, - algorithm: olmlib.MEGOLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - }; - const userDeviceMaps = this.splitDevices(devicesByUser); - for (let i = 0; i < userDeviceMaps.length; i++) { - try { - yield this.sendBlockedNotificationsToDevices(session, userDeviceMaps[i], payload); - this.prefixedLogger.log(`Completed blacklist notification for ${session.sessionId} ` + - `(slice ${i + 1}/${userDeviceMaps.length})`); - } - catch (e) { - this.prefixedLogger.log(`blacklist notification for ${session.sessionId} ` + - `(slice ${i + 1}/${userDeviceMaps.length}) failed`); - throw e; - } - } - }); - } - /** - * Perform any background tasks that can be done before a message is ready to - * send, in order to speed up sending of the message. - * - * @param room - the room the event is in - * @returns A function that, when called, will stop the preparation - */ - prepareToEncrypt(room) { - if (room.roomId !== this.roomId) { - throw new Error("MegolmEncryption.prepareToEncrypt called on unexpected room"); - } - if (this.encryptionPreparation != null) { - // We're already preparing something, so don't do anything else. - const elapsedTime = Date.now() - this.encryptionPreparation.startTime; - this.prefixedLogger.debug(`Already started preparing to encrypt for this room ${elapsedTime}ms ago, skipping`); - return this.encryptionPreparation.cancel; - } - this.prefixedLogger.debug("Preparing to encrypt events"); - let cancelled = false; - const isCancelled = () => cancelled; - this.encryptionPreparation = { - startTime: Date.now(), - promise: (() => __awaiter(this, void 0, void 0, function* () { - try { - // Attempt to enumerate the devices in room, and gracefully - // handle cancellation if it occurs. - const getDevicesResult = yield this.getDevicesInRoom(room, false, isCancelled); - if (getDevicesResult === null) - return; - const [devicesInRoom, blocked] = getDevicesResult; - if (this.crypto.globalErrorOnUnknownDevices) { - // Drop unknown devices for now. When the message gets sent, we'll - // throw an error, but we'll still be prepared to send to the known - // devices. - this.removeUnknownDevices(devicesInRoom); - } - this.prefixedLogger.debug("Ensuring outbound megolm session"); - yield this.ensureOutboundSession(room, devicesInRoom, blocked, true); - this.prefixedLogger.debug("Ready to encrypt events"); - } - catch (e) { - this.prefixedLogger.error("Failed to prepare to encrypt events", e); - } - finally { - delete this.encryptionPreparation; - } - }))(), - cancel: () => { - // The caller has indicated that the process should be cancelled, - // so tell the promise that we'd like to halt, and reset the preparation state. - cancelled = true; - delete this.encryptionPreparation; - }, - }; - return this.encryptionPreparation.cancel; - } - /** - * @param content - plaintext event content - * - * @returns Promise which resolves to the new event body - */ - encryptMessage(room, eventType, content) { - return __awaiter(this, void 0, void 0, function* () { - this.prefixedLogger.log("Starting to encrypt event"); - if (this.encryptionPreparation != null) { - // If we started sending keys, wait for it to be done. - // FIXME: check if we need to cancel - // (https://github.com/matrix-org/matrix-js-sdk/issues/1255) - try { - yield this.encryptionPreparation.promise; - } - catch (e) { - // ignore any errors -- if the preparation failed, we'll just - // restart everything here - } - } - /** - * When using in-room messages and the room has encryption enabled, - * clients should ensure that encryption does not hinder the verification. - */ - const forceDistributeToUnverified = this.isVerificationEvent(eventType, content); - const [devicesInRoom, blocked] = yield this.getDevicesInRoom(room, forceDistributeToUnverified); - // check if any of these devices are not yet known to the user. - // if so, warn the user so they can verify or ignore. - if (this.crypto.globalErrorOnUnknownDevices) { - this.checkForUnknownDevices(devicesInRoom); - } - const session = yield this.ensureOutboundSession(room, devicesInRoom, blocked); - const payloadJson = { - room_id: this.roomId, - type: eventType, - content: content, - }; - const ciphertext = this.olmDevice.encryptGroupMessage(session.sessionId, JSON.stringify(payloadJson)); - const encryptedContent = { - algorithm: olmlib.MEGOLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: ciphertext, - session_id: session.sessionId, - // Include our device ID so that recipients can send us a - // m.new_device message if they don't have our session key. - // XXX: Do we still need this now that m.new_device messages - // no longer exist since #483? - device_id: this.deviceId, - }; - session.useCount++; - return encryptedContent; - }); - } - isVerificationEvent(eventType, content) { - switch (eventType) { - case event_1.EventType.KeyVerificationCancel: - case event_1.EventType.KeyVerificationDone: - case event_1.EventType.KeyVerificationMac: - case event_1.EventType.KeyVerificationStart: - case event_1.EventType.KeyVerificationKey: - case event_1.EventType.KeyVerificationReady: - case event_1.EventType.KeyVerificationAccept: { - return true; - } - case event_1.EventType.RoomMessage: { - return content["msgtype"] === event_1.MsgType.KeyVerificationRequest; - } - default: { - return false; - } - } - } - /** - * Forces the current outbound group session to be discarded such - * that another one will be created next time an event is sent. - * - * This should not normally be necessary. - */ - forceDiscardSession() { - this.setupPromise = this.setupPromise.then(() => null); - } - /** - * Checks the devices we're about to send to and see if any are entirely - * unknown to the user. If so, warn the user, and mark them as known to - * give the user a chance to go verify them before re-sending this message. - * - * @param devicesInRoom - `userId -> {deviceId -> object}` - * devices we should shared the session with. - */ - checkForUnknownDevices(devicesInRoom) { - const unknownDevices = new utils_1.MapWithDefault(() => new Map()); - for (const [userId, userDevices] of devicesInRoom) { - for (const [deviceId, device] of userDevices) { - if (device.isUnverified() && !device.isKnown()) { - unknownDevices.getOrCreate(userId).set(deviceId, device); - } - } - } - if (unknownDevices.size) { - // it'd be kind to pass unknownDevices up to the user in this error - throw new base_1.UnknownDeviceError("This room contains unknown devices which have not been verified. " + - "We strongly recommend you verify them before continuing.", unknownDevices); - } - } - /** - * Remove unknown devices from a set of devices. The devicesInRoom parameter - * will be modified. - * - * @param devicesInRoom - `userId -> {deviceId -> object}` - * devices we should shared the session with. - */ - removeUnknownDevices(devicesInRoom) { - for (const [userId, userDevices] of devicesInRoom) { - for (const [deviceId, device] of userDevices) { - if (device.isUnverified() && !device.isKnown()) { - userDevices.delete(deviceId); - } - } - if (userDevices.size === 0) { - devicesInRoom.delete(userId); - } - } - } - getDevicesInRoom(room, forceDistributeToUnverified = false, isCancelled) { - return __awaiter(this, void 0, void 0, function* () { - const members = yield room.getEncryptionTargetMembers(); - this.prefixedLogger.debug(`Encrypting for users (shouldEncryptForInvitedMembers: ${room.shouldEncryptForInvitedMembers()}):`, members.map((u) => `${u.userId} (${u.membership})`)); - const roomMembers = members.map(function (u) { - return u.userId; - }); - // The global value is treated as a default for when rooms don't specify a value. - let isBlacklisting = this.crypto.globalBlacklistUnverifiedDevices; - const isRoomBlacklisting = room.getBlacklistUnverifiedDevices(); - if (typeof isRoomBlacklisting === "boolean") { - isBlacklisting = isRoomBlacklisting; - } - // We are happy to use a cached version here: we assume that if we already - // have a list of the user's devices, then we already share an e2e room - // with them, which means that they will have announced any new devices via - // device_lists in their /sync response. This cache should then be maintained - // using all the device_lists changes and left fields. - // See https://github.com/vector-im/element-web/issues/2305 for details. - const devices = yield this.crypto.downloadKeys(roomMembers, false); - if ((isCancelled === null || isCancelled === void 0 ? void 0 : isCancelled()) === true) { - return null; - } - const blocked = new utils_1.MapWithDefault(() => new Map()); - // remove any blocked devices - for (const [userId, userDevices] of devices) { - for (const [deviceId, userDevice] of userDevices) { - // Yield prior to checking each device so that we don't block - // updating/rendering for too long. - // See https://github.com/vector-im/element-web/issues/21612 - if (isCancelled !== undefined) - yield (0, utils_1.immediate)(); - if ((isCancelled === null || isCancelled === void 0 ? void 0 : isCancelled()) === true) - return null; - const deviceTrust = this.crypto.checkDeviceTrust(userId, deviceId); - if (userDevice.isBlocked() || - (!deviceTrust.isVerified() && isBlacklisting && !forceDistributeToUnverified)) { - const blockedDevices = blocked.getOrCreate(userId); - const isBlocked = userDevice.isBlocked(); - blockedDevices.set(deviceId, { - code: isBlocked ? "m.blacklisted" : "m.unverified", - reason: OlmDevice_1.WITHHELD_MESSAGES[isBlocked ? "m.blacklisted" : "m.unverified"], - deviceInfo: userDevice, - }); - userDevices.delete(deviceId); - } - } - } - return [devices, blocked]; - }); - } -} -exports.MegolmEncryption = MegolmEncryption; -/** - * Megolm decryption implementation - * - * @param params - parameters, as per {@link DecryptionAlgorithm} - */ -class MegolmDecryption extends base_1.DecryptionAlgorithm { - constructor(params) { - super(params); - // events which we couldn't decrypt due to unknown sessions / - // indexes, or which we could only decrypt with untrusted keys: - // map from senderKey|sessionId to Set of MatrixEvents - this.pendingEvents = new Map(); - // this gets stubbed out by the unit tests. - this.olmlib = olmlib; - this.roomId = params.roomId; - this.prefixedLogger = logger_1.logger.withPrefix(`[${this.roomId} decryption]`); - } - /** - * returns a promise which resolves to a - * {@link EventDecryptionResult} once we have finished - * decrypting, or rejects with an `algorithms.DecryptionError` if there is a - * problem decrypting the event. - */ - decryptEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getWireContent(); - if (!content.sender_key || !content.session_id || !content.ciphertext) { - throw new base_1.DecryptionError("MEGOLM_MISSING_FIELDS", "Missing fields in input"); - } - // we add the event to the pending list *before* we start decryption. - // - // then, if the key turns up while decryption is in progress (and - // decryption fails), we will schedule a retry. - // (fixes https://github.com/vector-im/element-web/issues/5001) - this.addEventToPendingList(event); - let res; - try { - res = yield this.olmDevice.decryptGroupMessage(event.getRoomId(), content.sender_key, content.session_id, content.ciphertext, event.getId(), event.getTs()); - } - catch (e) { - if (e.name === "DecryptionError") { - // re-throw decryption errors as-is - throw e; - } - let errorCode = "OLM_DECRYPT_GROUP_MESSAGE_ERROR"; - if ((e === null || e === void 0 ? void 0 : e.message) === "OLM.UNKNOWN_MESSAGE_INDEX") { - this.requestKeysForEvent(event); - errorCode = "OLM_UNKNOWN_MESSAGE_INDEX"; - } - throw new base_1.DecryptionError(errorCode, e instanceof Error ? e.message : "Unknown Error: Error is undefined", { - session: content.sender_key + "|" + content.session_id, - }); - } - if (res === null) { - // We've got a message for a session we don't have. - // try and get the missing key from the backup first - this.crypto.backupManager.queryKeyBackupRateLimited(event.getRoomId(), content.session_id).catch(() => { }); - // (XXX: We might actually have received this key since we started - // decrypting, in which case we'll have scheduled a retry, and this - // request will be redundant. We could probably check to see if the - // event is still in the pending list; if not, a retry will have been - // scheduled, so we needn't send out the request here.) - this.requestKeysForEvent(event); - // See if there was a problem with the olm session at the time the - // event was sent. Use a fuzz factor of 2 minutes. - const problem = yield this.olmDevice.sessionMayHaveProblems(content.sender_key, event.getTs() - 120000); - if (problem) { - this.prefixedLogger.info(`When handling UISI from ${event.getSender()} (sender key ${content.sender_key}): ` + - `recent session problem with that sender:`, problem); - let problemDescription = PROBLEM_DESCRIPTIONS[problem.type] || PROBLEM_DESCRIPTIONS.unknown; - if (problem.fixed) { - problemDescription += " Trying to create a new secure channel and re-requesting the keys."; - } - throw new base_1.DecryptionError("MEGOLM_UNKNOWN_INBOUND_SESSION_ID", problemDescription, { - session: content.sender_key + "|" + content.session_id, - }); - } - throw new base_1.DecryptionError("MEGOLM_UNKNOWN_INBOUND_SESSION_ID", "The sender's device has not sent us the keys for this message.", { - session: content.sender_key + "|" + content.session_id, - }); - } - // Success. We can remove the event from the pending list, if - // that hasn't already happened. However, if the event was - // decrypted with an untrusted key, leave it on the pending - // list so it will be retried if we find a trusted key later. - if (!res.untrusted) { - this.removeEventFromPendingList(event); - } - const payload = JSON.parse(res.result); - // belt-and-braces check that the room id matches that indicated by the HS - // (this is somewhat redundant, since the megolm session is scoped to the - // room, so neither the sender nor a MITM can lie about the room_id). - if (payload.room_id !== event.getRoomId()) { - throw new base_1.DecryptionError("MEGOLM_BAD_ROOM", "Message intended for room " + payload.room_id); - } - return { - clearEvent: payload, - senderCurve25519Key: res.senderKey, - claimedEd25519Key: res.keysClaimed.ed25519, - forwardingCurve25519KeyChain: res.forwardingCurve25519KeyChain, - untrusted: res.untrusted, - }; - }); - } - requestKeysForEvent(event) { - const wireContent = event.getWireContent(); - const recipients = event.getKeyRequestRecipients(this.userId); - this.crypto.requestRoomKey({ - room_id: event.getRoomId(), - algorithm: wireContent.algorithm, - sender_key: wireContent.sender_key, - session_id: wireContent.session_id, - }, recipients); - } - /** - * Add an event to the list of those awaiting their session keys. - * - * @internal - * - */ - addEventToPendingList(event) { - var _a; - const content = event.getWireContent(); - const senderKey = content.sender_key; - const sessionId = content.session_id; - if (!this.pendingEvents.has(senderKey)) { - this.pendingEvents.set(senderKey, new Map()); - } - const senderPendingEvents = this.pendingEvents.get(senderKey); - if (!senderPendingEvents.has(sessionId)) { - senderPendingEvents.set(sessionId, new Set()); - } - (_a = senderPendingEvents.get(sessionId)) === null || _a === void 0 ? void 0 : _a.add(event); - } - /** - * Remove an event from the list of those awaiting their session keys. - * - * @internal - * - */ - removeEventFromPendingList(event) { - const content = event.getWireContent(); - const senderKey = content.sender_key; - const sessionId = content.session_id; - const senderPendingEvents = this.pendingEvents.get(senderKey); - const pendingEvents = senderPendingEvents === null || senderPendingEvents === void 0 ? void 0 : senderPendingEvents.get(sessionId); - if (!pendingEvents) { - return; - } - pendingEvents.delete(event); - if (pendingEvents.size === 0) { - senderPendingEvents.delete(sessionId); - } - if (senderPendingEvents.size === 0) { - this.pendingEvents.delete(senderKey); - } - } - /** - * Parse a RoomKey out of an `m.room_key` event. - * - * @param event - the event containing the room key. - * - * @returns The `RoomKey` if it could be successfully parsed out of the - * event. - * - * @internal - * - */ - roomKeyFromEvent(event) { - const senderKey = event.getSenderKey(); - const content = event.getContent(); - const extraSessionData = {}; - if (!content.room_id || !content.session_key || !content.session_id || !content.algorithm) { - this.prefixedLogger.error("key event is missing fields"); - return; - } - if (!olmlib.isOlmEncrypted(event)) { - this.prefixedLogger.error("key event not properly encrypted"); - return; - } - if (content["org.matrix.msc3061.shared_history"]) { - extraSessionData.sharedHistory = true; - } - const roomKey = { - senderKey: senderKey, - sessionId: content.session_id, - sessionKey: content.session_key, - extraSessionData, - exportFormat: false, - roomId: content.room_id, - algorithm: content.algorithm, - forwardingKeyChain: [], - keysClaimed: event.getKeysClaimed(), - }; - return roomKey; - } - /** - * Parse a RoomKey out of an `m.forwarded_room_key` event. - * - * @param event - the event containing the forwarded room key. - * - * @returns The `RoomKey` if it could be successfully parsed out of the - * event. - * - * @internal - * - */ - forwardedRoomKeyFromEvent(event) { - // the properties in m.forwarded_room_key are a superset of those in m.room_key, so - // start by parsing the m.room_key fields. - const roomKey = this.roomKeyFromEvent(event); - if (!roomKey) { - return; - } - const senderKey = event.getSenderKey(); - const content = event.getContent(); - const senderKeyUser = this.baseApis.crypto.deviceList.getUserByIdentityKey(olmlib.OLM_ALGORITHM, senderKey); - // We received this to-device event from event.getSenderKey(), but the original - // creator of the room key is claimed in the content. - const claimedCurve25519Key = content.sender_key; - const claimedEd25519Key = content.sender_claimed_ed25519_key; - let forwardingKeyChain = Array.isArray(content.forwarding_curve25519_key_chain) - ? content.forwarding_curve25519_key_chain - : []; - // copy content before we modify it - forwardingKeyChain = forwardingKeyChain.slice(); - forwardingKeyChain.push(senderKey); - // Check if we have all the fields we need. - if (senderKeyUser !== event.getSender()) { - this.prefixedLogger.error("sending device does not belong to the user it claims to be from"); - return; - } - if (!claimedCurve25519Key) { - this.prefixedLogger.error("forwarded_room_key event is missing sender_key field"); - return; - } - if (!claimedEd25519Key) { - this.prefixedLogger.error(`forwarded_room_key_event is missing sender_claimed_ed25519_key field`); - return; - } - const keysClaimed = { - ed25519: claimedEd25519Key, - }; - // FIXME: We're reusing the same field to track both: - // - // 1. The Olm identity we've received this room key from. - // 2. The Olm identity deduced (in the trusted case) or claiming (in the - // untrusted case) to be the original creator of this room key. - // - // We now overwrite the value tracking usage 1 with the value tracking usage 2. - roomKey.senderKey = claimedCurve25519Key; - // Replace our keysClaimed as well. - roomKey.keysClaimed = keysClaimed; - roomKey.exportFormat = true; - roomKey.forwardingKeyChain = forwardingKeyChain; - // forwarded keys are always untrusted - roomKey.extraSessionData.untrusted = true; - return roomKey; - } - /** - * Determine if we should accept the forwarded room key that was found in the given - * event. - * - * @param event - An `m.forwarded_room_key` event. - * @param roomKey - The room key that was found in the event. - * - * @returns promise that will resolve to a boolean telling us if it's ok to - * accept the given forwarded room key. - * - * @internal - * - */ - shouldAcceptForwardedKey(event, roomKey) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const senderKey = event.getSenderKey(); - const sendingDevice = (_a = this.crypto.deviceList.getDeviceByIdentityKey(olmlib.OLM_ALGORITHM, senderKey)) !== null && _a !== void 0 ? _a : undefined; - const deviceTrust = this.crypto.checkDeviceInfoTrust(event.getSender(), sendingDevice); - // Using the plaintext sender here is fine since we checked that the - // sender matches to the user id in the device keys when this event was - // originally decrypted. This can obviously only happen if the device - // keys have been downloaded, but if they haven't the - // `deviceTrust.isVerified()` flag would be false as well. - // - // It would still be far nicer if the `sendingDevice` had a user ID - // attached to it that went through signature checks. - const fromUs = event.getSender() === this.baseApis.getUserId(); - const keyFromOurVerifiedDevice = deviceTrust.isVerified() && fromUs; - const weRequested = yield this.wasRoomKeyRequested(event, roomKey); - const fromInviter = this.wasRoomKeyForwardedByInviter(event, roomKey); - const sharedAsHistory = this.wasRoomKeyForwardedAsHistory(roomKey); - return (weRequested && keyFromOurVerifiedDevice) || (fromInviter && sharedAsHistory); - }); - } - /** - * Did we ever request the given room key from the event sender and its - * accompanying device. - * - * @param event - An `m.forwarded_room_key` event. - * @param roomKey - The room key that was found in the event. - * - * @internal - * - */ - wasRoomKeyRequested(event, roomKey) { - return __awaiter(this, void 0, void 0, function* () { - // We send the `m.room_key_request` out as a wildcard to-device request, - // otherwise we would have to duplicate the same content for each - // device. This is why we need to pass in "*" as the device id here. - const outgoingRequests = yield this.crypto.cryptoStore.getOutgoingRoomKeyRequestsByTarget(event.getSender(), "*", [OutgoingRoomKeyRequestManager_1.RoomKeyRequestState.Sent]); - return outgoingRequests.some((req) => req.requestBody.room_id === roomKey.roomId && req.requestBody.session_id === roomKey.sessionId); - }); - } - wasRoomKeyForwardedByInviter(event, roomKey) { - var _a, _b, _c; - // TODO: This is supposed to have a time limit. We should only accept - // such keys if we happen to receive them for a recently joined room. - const room = this.baseApis.getRoom(roomKey.roomId); - const senderKey = event.getSenderKey(); - if (!senderKey) { - return false; - } - const senderKeyUser = this.crypto.deviceList.getUserByIdentityKey(olmlib.OLM_ALGORITHM, senderKey); - if (!senderKeyUser) { - return false; - } - const memberEvent = (_a = room === null || room === void 0 ? void 0 : room.getMember(this.userId)) === null || _a === void 0 ? void 0 : _a.events.member; - const fromInviter = (memberEvent === null || memberEvent === void 0 ? void 0 : memberEvent.getSender()) === senderKeyUser || - (((_b = memberEvent === null || memberEvent === void 0 ? void 0 : memberEvent.getUnsigned()) === null || _b === void 0 ? void 0 : _b.prev_sender) === senderKeyUser && - ((_c = memberEvent === null || memberEvent === void 0 ? void 0 : memberEvent.getPrevContent()) === null || _c === void 0 ? void 0 : _c.membership) === "invite"); - if (room && fromInviter) { - return true; - } - else { - return false; - } - } - wasRoomKeyForwardedAsHistory(roomKey) { - const room = this.baseApis.getRoom(roomKey.roomId); - // If the key is not for a known room, then something fishy is going on, - // so we reject the key out of caution. In practice, this is a bit moot - // because we'll only accept shared_history forwarded by the inviter, and - // we won't know who was the inviter for an unknown room, so we'll reject - // it anyway. - if (room && roomKey.extraSessionData.sharedHistory) { - return true; - } - else { - return false; - } - } - /** - * Check if a forwarded room key should be parked. - * - * A forwarded room key should be parked if it's a key for a room we're not - * in. We park the forwarded room key in case *this sender* invites us to - * that room later. - */ - shouldParkForwardedKey(roomKey) { - const room = this.baseApis.getRoom(roomKey.roomId); - if (!room && roomKey.extraSessionData.sharedHistory) { - return true; - } - else { - return false; - } - } - /** - * Park the given room key to our store. - * - * @param event - An `m.forwarded_room_key` event. - * @param roomKey - The room key that was found in the event. - * - * @internal - * - */ - parkForwardedKey(event, roomKey) { - return __awaiter(this, void 0, void 0, function* () { - const parkedData = { - senderId: event.getSender(), - senderKey: roomKey.senderKey, - sessionId: roomKey.sessionId, - sessionKey: roomKey.sessionKey, - keysClaimed: roomKey.keysClaimed, - forwardingCurve25519KeyChain: roomKey.forwardingKeyChain, - }; - yield this.crypto.cryptoStore.doTxn("readwrite", ["parked_shared_history"], (txn) => this.crypto.cryptoStore.addParkedSharedHistory(roomKey.roomId, parkedData, txn), logger_1.logger.withPrefix("[addParkedSharedHistory]")); - }); - } - /** - * Add the given room key to our store. - * - * @param roomKey - The room key that should be added to the store. - * - * @internal - * - */ - addRoomKey(roomKey) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield this.olmDevice.addInboundGroupSession(roomKey.roomId, roomKey.senderKey, roomKey.forwardingKeyChain, roomKey.sessionId, roomKey.sessionKey, roomKey.keysClaimed, roomKey.exportFormat, roomKey.extraSessionData); - // have another go at decrypting events sent with this session. - if (yield this.retryDecryption(roomKey.senderKey, roomKey.sessionId, !roomKey.extraSessionData.untrusted)) { - // cancel any outstanding room key requests for this session. - // Only do this if we managed to decrypt every message in the - // session, because if we didn't, we leave the other key - // requests in the hopes that someone sends us a key that - // includes an earlier index. - this.crypto.cancelRoomKeyRequest({ - algorithm: roomKey.algorithm, - room_id: roomKey.roomId, - session_id: roomKey.sessionId, - sender_key: roomKey.senderKey, - }); - } - // don't wait for the keys to be backed up for the server - yield this.crypto.backupManager.backupGroupSession(roomKey.senderKey, roomKey.sessionId); - } - catch (e) { - this.prefixedLogger.error(`Error handling m.room_key_event: ${e}`); - } - }); - } - /** - * Handle room keys that have been forwarded to us as an - * `m.forwarded_room_key` event. - * - * Forwarded room keys need special handling since we have no way of knowing - * who the original creator of the room key was. This naturally means that - * forwarded room keys are always untrusted and should only be accepted in - * some cases. - * - * @param event - An `m.forwarded_room_key` event. - * - * @internal - * - */ - onForwardedRoomKey(event) { - return __awaiter(this, void 0, void 0, function* () { - const roomKey = this.forwardedRoomKeyFromEvent(event); - if (!roomKey) { - return; - } - if (yield this.shouldAcceptForwardedKey(event, roomKey)) { - yield this.addRoomKey(roomKey); - } - else if (this.shouldParkForwardedKey(roomKey)) { - yield this.parkForwardedKey(event, roomKey); - } - }); - } - onRoomKeyEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - if (event.getType() == "m.forwarded_room_key") { - yield this.onForwardedRoomKey(event); - } - else { - const roomKey = this.roomKeyFromEvent(event); - if (!roomKey) { - return; - } - yield this.addRoomKey(roomKey); - } - }); - } - /** - * @param event - key event - */ - onRoomKeyWithheldEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getContent(); - const senderKey = content.sender_key; - if (content.code === "m.no_olm") { - yield this.onNoOlmWithheldEvent(event); - } - else if (content.code === "m.unavailable") { - // this simply means that the other device didn't have the key, which isn't very useful information. Don't - // record it in the storage - } - else { - yield this.olmDevice.addInboundGroupSessionWithheld(content.room_id, senderKey, content.session_id, content.code, content.reason); - } - // Having recorded the problem, retry decryption on any affected messages. - // It's unlikely we'll be able to decrypt sucessfully now, but this will - // update the error message. - // - if (content.session_id) { - yield this.retryDecryption(senderKey, content.session_id); - } - else { - // no_olm messages aren't specific to a given megolm session, so - // we trigger retrying decryption for all the messages from the sender's - // key, so that we can update the error message to indicate the olm - // session problem. - yield this.retryDecryptionFromSender(senderKey); - } - }); - } - onNoOlmWithheldEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getContent(); - const senderKey = content.sender_key; - const sender = event.getSender(); - this.prefixedLogger.warn(`${sender}:${senderKey} was unable to establish an olm session with us`); - // if the sender says that they haven't been able to establish an olm - // session, let's proactively establish one - if (yield this.olmDevice.getSessionIdForDevice(senderKey)) { - // a session has already been established, so we don't need to - // create a new one. - this.prefixedLogger.debug("New session already created. Not creating a new one."); - yield this.olmDevice.recordSessionProblem(senderKey, "no_olm", true); - return; - } - let device = this.crypto.deviceList.getDeviceByIdentityKey(content.algorithm, senderKey); - if (!device) { - // if we don't know about the device, fetch the user's devices again - // and retry before giving up - yield this.crypto.downloadKeys([sender], false); - device = this.crypto.deviceList.getDeviceByIdentityKey(content.algorithm, senderKey); - if (!device) { - this.prefixedLogger.info("Couldn't find device for identity key " + senderKey + ": not establishing session"); - yield this.olmDevice.recordSessionProblem(senderKey, "no_olm", false); - return; - } - } - // XXX: switch this to use encryptAndSendToDevices() rather than duplicating it? - yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, new Map([[sender, [device]]]), false); - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - yield olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.userId, undefined, this.olmDevice, sender, device, { type: "m.dummy" }); - yield this.olmDevice.recordSessionProblem(senderKey, "no_olm", true); - yield this.baseApis.sendToDevice("m.room.encrypted", new Map([[sender, new Map([[device.deviceId, encryptedContent]])]])); - }); - } - hasKeysForKeyRequest(keyRequest) { - const body = keyRequest.requestBody; - return this.olmDevice.hasInboundSessionKeys(body.room_id, body.sender_key, body.session_id); - } - shareKeysWithDevice(keyRequest) { - const userId = keyRequest.userId; - const deviceId = keyRequest.deviceId; - const deviceInfo = this.crypto.getStoredDevice(userId, deviceId); - const body = keyRequest.requestBody; - // XXX: switch this to use encryptAndSendToDevices()? - this.olmlib - .ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, new Map([[userId, [deviceInfo]]])) - .then((devicemap) => { - var _a; - const olmSessionResult = (_a = devicemap.get(userId)) === null || _a === void 0 ? void 0 : _a.get(deviceId); - if (!(olmSessionResult === null || olmSessionResult === void 0 ? void 0 : olmSessionResult.sessionId)) { - // no session with this device, probably because there - // were no one-time keys. - // - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - return null; - } - this.prefixedLogger.log("sharing keys for session " + - body.sender_key + - "|" + - body.session_id + - " with device " + - userId + - ":" + - deviceId); - return this.buildKeyForwardingMessage(body.room_id, body.sender_key, body.session_id); - }) - .then((payload) => { - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - return this.olmlib - .encryptMessageForDevice(encryptedContent.ciphertext, this.userId, undefined, this.olmDevice, userId, deviceInfo, payload) - .then(() => { - // TODO: retries - return this.baseApis.sendToDevice("m.room.encrypted", new Map([[userId, new Map([[deviceId, encryptedContent]])]])); - }); - }); - } - buildKeyForwardingMessage(roomId, senderKey, sessionId) { - return __awaiter(this, void 0, void 0, function* () { - const key = yield this.olmDevice.getInboundGroupSessionKey(roomId, senderKey, sessionId); - return { - type: "m.forwarded_room_key", - content: { - "algorithm": olmlib.MEGOLM_ALGORITHM, - "room_id": roomId, - "sender_key": senderKey, - "sender_claimed_ed25519_key": key.sender_claimed_ed25519_key, - "session_id": sessionId, - "session_key": key.key, - "chain_index": key.chain_index, - "forwarding_curve25519_key_chain": key.forwarding_curve25519_key_chain, - "org.matrix.msc3061.shared_history": key.shared_history || false, - }, - }; - }); - } - /** - * @param untrusted - whether the key should be considered as untrusted - * @param source - where the key came from - */ - importRoomKey(session, { untrusted, source } = {}) { - const extraSessionData = {}; - if (untrusted || session.untrusted) { - extraSessionData.untrusted = true; - } - if (session["org.matrix.msc3061.shared_history"]) { - extraSessionData.sharedHistory = true; - } - return this.olmDevice - .addInboundGroupSession(session.room_id, session.sender_key, session.forwarding_curve25519_key_chain, session.session_id, session.session_key, session.sender_claimed_keys, true, extraSessionData) - .then(() => { - if (source !== "backup") { - // don't wait for it to complete - this.crypto.backupManager.backupGroupSession(session.sender_key, session.session_id).catch((e) => { - // This throws if the upload failed, but this is fine - // since it will have written it to the db and will retry. - this.prefixedLogger.log("Failed to back up megolm session", e); - }); - } - // have another go at decrypting events sent with this session. - this.retryDecryption(session.sender_key, session.session_id, !extraSessionData.untrusted); - }); - } - /** - * Have another go at decrypting events after we receive a key. Resolves once - * decryption has been re-attempted on all events. - * - * @internal - * @param forceRedecryptIfUntrusted - whether messages that were already - * successfully decrypted using untrusted keys should be re-decrypted - * - * @returns whether all messages were successfully - * decrypted with trusted keys - */ - retryDecryption(senderKey, sessionId, forceRedecryptIfUntrusted) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const senderPendingEvents = this.pendingEvents.get(senderKey); - if (!senderPendingEvents) { - return true; - } - const pending = senderPendingEvents.get(sessionId); - if (!pending) { - return true; - } - const pendingList = [...pending]; - this.prefixedLogger.debug("Retrying decryption on events:", pendingList.map((e) => `${e.getId()}`)); - yield Promise.all(pendingList.map((ev) => __awaiter(this, void 0, void 0, function* () { - try { - yield ev.attemptDecryption(this.crypto, { isRetry: true, forceRedecryptIfUntrusted }); - } - catch (e) { - // don't die if something goes wrong - } - }))); - // If decrypted successfully with trusted keys, they'll have - // been removed from pendingEvents - return !((_a = this.pendingEvents.get(senderKey)) === null || _a === void 0 ? void 0 : _a.has(sessionId)); - }); - } - retryDecryptionFromSender(senderKey) { - return __awaiter(this, void 0, void 0, function* () { - const senderPendingEvents = this.pendingEvents.get(senderKey); - if (!senderPendingEvents) { - return true; - } - this.pendingEvents.delete(senderKey); - yield Promise.all([...senderPendingEvents].map(([_sessionId, pending]) => __awaiter(this, void 0, void 0, function* () { - yield Promise.all([...pending].map((ev) => __awaiter(this, void 0, void 0, function* () { - try { - yield ev.attemptDecryption(this.crypto); - } - catch (e) { - // don't die if something goes wrong - } - }))); - }))); - return !this.pendingEvents.has(senderKey); - }); - } - sendSharedHistoryInboundSessions(devicesByUser) { - return __awaiter(this, void 0, void 0, function* () { - yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser); - const sharedHistorySessions = yield this.olmDevice.getSharedHistoryInboundGroupSessions(this.roomId); - this.prefixedLogger.log(`Sharing history in with users ${Array.from(devicesByUser.keys())}`, sharedHistorySessions.map(([senderKey, sessionId]) => `${senderKey}|${sessionId}`)); - for (const [senderKey, sessionId] of sharedHistorySessions) { - const payload = yield this.buildKeyForwardingMessage(this.roomId, senderKey, sessionId); - // FIXME: use encryptAndSendToDevices() rather than duplicating it here. - const promises = []; - const contentMap = new Map(); - for (const [userId, devices] of devicesByUser) { - const deviceMessages = new Map(); - contentMap.set(userId, deviceMessages); - for (const deviceInfo of devices) { - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - deviceMessages.set(deviceInfo.deviceId, encryptedContent); - promises.push(olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.userId, undefined, this.olmDevice, userId, deviceInfo, payload)); - } - } - yield Promise.all(promises); - // prune out any devices that encryptMessageForDevice could not encrypt for, - // in which case it will have just not added anything to the ciphertext object. - // There's no point sending messages to devices if we couldn't encrypt to them, - // since that's effectively a blank message. - for (const [userId, deviceMessages] of contentMap) { - for (const [deviceId, content] of deviceMessages) { - if (!hasCiphertext(content)) { - this.prefixedLogger.log("No ciphertext for device " + userId + ":" + deviceId + ": pruning"); - deviceMessages.delete(deviceId); - } - } - // No devices left for that user? Strip that too. - if (deviceMessages.size === 0) { - this.prefixedLogger.log("Pruned all devices for user " + userId); - contentMap.delete(userId); - } - } - // Is there anything left? - if (contentMap.size === 0) { - this.prefixedLogger.log("No users left to send to: aborting"); - return; - } - yield this.baseApis.sendToDevice("m.room.encrypted", contentMap); - } - }); - } -} -exports.MegolmDecryption = MegolmDecryption; -const PROBLEM_DESCRIPTIONS = { - no_olm: "The sender was unable to establish a secure channel.", - unknown: "The secure channel with the sender was corrupted.", -}; -(0, base_1.registerAlgorithm)(olmlib.MEGOLM_ALGORITHM, MegolmEncryption, MegolmDecryption); - -},{"../../@types/event":306,"../../logger":374,"../../utils":416,"../OlmDevice":327,"../OutgoingRoomKeyRequestManager":328,"../olmlib":343,"./base":332,"uuid":287}],335:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("../../logger"); -const olmlib = __importStar(require("../olmlib")); -const deviceinfo_1 = require("../deviceinfo"); -const base_1 = require("./base"); -const DeviceVerification = deviceinfo_1.DeviceInfo.DeviceVerification; -/** - * Olm encryption implementation - * - * @param params - parameters, as per {@link EncryptionAlgorithm} - */ -class OlmEncryption extends base_1.EncryptionAlgorithm { - constructor() { - super(...arguments); - this.sessionPrepared = false; - this.prepPromise = null; - } - /** - * @internal - - * @param roomMembers - list of currently-joined users in the room - * @returns Promise which resolves when setup is complete - */ - ensureSession(roomMembers) { - if (this.prepPromise) { - // prep already in progress - return this.prepPromise; - } - if (this.sessionPrepared) { - // prep already done - return Promise.resolve(); - } - this.prepPromise = this.crypto - .downloadKeys(roomMembers) - .then(() => { - return this.crypto.ensureOlmSessionsForUsers(roomMembers); - }) - .then(() => { - this.sessionPrepared = true; - }) - .finally(() => { - this.prepPromise = null; - }); - return this.prepPromise; - } - /** - * @param content - plaintext event content - * - * @returns Promise which resolves to the new event body - */ - encryptMessage(room, eventType, content) { - return __awaiter(this, void 0, void 0, function* () { - // pick the list of recipients based on the membership list. - // - // TODO: there is a race condition here! What if a new user turns up - // just as you are sending a secret message? - const members = yield room.getEncryptionTargetMembers(); - const users = members.map(function (u) { - return u.userId; - }); - yield this.ensureSession(users); - const payloadFields = { - room_id: room.roomId, - type: eventType, - content: content, - }; - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - }; - const promises = []; - for (const userId of users) { - const devices = this.crypto.getStoredDevicesForUser(userId) || []; - for (const deviceInfo of devices) { - const key = deviceInfo.getIdentityKey(); - if (key == this.olmDevice.deviceCurve25519Key) { - // don't bother sending to ourself - continue; - } - if (deviceInfo.verified == DeviceVerification.BLOCKED) { - // don't bother setting up sessions with blocked users - continue; - } - promises.push(olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.userId, this.deviceId, this.olmDevice, userId, deviceInfo, payloadFields)); - } - } - return Promise.all(promises).then(() => encryptedContent); - }); - } -} -/** - * Olm decryption implementation - * - * @param params - parameters, as per {@link DecryptionAlgorithm} - */ -class OlmDecryption extends base_1.DecryptionAlgorithm { - /** - * returns a promise which resolves to a - * {@link EventDecryptionResult} once we have finished - * decrypting. Rejects with an `algorithms.DecryptionError` if there is a - * problem decrypting the event. - */ - decryptEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getWireContent(); - const deviceKey = content.sender_key; - const ciphertext = content.ciphertext; - if (!ciphertext) { - throw new base_1.DecryptionError("OLM_MISSING_CIPHERTEXT", "Missing ciphertext"); - } - if (!(this.olmDevice.deviceCurve25519Key in ciphertext)) { - throw new base_1.DecryptionError("OLM_NOT_INCLUDED_IN_RECIPIENTS", "Not included in recipients"); - } - const message = ciphertext[this.olmDevice.deviceCurve25519Key]; - let payloadString; - try { - payloadString = yield this.decryptMessage(deviceKey, message); - } - catch (e) { - throw new base_1.DecryptionError("OLM_BAD_ENCRYPTED_MESSAGE", "Bad Encrypted Message", { - sender: deviceKey, - err: e, - }); - } - const payload = JSON.parse(payloadString); - // check that we were the intended recipient, to avoid unknown-key attack - // https://github.com/vector-im/vector-web/issues/2483 - if (payload.recipient != this.userId) { - throw new base_1.DecryptionError("OLM_BAD_RECIPIENT", "Message was intented for " + payload.recipient); - } - if (payload.recipient_keys.ed25519 != this.olmDevice.deviceEd25519Key) { - throw new base_1.DecryptionError("OLM_BAD_RECIPIENT_KEY", "Message not intended for this device", { - intended: payload.recipient_keys.ed25519, - our_key: this.olmDevice.deviceEd25519Key, - }); - } - // check that the device that encrypted the event belongs to the user - // that the event claims it's from. We need to make sure that our - // device list is up-to-date. If the device is unknown, we can only - // assume that the device logged out. Some event handlers, such as - // secret sharing, may be more strict and reject events that come from - // unknown devices. - yield this.crypto.deviceList.downloadKeys([event.getSender()], false); - const senderKeyUser = this.crypto.deviceList.getUserByIdentityKey(olmlib.OLM_ALGORITHM, deviceKey); - if (senderKeyUser !== event.getSender() && senderKeyUser != undefined) { - throw new base_1.DecryptionError("OLM_BAD_SENDER", "Message claimed to be from " + event.getSender(), { - real_sender: senderKeyUser, - }); - } - // check that the original sender matches what the homeserver told us, to - // avoid people masquerading as others. - // (this check is also provided via the sender's embedded ed25519 key, - // which is checked elsewhere). - if (payload.sender != event.getSender()) { - throw new base_1.DecryptionError("OLM_FORWARDED_MESSAGE", "Message forwarded from " + payload.sender, { - reported_sender: event.getSender(), - }); - } - // Olm events intended for a room have a room_id. - if (payload.room_id !== event.getRoomId()) { - throw new base_1.DecryptionError("OLM_BAD_ROOM", "Message intended for room " + payload.room_id, { - reported_room: event.getRoomId() || "ROOM_ID_UNDEFINED", - }); - } - const claimedKeys = payload.keys || {}; - return { - clearEvent: payload, - senderCurve25519Key: deviceKey, - claimedEd25519Key: claimedKeys.ed25519 || null, - }; - }); - } - /** - * Attempt to decrypt an Olm message - * - * @param theirDeviceIdentityKey - Curve25519 identity key of the sender - * @param message - message object, with 'type' and 'body' fields - * - * @returns payload, if decrypted successfully. - */ - decryptMessage(theirDeviceIdentityKey, message) { - // This is a wrapper that serialises decryptions of prekey messages, because - // otherwise we race between deciding we have no active sessions for the message - // and creating a new one, which we can only do once because it removes the OTK. - if (message.type !== 0) { - // not a prekey message: we can safely just try & decrypt it - return this.reallyDecryptMessage(theirDeviceIdentityKey, message); - } - else { - const myPromise = this.olmDevice.olmPrekeyPromise.then(() => { - return this.reallyDecryptMessage(theirDeviceIdentityKey, message); - }); - // we want the error, but don't propagate it to the next decryption - this.olmDevice.olmPrekeyPromise = myPromise.catch(() => { }); - return myPromise; - } - } - reallyDecryptMessage(theirDeviceIdentityKey, message) { - return __awaiter(this, void 0, void 0, function* () { - const sessionIds = yield this.olmDevice.getSessionIdsForDevice(theirDeviceIdentityKey); - // try each session in turn. - const decryptionErrors = {}; - for (const sessionId of sessionIds) { - try { - const payload = yield this.olmDevice.decryptMessage(theirDeviceIdentityKey, sessionId, message.type, message.body); - logger_1.logger.log("Decrypted Olm message from " + theirDeviceIdentityKey + " with session " + sessionId); - return payload; - } - catch (e) { - const foundSession = yield this.olmDevice.matchesSession(theirDeviceIdentityKey, sessionId, message.type, message.body); - if (foundSession) { - // decryption failed, but it was a prekey message matching this - // session, so it should have worked. - throw new Error("Error decrypting prekey message with existing session id " + - sessionId + - ": " + - e.message); - } - // otherwise it's probably a message for another session; carry on, but - // keep a record of the error - decryptionErrors[sessionId] = e.message; - } - } - if (message.type !== 0) { - // not a prekey message, so it should have matched an existing session, but it - // didn't work. - if (sessionIds.length === 0) { - throw new Error("No existing sessions"); - } - throw new Error("Error decrypting non-prekey message with existing sessions: " + JSON.stringify(decryptionErrors)); - } - // prekey message which doesn't match any existing sessions: make a new - // session. - let res; - try { - res = yield this.olmDevice.createInboundSession(theirDeviceIdentityKey, message.type, message.body); - } - catch (e) { - decryptionErrors["(new)"] = e.message; - throw new Error("Error decrypting prekey message: " + JSON.stringify(decryptionErrors)); - } - logger_1.logger.log("created new inbound Olm session ID " + res.session_id + " with " + theirDeviceIdentityKey); - return res.payload; - }); - } -} -(0, base_1.registerAlgorithm)(olmlib.OLM_ALGORITHM, OlmEncryption, OlmDecryption); - -},{"../../logger":374,"../deviceinfo":340,"../olmlib":343,"./base":332}],336:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CrossSigningKey = void 0; -// TODO: Merge this with crypto.js once converted -var CrossSigningKey; -(function (CrossSigningKey) { - CrossSigningKey["Master"] = "master"; - CrossSigningKey["SelfSigning"] = "self_signing"; - CrossSigningKey["UserSigning"] = "user_signing"; -})(CrossSigningKey = exports.CrossSigningKey || (exports.CrossSigningKey = {})); - -},{}],337:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DefaultAlgorithm = exports.algorithmsByName = exports.Aes256 = exports.Curve25519 = exports.BackupManager = void 0; -const client_1 = require("../client"); -const logger_1 = require("../logger"); -const olmlib_1 = require("./olmlib"); -const key_passphrase_1 = require("./key_passphrase"); -const utils_1 = require("../utils"); -const indexeddb_crypto_store_1 = require("./store/indexeddb-crypto-store"); -const recoverykey_1 = require("./recoverykey"); -const aes_1 = require("./aes"); -const NamespacedValue_1 = require("../NamespacedValue"); -const index_1 = require("./index"); -const crypto_1 = require("./crypto"); -const http_api_1 = require("../http-api"); -const KEY_BACKUP_KEYS_PER_REQUEST = 200; -const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms -/** - * Manages the key backup. - */ -class BackupManager { - constructor(baseApis, getKey) { - this.baseApis = baseApis; - this.getKey = getKey; - this.sessionLastCheckAttemptedTime = {}; // When did we last try to check the server for a given session id? - this.checkedForBackup = false; - this.sendingBackups = false; - } - get version() { - return this.backupInfo && this.backupInfo.version; - } - /** - * Performs a quick check to ensure that the backup info looks sane. - * - * Throws an error if a problem is detected. - * - * @param info - the key backup info - */ - static checkBackupVersion(info) { - const Algorithm = exports.algorithmsByName[info.algorithm]; - if (!Algorithm) { - throw new Error("Unknown backup algorithm: " + info.algorithm); - } - if (typeof info.auth_data !== "object") { - throw new Error("Invalid backup data returned"); - } - return Algorithm.checkBackupVersion(info); - } - static makeAlgorithm(info, getKey) { - const Algorithm = exports.algorithmsByName[info.algorithm]; - if (!Algorithm) { - throw new Error("Unknown backup algorithm"); - } - return Algorithm.init(info.auth_data, getKey); - } - enableKeyBackup(info) { - return __awaiter(this, void 0, void 0, function* () { - this.backupInfo = info; - if (this.algorithm) { - this.algorithm.free(); - } - this.algorithm = yield BackupManager.makeAlgorithm(info, this.getKey); - this.baseApis.emit(index_1.CryptoEvent.KeyBackupStatus, true); - // There may be keys left over from a partially completed backup, so - // schedule a send to check. - this.scheduleKeyBackupSend(); - }); - } - /** - * Disable backing up of keys. - */ - disableKeyBackup() { - if (this.algorithm) { - this.algorithm.free(); - } - this.algorithm = undefined; - this.backupInfo = undefined; - this.baseApis.emit(index_1.CryptoEvent.KeyBackupStatus, false); - } - getKeyBackupEnabled() { - if (!this.checkedForBackup) { - return null; - } - return Boolean(this.algorithm); - } - prepareKeyBackupVersion(key, algorithm) { - return __awaiter(this, void 0, void 0, function* () { - const Algorithm = algorithm ? exports.algorithmsByName[algorithm] : exports.DefaultAlgorithm; - if (!Algorithm) { - throw new Error("Unknown backup algorithm"); - } - const [privateKey, authData] = yield Algorithm.prepare(key); - const recoveryKey = (0, recoverykey_1.encodeRecoveryKey)(privateKey); - return { - algorithm: Algorithm.algorithmName, - auth_data: authData, - recovery_key: recoveryKey, - privateKey, - }; - }); - } - createKeyBackupVersion(info) { - return __awaiter(this, void 0, void 0, function* () { - this.algorithm = yield BackupManager.makeAlgorithm(info, this.getKey); - }); - } - /** - * Check the server for an active key backup and - * if one is present and has a valid signature from - * one of the user's verified devices, start backing up - * to it. - */ - checkAndStart() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log("Checking key backup status..."); - if (this.baseApis.isGuest()) { - logger_1.logger.log("Skipping key backup check since user is guest"); - this.checkedForBackup = true; - return null; - } - let backupInfo; - try { - backupInfo = (_a = (yield this.baseApis.getKeyBackupVersion())) !== null && _a !== void 0 ? _a : undefined; - } - catch (e) { - logger_1.logger.log("Error checking for active key backup", e); - if (e.httpStatus === 404) { - // 404 is returned when the key backup does not exist, so that - // counts as successfully checking. - this.checkedForBackup = true; - } - return null; - } - this.checkedForBackup = true; - const trustInfo = yield this.isKeyBackupTrusted(backupInfo); - if (trustInfo.usable && !this.backupInfo) { - logger_1.logger.log(`Found usable key backup v${backupInfo.version}: enabling key backups`); - yield this.enableKeyBackup(backupInfo); - } - else if (!trustInfo.usable && this.backupInfo) { - logger_1.logger.log("No usable key backup: disabling key backup"); - this.disableKeyBackup(); - } - else if (!trustInfo.usable && !this.backupInfo) { - logger_1.logger.log("No usable key backup: not enabling key backup"); - } - else if (trustInfo.usable && this.backupInfo) { - // may not be the same version: if not, we should switch - if (backupInfo.version !== this.backupInfo.version) { - logger_1.logger.log(`On backup version ${this.backupInfo.version} but ` + - `found version ${backupInfo.version}: switching.`); - this.disableKeyBackup(); - yield this.enableKeyBackup(backupInfo); - // We're now using a new backup, so schedule all the keys we have to be - // uploaded to the new backup. This is a bit of a workaround to upload - // keys to a new backup in *most* cases, but it won't cover all cases - // because we don't remember what backup version we uploaded keys to: - // see https://github.com/vector-im/element-web/issues/14833 - yield this.scheduleAllGroupSessionsForBackup(); - } - else { - logger_1.logger.log(`Backup version ${backupInfo.version} still current`); - } - } - return { backupInfo, trustInfo }; - }); - } - /** - * Forces a re-check of the key backup and enables/disables it - * as appropriate. - * - * @returns Object with backup info (as returned by - * getKeyBackupVersion) in backupInfo and - * trust information (as returned by isKeyBackupTrusted) - * in trustInfo. - */ - checkKeyBackup() { - return __awaiter(this, void 0, void 0, function* () { - this.checkedForBackup = false; - return this.checkAndStart(); - }); - } - /** - * Attempts to retrieve a session from a key backup, if enough time - * has elapsed since the last check for this session id. - */ - queryKeyBackupRateLimited(targetRoomId, targetSessionId) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.backupInfo) { - return; - } - const now = new Date().getTime(); - if (!this.sessionLastCheckAttemptedTime[targetSessionId] || - now - this.sessionLastCheckAttemptedTime[targetSessionId] > KEY_BACKUP_CHECK_RATE_LIMIT) { - this.sessionLastCheckAttemptedTime[targetSessionId] = now; - yield this.baseApis.restoreKeyBackupWithCache(targetRoomId, targetSessionId, this.backupInfo, {}); - } - }); - } - /** - * Check if the given backup info is trusted. - * - * @param backupInfo - key backup info dict from /room_keys/version - */ - isKeyBackupTrusted(backupInfo) { - return __awaiter(this, void 0, void 0, function* () { - const ret = { - usable: false, - trusted_locally: false, - sigs: [], - }; - if (!backupInfo || !backupInfo.algorithm || !backupInfo.auth_data || !backupInfo.auth_data.signatures) { - logger_1.logger.info("Key backup is absent or missing required data"); - return ret; - } - const userId = this.baseApis.getUserId(); - const privKey = yield this.baseApis.crypto.getSessionBackupPrivateKey(); - if (privKey) { - let algorithm = null; - try { - algorithm = yield BackupManager.makeAlgorithm(backupInfo, () => __awaiter(this, void 0, void 0, function* () { return privKey; })); - if (yield algorithm.keyMatches(privKey)) { - logger_1.logger.info("Backup is trusted locally"); - ret.trusted_locally = true; - } - } - catch (_a) { - // do nothing -- if we have an error, then we don't mark it as - // locally trusted - } - finally { - algorithm === null || algorithm === void 0 ? void 0 : algorithm.free(); - } - } - const mySigs = backupInfo.auth_data.signatures[userId] || {}; - for (const keyId of Object.keys(mySigs)) { - const keyIdParts = keyId.split(":"); - if (keyIdParts[0] !== "ed25519") { - logger_1.logger.log("Ignoring unknown signature type: " + keyIdParts[0]); - continue; - } - // Could be a cross-signing master key, but just say this is the device - // ID for backwards compat - const sigInfo = { deviceId: keyIdParts[1] }; - // first check to see if it's from our cross-signing key - const crossSigningId = this.baseApis.crypto.crossSigningInfo.getId(); - if (crossSigningId === sigInfo.deviceId) { - sigInfo.crossSigningId = true; - try { - yield (0, olmlib_1.verifySignature)(this.baseApis.crypto.olmDevice, backupInfo.auth_data, userId, sigInfo.deviceId, crossSigningId); - sigInfo.valid = true; - } - catch (e) { - logger_1.logger.warn("Bad signature from cross signing key " + crossSigningId, e); - sigInfo.valid = false; - } - ret.sigs.push(sigInfo); - continue; - } - // Now look for a sig from a device - // At some point this can probably go away and we'll just support - // it being signed by the cross-signing master key - const device = this.baseApis.crypto.deviceList.getStoredDevice(userId, sigInfo.deviceId); - if (device) { - sigInfo.device = device; - sigInfo.deviceTrust = this.baseApis.checkDeviceTrust(userId, sigInfo.deviceId); - try { - yield (0, olmlib_1.verifySignature)(this.baseApis.crypto.olmDevice, backupInfo.auth_data, userId, device.deviceId, device.getFingerprint()); - sigInfo.valid = true; - } - catch (e) { - logger_1.logger.info("Bad signature from key ID " + - keyId + - " userID " + - this.baseApis.getUserId() + - " device ID " + - device.deviceId + - " fingerprint: " + - device.getFingerprint(), backupInfo.auth_data, e); - sigInfo.valid = false; - } - } - else { - sigInfo.valid = null; // Can't determine validity because we don't have the signing device - logger_1.logger.info("Ignoring signature from unknown key " + keyId); - } - ret.sigs.push(sigInfo); - } - ret.usable = ret.sigs.some((s) => { - var _a; - return s.valid && ((s.device && ((_a = s.deviceTrust) === null || _a === void 0 ? void 0 : _a.isVerified())) || s.crossSigningId); - }); - return ret; - }); - } - /** - * Schedules sending all keys waiting to be sent to the backup, if not already - * scheduled. Retries if necessary. - * - * @param maxDelay - Maximum delay to wait in ms. 0 means no delay. - */ - scheduleKeyBackupSend(maxDelay = 10000) { - return __awaiter(this, void 0, void 0, function* () { - if (this.sendingBackups) - return; - this.sendingBackups = true; - try { - // wait between 0 and `maxDelay` seconds, to avoid backup - // requests from different clients hitting the server all at - // the same time when a new key is sent - const delay = Math.random() * maxDelay; - yield (0, utils_1.sleep)(delay); - let numFailures = 0; // number of consecutive failures - for (;;) { - if (!this.algorithm) { - return; - } - try { - const numBackedUp = yield this.backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST); - if (numBackedUp === 0) { - // no sessions left needing backup: we're done - return; - } - numFailures = 0; - } - catch (err) { - numFailures++; - logger_1.logger.log("Key backup request failed", err); - if (err.data) { - if (err.data.errcode == "M_NOT_FOUND" || - err.data.errcode == "M_WRONG_ROOM_KEYS_VERSION") { - // Re-check key backup status on error, so we can be - // sure to present the current situation when asked. - yield this.checkKeyBackup(); - // Backup version has changed or this backup version - // has been deleted - this.baseApis.crypto.emit(index_1.CryptoEvent.KeyBackupFailed, err.data.errcode); - throw err; - } - } - } - if (numFailures) { - // exponential backoff if we have failures - yield (0, utils_1.sleep)(1000 * Math.pow(2, Math.min(numFailures - 1, 4))); - } - } - } - finally { - this.sendingBackups = false; - } - }); - } - /** - * Take some e2e keys waiting to be backed up and send them - * to the backup. - * - * @param limit - Maximum number of keys to back up - * @returns Number of sessions backed up - */ - backupPendingKeys(limit) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const sessions = yield this.baseApis.crypto.cryptoStore.getSessionsNeedingBackup(limit); - if (!sessions.length) { - return 0; - } - let remaining = yield this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup(); - this.baseApis.crypto.emit(index_1.CryptoEvent.KeyBackupSessionsRemaining, remaining); - const rooms = {}; - for (const session of sessions) { - const roomId = session.sessionData.room_id; - (0, utils_1.safeSet)(rooms, roomId, rooms[roomId] || { sessions: {} }); - const sessionData = this.baseApis.crypto.olmDevice.exportInboundGroupSession(session.senderKey, session.sessionId, session.sessionData); - sessionData.algorithm = olmlib_1.MEGOLM_ALGORITHM; - const forwardedCount = (sessionData.forwarding_curve25519_key_chain || []).length; - const userId = this.baseApis.crypto.deviceList.getUserByIdentityKey(olmlib_1.MEGOLM_ALGORITHM, session.senderKey); - const device = (_a = this.baseApis.crypto.deviceList.getDeviceByIdentityKey(olmlib_1.MEGOLM_ALGORITHM, session.senderKey)) !== null && _a !== void 0 ? _a : undefined; - const verified = this.baseApis.crypto.checkDeviceInfoTrust(userId, device).isVerified(); - (0, utils_1.safeSet)(rooms[roomId]["sessions"], session.sessionId, { - first_message_index: sessionData.first_known_index, - forwarded_count: forwardedCount, - is_verified: verified, - session_data: yield this.algorithm.encryptSession(sessionData), - }); - } - yield this.baseApis.sendKeyBackup(undefined, undefined, this.backupInfo.version, { rooms }); - yield this.baseApis.crypto.cryptoStore.unmarkSessionsNeedingBackup(sessions); - remaining = yield this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup(); - this.baseApis.crypto.emit(index_1.CryptoEvent.KeyBackupSessionsRemaining, remaining); - return sessions.length; - }); - } - backupGroupSession(senderKey, sessionId) { - return __awaiter(this, void 0, void 0, function* () { - yield this.baseApis.crypto.cryptoStore.markSessionsNeedingBackup([ - { - senderKey: senderKey, - sessionId: sessionId, - }, - ]); - if (this.backupInfo) { - // don't wait for this to complete: it will delay so - // happens in the background - this.scheduleKeyBackupSend(); - } - // if this.backupInfo is not set, then the keys will be backed up when - // this.enableKeyBackup is called - }); - } - /** - * Marks all group sessions as needing to be backed up and schedules them to - * upload in the background as soon as possible. - */ - scheduleAllGroupSessionsForBackup() { - return __awaiter(this, void 0, void 0, function* () { - yield this.flagAllGroupSessionsForBackup(); - // Schedule keys to upload in the background as soon as possible. - this.scheduleKeyBackupSend(0 /* maxDelay */); - }); - } - /** - * Marks all group sessions as needing to be backed up without scheduling - * them to upload in the background. - * @returns Promise which resolves to the number of sessions now requiring a backup - * (which will be equal to the number of sessions in the store). - */ - flagAllGroupSessionsForBackup() { - return __awaiter(this, void 0, void 0, function* () { - yield this.baseApis.crypto.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS, indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_BACKUP], (txn) => { - this.baseApis.crypto.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (session) => { - if (session !== null) { - this.baseApis.crypto.cryptoStore.markSessionsNeedingBackup([session], txn); - } - }); - }); - const remaining = yield this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup(); - this.baseApis.emit(index_1.CryptoEvent.KeyBackupSessionsRemaining, remaining); - return remaining; - }); - } - /** - * Counts the number of end to end session keys that are waiting to be backed up - * @returns Promise which resolves to the number of sessions requiring backup - */ - countSessionsNeedingBackup() { - return this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup(); - } -} -exports.BackupManager = BackupManager; -class Curve25519 { - constructor(authData, publicKey, // FIXME: PkEncryption - getKey) { - this.authData = authData; - this.publicKey = publicKey; - this.getKey = getKey; - } - static init(authData, getKey) { - return __awaiter(this, void 0, void 0, function* () { - if (!authData || !("public_key" in authData)) { - throw new Error("auth_data missing required information"); - } - const publicKey = new global.Olm.PkEncryption(); - publicKey.set_recipient_key(authData.public_key); - return new Curve25519(authData, publicKey, getKey); - }); - } - static prepare(key) { - return __awaiter(this, void 0, void 0, function* () { - const decryption = new global.Olm.PkDecryption(); - try { - const authData = {}; - if (!key) { - authData.public_key = decryption.generate_key(); - } - else if (key instanceof Uint8Array) { - authData.public_key = decryption.init_with_private_key(key); - } - else { - const derivation = yield (0, key_passphrase_1.keyFromPassphrase)(key); - authData.private_key_salt = derivation.salt; - authData.private_key_iterations = derivation.iterations; - authData.public_key = decryption.init_with_private_key(derivation.key); - } - const publicKey = new global.Olm.PkEncryption(); - publicKey.set_recipient_key(authData.public_key); - return [decryption.get_private_key(), authData]; - } - finally { - decryption.free(); - } - }); - } - static checkBackupVersion(info) { - if (!("public_key" in info.auth_data)) { - throw new Error("Invalid backup data returned"); - } - } - get untrusted() { - return true; - } - encryptSession(data) { - return __awaiter(this, void 0, void 0, function* () { - const plainText = Object.assign({}, data); - delete plainText.session_id; - delete plainText.room_id; - delete plainText.first_known_index; - return this.publicKey.encrypt(JSON.stringify(plainText)); - }); - } - decryptSessions(sessions) { - return __awaiter(this, void 0, void 0, function* () { - const privKey = yield this.getKey(); - const decryption = new global.Olm.PkDecryption(); - try { - const backupPubKey = decryption.init_with_private_key(privKey); - if (backupPubKey !== this.authData.public_key) { - throw new http_api_1.MatrixError({ errcode: client_1.MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY }); - } - const keys = []; - for (const [sessionId, sessionData] of Object.entries(sessions)) { - try { - const decrypted = JSON.parse(decryption.decrypt(sessionData.session_data.ephemeral, sessionData.session_data.mac, sessionData.session_data.ciphertext)); - decrypted.session_id = sessionId; - keys.push(decrypted); - } - catch (e) { - logger_1.logger.log("Failed to decrypt megolm session from backup", e, sessionData); - } - } - return keys; - } - finally { - decryption.free(); - } - }); - } - keyMatches(key) { - return __awaiter(this, void 0, void 0, function* () { - const decryption = new global.Olm.PkDecryption(); - let pubKey; - try { - pubKey = decryption.init_with_private_key(key); - } - finally { - decryption.free(); - } - return pubKey === this.authData.public_key; - }); - } - free() { - this.publicKey.free(); - } -} -exports.Curve25519 = Curve25519; -Curve25519.algorithmName = "m.megolm_backup.v1.curve25519-aes-sha2"; -function randomBytes(size) { - const buf = new Uint8Array(size); - crypto_1.crypto.getRandomValues(buf); - return buf; -} -const UNSTABLE_MSC3270_NAME = new NamespacedValue_1.UnstableValue("m.megolm_backup.v1.aes-hmac-sha2", "org.matrix.msc3270.v1.aes-hmac-sha2"); -class Aes256 { - constructor(authData, key) { - this.authData = authData; - this.key = key; - } - static init(authData, getKey) { - return __awaiter(this, void 0, void 0, function* () { - if (!authData) { - throw new Error("auth_data missing"); - } - const key = yield getKey(); - if (authData.mac) { - const { mac } = yield (0, aes_1.calculateKeyCheck)(key, authData.iv); - if (authData.mac.replace(/=+$/g, "") !== mac.replace(/=+/g, "")) { - throw new Error("Key does not match"); - } - } - return new Aes256(authData, key); - }); - } - static prepare(key) { - return __awaiter(this, void 0, void 0, function* () { - let outKey; - const authData = {}; - if (!key) { - outKey = randomBytes(32); - } - else if (key instanceof Uint8Array) { - outKey = new Uint8Array(key); - } - else { - const derivation = yield (0, key_passphrase_1.keyFromPassphrase)(key); - authData.private_key_salt = derivation.salt; - authData.private_key_iterations = derivation.iterations; - outKey = derivation.key; - } - const { iv, mac } = yield (0, aes_1.calculateKeyCheck)(outKey); - authData.iv = iv; - authData.mac = mac; - return [outKey, authData]; - }); - } - static checkBackupVersion(info) { - if (!("iv" in info.auth_data && "mac" in info.auth_data)) { - throw new Error("Invalid backup data returned"); - } - } - get untrusted() { - return false; - } - encryptSession(data) { - const plainText = Object.assign({}, data); - delete plainText.session_id; - delete plainText.room_id; - delete plainText.first_known_index; - return (0, aes_1.encryptAES)(JSON.stringify(plainText), this.key, data.session_id); - } - decryptSessions(sessions) { - return __awaiter(this, void 0, void 0, function* () { - const keys = []; - for (const [sessionId, sessionData] of Object.entries(sessions)) { - try { - const decrypted = JSON.parse(yield (0, aes_1.decryptAES)(sessionData.session_data, this.key, sessionId)); - decrypted.session_id = sessionId; - keys.push(decrypted); - } - catch (e) { - logger_1.logger.log("Failed to decrypt megolm session from backup", e, sessionData); - } - } - return keys; - }); - } - keyMatches(key) { - return __awaiter(this, void 0, void 0, function* () { - if (this.authData.mac) { - const { mac } = yield (0, aes_1.calculateKeyCheck)(key, this.authData.iv); - return this.authData.mac.replace(/=+$/g, "") === mac.replace(/=+/g, ""); - } - else { - // if we have no information, we have to assume the key is right - return true; - } - }); - } - free() { - this.key.fill(0); - } -} -exports.Aes256 = Aes256; -Aes256.algorithmName = UNSTABLE_MSC3270_NAME.name; -exports.algorithmsByName = { - [Curve25519.algorithmName]: Curve25519, - [Aes256.algorithmName]: Aes256, -}; -exports.DefaultAlgorithm = Curve25519; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../NamespacedValue":316,"../client":321,"../http-api":367,"../logger":374,"../utils":416,"./aes":331,"./crypto":338,"./index":341,"./key_passphrase":342,"./olmlib":343,"./recoverykey":344,"./store/indexeddb-crypto-store":346}],338:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var _a, _b, _c, _d, _e, _f, _g; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setTextEncoder = exports.setCrypto = exports.TextEncoder = exports.subtleCrypto = exports.crypto = void 0; -const logger_1 = require("../logger"); -exports.crypto = (_a = global.window) === null || _a === void 0 ? void 0 : _a.crypto; -exports.subtleCrypto = (_d = (_c = (_b = global.window) === null || _b === void 0 ? void 0 : _b.crypto) === null || _c === void 0 ? void 0 : _c.subtle) !== null && _d !== void 0 ? _d : (_f = (_e = global.window) === null || _e === void 0 ? void 0 : _e.crypto) === null || _f === void 0 ? void 0 : _f.webkitSubtle; -exports.TextEncoder = (_g = global.window) === null || _g === void 0 ? void 0 : _g.TextEncoder; -/* eslint-disable @typescript-eslint/no-var-requires */ -if (!exports.crypto) { - try { - exports.crypto = require("crypto").webcrypto; - } - catch (e) { - logger_1.logger.error("Failed to load webcrypto", e); - } -} -if (!exports.subtleCrypto) { - exports.subtleCrypto = exports.crypto === null || exports.crypto === void 0 ? void 0 : exports.crypto.subtle; -} -if (!exports.TextEncoder) { - try { - exports.TextEncoder = require("util").TextEncoder; - } - catch (e) { - logger_1.logger.error("Failed to load TextEncoder util", e); - } -} -/* eslint-enable @typescript-eslint/no-var-requires */ -function setCrypto(_crypto) { - var _a; - exports.crypto = _crypto; - exports.subtleCrypto = (_a = _crypto.subtle) !== null && _a !== void 0 ? _a : _crypto.webkitSubtle; -} -exports.setCrypto = setCrypto; -function setTextEncoder(_TextEncoder) { - exports.TextEncoder = _TextEncoder; -} -exports.setTextEncoder = setTextEncoder; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../logger":374,"crypto":78,"util":286}],339:[function(require,module,exports){ -(function (global,Buffer){(function (){ -"use strict"; -/* -Copyright 2020-2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DehydrationManager = exports.DEHYDRATION_ALGORITHM = void 0; -const another_json_1 = __importDefault(require("another-json")); -const olmlib_1 = require("./olmlib"); -const indexeddb_crypto_store_1 = require("../crypto/store/indexeddb-crypto-store"); -const aes_1 = require("./aes"); -const logger_1 = require("../logger"); -const http_api_1 = require("../http-api"); -exports.DEHYDRATION_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle"; -const oneweek = 7 * 24 * 60 * 60 * 1000; -class DehydrationManager { - constructor(crypto) { - this.crypto = crypto; - this.inProgress = false; - this.getDehydrationKeyFromCache(); - } - getDehydrationKeyFromCache() { - return this.crypto.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.crypto.cryptoStore.getSecretStorePrivateKey(txn, (result) => __awaiter(this, void 0, void 0, function* () { - if (result) { - const { key, keyInfo, deviceDisplayName, time } = result; - const pickleKey = Buffer.from(this.crypto.olmDevice.pickleKey); - const decrypted = yield (0, aes_1.decryptAES)(key, pickleKey, exports.DEHYDRATION_ALGORITHM); - this.key = (0, olmlib_1.decodeBase64)(decrypted); - this.keyInfo = keyInfo; - this.deviceDisplayName = deviceDisplayName; - const now = Date.now(); - const delay = Math.max(1, time + oneweek - now); - this.timeoutId = global.setTimeout(this.dehydrateDevice.bind(this), delay); - } - }), "dehydration"); - }); - } - /** set the key, and queue periodic dehydration to the server in the background */ - setKeyAndQueueDehydration(key, keyInfo = {}, deviceDisplayName) { - return __awaiter(this, void 0, void 0, function* () { - const matches = yield this.setKey(key, keyInfo, deviceDisplayName); - if (!matches) { - // start dehydration in the background - this.dehydrateDevice(); - } - }); - } - setKey(key, keyInfo = {}, deviceDisplayName) { - return __awaiter(this, void 0, void 0, function* () { - if (!key) { - // unsetting the key -- cancel any pending dehydration task - if (this.timeoutId) { - global.clearTimeout(this.timeoutId); - this.timeoutId = undefined; - } - // clear storage - yield this.crypto.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.crypto.cryptoStore.storeSecretStorePrivateKey(txn, "dehydration", null); - }); - this.key = undefined; - this.keyInfo = undefined; - return; - } - // Check to see if it's the same key as before. If it's different, - // dehydrate a new device. If it's the same, we can keep the same - // device. (Assume that keyInfo and deviceDisplayName will be the - // same if the key is the same.) - let matches = !!this.key && key.length == this.key.length; - for (let i = 0; matches && i < key.length; i++) { - if (key[i] != this.key[i]) { - matches = false; - } - } - if (!matches) { - this.key = key; - this.keyInfo = keyInfo; - this.deviceDisplayName = deviceDisplayName; - } - return matches; - }); - } - /** returns the device id of the newly created dehydrated device */ - dehydrateDevice() { - return __awaiter(this, void 0, void 0, function* () { - if (this.inProgress) { - logger_1.logger.log("Dehydration already in progress -- not starting new dehydration"); - return; - } - this.inProgress = true; - if (this.timeoutId) { - global.clearTimeout(this.timeoutId); - this.timeoutId = undefined; - } - try { - const pickleKey = Buffer.from(this.crypto.olmDevice.pickleKey); - // update the crypto store with the timestamp - const key = yield (0, aes_1.encryptAES)((0, olmlib_1.encodeBase64)(this.key), pickleKey, exports.DEHYDRATION_ALGORITHM); - yield this.crypto.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.crypto.cryptoStore.storeSecretStorePrivateKey(txn, "dehydration", { - keyInfo: this.keyInfo, - key, - deviceDisplayName: this.deviceDisplayName, - time: Date.now(), - }); - }); - logger_1.logger.log("Attempting to dehydrate device"); - logger_1.logger.log("Creating account"); - // create the account and all the necessary keys - const account = new global.Olm.Account(); - account.create(); - const e2eKeys = JSON.parse(account.identity_keys()); - const maxKeys = account.max_number_of_one_time_keys(); - // FIXME: generate in small batches? - account.generate_one_time_keys(maxKeys / 2); - account.generate_fallback_key(); - const otks = JSON.parse(account.one_time_keys()); - const fallbacks = JSON.parse(account.fallback_key()); - account.mark_keys_as_published(); - // dehydrate the account and store it on the server - const pickledAccount = account.pickle(new Uint8Array(this.key)); - const deviceData = { - algorithm: exports.DEHYDRATION_ALGORITHM, - account: pickledAccount, - }; - if (this.keyInfo.passphrase) { - deviceData.passphrase = this.keyInfo.passphrase; - } - logger_1.logger.log("Uploading account to server"); - // eslint-disable-next-line camelcase - const dehydrateResult = yield this.crypto.baseApis.http.authedRequest(http_api_1.Method.Put, "/dehydrated_device", undefined, { - device_data: deviceData, - initial_device_display_name: this.deviceDisplayName, - }, { - prefix: "/_matrix/client/unstable/org.matrix.msc2697.v2", - }); - // send the keys to the server - const deviceId = dehydrateResult.device_id; - logger_1.logger.log("Preparing device keys", deviceId); - const deviceKeys = { - algorithms: this.crypto.supportedAlgorithms, - device_id: deviceId, - user_id: this.crypto.userId, - keys: { - [`ed25519:${deviceId}`]: e2eKeys.ed25519, - [`curve25519:${deviceId}`]: e2eKeys.curve25519, - }, - }; - const deviceSignature = account.sign(another_json_1.default.stringify(deviceKeys)); - deviceKeys.signatures = { - [this.crypto.userId]: { - [`ed25519:${deviceId}`]: deviceSignature, - }, - }; - if (this.crypto.crossSigningInfo.getId("self_signing")) { - yield this.crypto.crossSigningInfo.signObject(deviceKeys, "self_signing"); - } - logger_1.logger.log("Preparing one-time keys"); - const oneTimeKeys = {}; - for (const [keyId, key] of Object.entries(otks.curve25519)) { - const k = { key }; - const signature = account.sign(another_json_1.default.stringify(k)); - k.signatures = { - [this.crypto.userId]: { - [`ed25519:${deviceId}`]: signature, - }, - }; - oneTimeKeys[`signed_curve25519:${keyId}`] = k; - } - logger_1.logger.log("Preparing fallback keys"); - const fallbackKeys = {}; - for (const [keyId, key] of Object.entries(fallbacks.curve25519)) { - const k = { key, fallback: true }; - const signature = account.sign(another_json_1.default.stringify(k)); - k.signatures = { - [this.crypto.userId]: { - [`ed25519:${deviceId}`]: signature, - }, - }; - fallbackKeys[`signed_curve25519:${keyId}`] = k; - } - logger_1.logger.log("Uploading keys to server"); - yield this.crypto.baseApis.http.authedRequest(http_api_1.Method.Post, "/keys/upload/" + encodeURI(deviceId), undefined, { - "device_keys": deviceKeys, - "one_time_keys": oneTimeKeys, - "org.matrix.msc2732.fallback_keys": fallbackKeys, - }); - logger_1.logger.log("Done dehydrating"); - // dehydrate again in a week - this.timeoutId = global.setTimeout(this.dehydrateDevice.bind(this), oneweek); - return deviceId; - } - finally { - this.inProgress = false; - } - }); - } - stop() { - if (this.timeoutId) { - global.clearTimeout(this.timeoutId); - this.timeoutId = undefined; - } - } -} -exports.DehydrationManager = DehydrationManager; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) - -},{"../crypto/store/indexeddb-crypto-store":346,"../http-api":367,"../logger":374,"./aes":331,"./olmlib":343,"another-json":1,"buffer":68}],340:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DeviceInfo = void 0; -var DeviceVerification; -(function (DeviceVerification) { - DeviceVerification[DeviceVerification["Blocked"] = -1] = "Blocked"; - DeviceVerification[DeviceVerification["Unverified"] = 0] = "Unverified"; - DeviceVerification[DeviceVerification["Verified"] = 1] = "Verified"; -})(DeviceVerification || (DeviceVerification = {})); -/** - * Information about a user's device - */ -class DeviceInfo { - /** - * rehydrate a DeviceInfo from the session store - * - * @param obj - raw object from session store - * @param deviceId - id of the device - * - * @returns new DeviceInfo - */ - static fromStorage(obj, deviceId) { - const res = new DeviceInfo(deviceId); - for (const prop in obj) { - if (obj.hasOwnProperty(prop)) { - // @ts-ignore - this is messy and typescript doesn't like it - res[prop] = obj[prop]; - } - } - return res; - } - /** - * @param deviceId - id of the device - */ - constructor(deviceId) { - this.deviceId = deviceId; - /** list of algorithms supported by this device */ - this.algorithms = []; - /** a map from `: -> ` */ - this.keys = {}; - /** whether the device has been verified/blocked by the user */ - this.verified = DeviceVerification.Unverified; - /** - * whether the user knows of this device's existence - * (useful when warning the user that a user has added new devices) - */ - this.known = false; - /** additional data from the homeserver */ - this.unsigned = {}; - this.signatures = {}; - } - /** - * Prepare a DeviceInfo for JSON serialisation in the session store - * - * @returns deviceinfo with non-serialised members removed - */ - toStorage() { - return { - algorithms: this.algorithms, - keys: this.keys, - verified: this.verified, - known: this.known, - unsigned: this.unsigned, - signatures: this.signatures, - }; - } - /** - * Get the fingerprint for this device (ie, the Ed25519 key) - * - * @returns base64-encoded fingerprint of this device - */ - getFingerprint() { - return this.keys["ed25519:" + this.deviceId]; - } - /** - * Get the identity key for this device (ie, the Curve25519 key) - * - * @returns base64-encoded identity key of this device - */ - getIdentityKey() { - return this.keys["curve25519:" + this.deviceId]; - } - /** - * Get the configured display name for this device, if any - * - * @returns displayname - */ - getDisplayName() { - return this.unsigned.device_display_name || null; - } - /** - * Returns true if this device is blocked - * - * @returns true if blocked - */ - isBlocked() { - return this.verified == DeviceVerification.Blocked; - } - /** - * Returns true if this device is verified - * - * @returns true if verified - */ - isVerified() { - return this.verified == DeviceVerification.Verified; - } - /** - * Returns true if this device is unverified - * - * @returns true if unverified - */ - isUnverified() { - return this.verified == DeviceVerification.Unverified; - } - /** - * Returns true if the user knows about this device's existence - * - * @returns true if known - */ - isKnown() { - return this.known === true; - } -} -exports.DeviceInfo = DeviceInfo; -DeviceInfo.DeviceVerification = { - VERIFIED: DeviceVerification.Verified, - UNVERIFIED: DeviceVerification.Unverified, - BLOCKED: DeviceVerification.Blocked, -}; - -},{}],341:[function(require,module,exports){ -(function (global,Buffer){(function (){ -"use strict"; -/* -Copyright 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2018-2019 New Vector Ltd -Copyright 2019-2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IncomingRoomKeyRequest = exports.fixBackupKey = exports.Crypto = exports.CryptoEvent = exports.isCryptoAvailable = exports.verificationMethods = void 0; -const another_json_1 = __importDefault(require("another-json")); -const uuid_1 = require("uuid"); -const event_1 = require("../@types/event"); -const ReEmitter_1 = require("../ReEmitter"); -const logger_1 = require("../logger"); -const OlmDevice_1 = require("./OlmDevice"); -const olmlib = __importStar(require("./olmlib")); -const DeviceList_1 = require("./DeviceList"); -const deviceinfo_1 = require("./deviceinfo"); -const algorithms = __importStar(require("./algorithms")); -const CrossSigning_1 = require("./CrossSigning"); -const EncryptionSetup_1 = require("./EncryptionSetup"); -const SecretStorage_1 = require("./SecretStorage"); -const OutgoingRoomKeyRequestManager_1 = require("./OutgoingRoomKeyRequestManager"); -const indexeddb_crypto_store_1 = require("./store/indexeddb-crypto-store"); -const QRCode_1 = require("./verification/QRCode"); -const SAS_1 = require("./verification/SAS"); -const key_passphrase_1 = require("./key_passphrase"); -const recoverykey_1 = require("./recoverykey"); -const VerificationRequest_1 = require("./verification/request/VerificationRequest"); -const InRoomChannel_1 = require("./verification/request/InRoomChannel"); -const ToDeviceChannel_1 = require("./verification/request/ToDeviceChannel"); -const IllegalMethod_1 = require("./verification/IllegalMethod"); -const errors_1 = require("../errors"); -const aes_1 = require("./aes"); -const dehydration_1 = require("./dehydration"); -const backup_1 = require("./backup"); -const room_1 = require("../models/room"); -const room_member_1 = require("../models/room-member"); -const event_2 = require("../models/event"); -const client_1 = require("../client"); -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -const room_state_1 = require("../models/room-state"); -const utils_1 = require("../utils"); -const DeviceVerification = deviceinfo_1.DeviceInfo.DeviceVerification; -const defaultVerificationMethods = { - [QRCode_1.ReciprocateQRCode.NAME]: QRCode_1.ReciprocateQRCode, - [SAS_1.SAS.NAME]: SAS_1.SAS, - // These two can't be used for actual verification, but we do - // need to be able to define them here for the verification flows - // to start. - [QRCode_1.SHOW_QR_CODE_METHOD]: IllegalMethod_1.IllegalMethod, - [QRCode_1.SCAN_QR_CODE_METHOD]: IllegalMethod_1.IllegalMethod, -}; -/** - * verification method names - */ -// legacy export identifier -exports.verificationMethods = { - RECIPROCATE_QR_CODE: QRCode_1.ReciprocateQRCode.NAME, - SAS: SAS_1.SAS.NAME, -}; -function isCryptoAvailable() { - return Boolean(global.Olm); -} -exports.isCryptoAvailable = isCryptoAvailable; -const MIN_FORCE_SESSION_INTERVAL_MS = 60 * 60 * 1000; -var CryptoEvent; -(function (CryptoEvent) { - CryptoEvent["DeviceVerificationChanged"] = "deviceVerificationChanged"; - CryptoEvent["UserTrustStatusChanged"] = "userTrustStatusChanged"; - CryptoEvent["UserCrossSigningUpdated"] = "userCrossSigningUpdated"; - CryptoEvent["RoomKeyRequest"] = "crypto.roomKeyRequest"; - CryptoEvent["RoomKeyRequestCancellation"] = "crypto.roomKeyRequestCancellation"; - CryptoEvent["KeyBackupStatus"] = "crypto.keyBackupStatus"; - CryptoEvent["KeyBackupFailed"] = "crypto.keyBackupFailed"; - CryptoEvent["KeyBackupSessionsRemaining"] = "crypto.keyBackupSessionsRemaining"; - CryptoEvent["KeySignatureUploadFailure"] = "crypto.keySignatureUploadFailure"; - CryptoEvent["VerificationRequest"] = "crypto.verification.request"; - CryptoEvent["Warning"] = "crypto.warning"; - CryptoEvent["WillUpdateDevices"] = "crypto.willUpdateDevices"; - CryptoEvent["DevicesUpdated"] = "crypto.devicesUpdated"; - CryptoEvent["KeysChanged"] = "crossSigning.keysChanged"; -})(CryptoEvent = exports.CryptoEvent || (exports.CryptoEvent = {})); -class Crypto extends typed_event_emitter_1.TypedEventEmitter { - /** - * @returns The version of Olm. - */ - static getOlmVersion() { - return OlmDevice_1.OlmDevice.getOlmVersion(); - } - /** - * Cryptography bits - * - * This module is internal to the js-sdk; the public API is via MatrixClient. - * - * @internal - * - * @param baseApis - base matrix api interface - * - * @param userId - The user ID for the local user - * - * @param deviceId - The identifier for this device. - * - * @param clientStore - the MatrixClient data store. - * - * @param cryptoStore - storage for the crypto layer. - * - * @param roomList - An initialised RoomList object - * - * @param verificationMethods - Array of verification methods to use. - * Each element can either be a string from MatrixClient.verificationMethods - * or a class that implements a verification method. - */ - constructor(baseApis, userId, deviceId, clientStore, cryptoStore, roomList, verificationMethods) { - super(); - this.baseApis = baseApis; - this.userId = userId; - this.deviceId = deviceId; - this.clientStore = clientStore; - this.cryptoStore = cryptoStore; - this.roomList = roomList; - this.trustCrossSignedDevices = true; - // the last time we did a check for the number of one-time-keys on the server. - this.lastOneTimeKeyCheck = null; - this.oneTimeKeyCheckInProgress = false; - // EncryptionAlgorithm instance for each room - this.roomEncryptors = new Map(); - // map from algorithm to DecryptionAlgorithm instance, for each room - this.roomDecryptors = new Map(); - this.deviceKeys = {}; // type: key - this.globalBlacklistUnverifiedDevices = false; - this.globalErrorOnUnknownDevices = true; - // list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations - // we received in the current sync. - this.receivedRoomKeyRequests = []; - this.receivedRoomKeyRequestCancellations = []; - // true if we are currently processing received room key requests - this.processingRoomKeyRequests = false; - // controls whether device tracking is delayed - // until calling encryptEvent or trackRoomDevices, - // or done immediately upon enabling room encryption. - this.lazyLoadMembers = false; - // in case lazyLoadMembers is true, - // track if an initial tracking of all the room members - // has happened for a given room. This is delayed - // to avoid loading room members as long as possible. - this.roomDeviceTrackingState = {}; - // The timestamp of the last time we forced establishment - // of a new session for each device, in milliseconds. - // { - // userId: { - // deviceId: 1234567890000, - // }, - // } - // Map: user Id → device Id → timestamp - this.lastNewSessionForced = new utils_1.MapWithDefault(() => new utils_1.MapWithDefault(() => 0)); - // This flag will be unset whilst the client processes a sync response - // so that we don't start requesting keys until we've actually finished - // processing the response. - this.sendKeyRequestsImmediately = false; - /* - * Event handler for DeviceList's userNewDevices event - */ - this.onDeviceListUserCrossSigningUpdated = (userId) => __awaiter(this, void 0, void 0, function* () { - if (userId === this.userId) { - // An update to our own cross-signing key. - // Get the new key first: - const newCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId); - const seenPubkey = newCrossSigning ? newCrossSigning.getId() : null; - const currentPubkey = this.crossSigningInfo.getId(); - const changed = currentPubkey !== seenPubkey; - if (currentPubkey && seenPubkey && !changed) { - // If it's not changed, just make sure everything is up to date - yield this.checkOwnCrossSigningTrust(); - } - else { - // We'll now be in a state where cross-signing on the account is not trusted - // because our locally stored cross-signing keys will not match the ones - // on the server for our account. So we clear our own stored cross-signing keys, - // effectively disabling cross-signing until the user gets verified by the device - // that reset the keys - this.storeTrustedSelfKeys(null); - // emit cross-signing has been disabled - this.emit(CryptoEvent.KeysChanged, {}); - // as the trust for our own user has changed, - // also emit an event for this - this.emit(CryptoEvent.UserTrustStatusChanged, this.userId, this.checkUserTrust(userId)); - } - } - else { - yield this.checkDeviceVerifications(userId); - // Update verified before latch using the current state and save the new - // latch value in the device list store. - const crossSigning = this.deviceList.getStoredCrossSigningForUser(userId); - if (crossSigning) { - crossSigning.updateCrossSigningVerifiedBefore(this.checkUserTrust(userId).isCrossSigningVerified()); - this.deviceList.setRawStoredCrossSigningForUser(userId, crossSigning.toStorage()); - } - this.emit(CryptoEvent.UserTrustStatusChanged, userId, this.checkUserTrust(userId)); - } - }); - this.onMembership = (event, member, oldMembership) => { - try { - this.onRoomMembership(event, member, oldMembership); - } - catch (e) { - logger_1.logger.error("Error handling membership change:", e); - } - }; - this.onToDeviceEvent = (event) => { - try { - logger_1.logger.log(`received to-device ${event.getType()} from: ` + - `${event.getSender()} id: ${event.getContent()[event_1.ToDeviceMessageId]}`); - if (event.getType() == "m.room_key" || event.getType() == "m.forwarded_room_key") { - this.onRoomKeyEvent(event); - } - else if (event.getType() == "m.room_key_request") { - this.onRoomKeyRequestEvent(event); - } - else if (event.getType() === "m.secret.request") { - this.secretStorage.onRequestReceived(event); - } - else if (event.getType() === "m.secret.send") { - this.secretStorage.onSecretReceived(event); - } - else if (event.getType() === "m.room_key.withheld") { - this.onRoomKeyWithheldEvent(event); - } - else if (event.getContent().transaction_id) { - this.onKeyVerificationMessage(event); - } - else if (event.getContent().msgtype === "m.bad.encrypted") { - this.onToDeviceBadEncrypted(event); - } - else if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) { - if (!event.isBeingDecrypted()) { - event.attemptDecryption(this); - } - // once the event has been decrypted, try again - event.once(event_2.MatrixEventEvent.Decrypted, (ev) => { - this.onToDeviceEvent(ev); - }); - } - } - catch (e) { - logger_1.logger.error("Error handling toDeviceEvent:", e); - } - }; - /** - * Handle key verification requests sent as timeline events - * - * @internal - * @param event - the timeline event - * @param room - not used - * @param atStart - not used - * @param removed - not used - * @param whether - this is a live event - */ - this.onTimelineEvent = (event, room, atStart, removed, { liveEvent = true } = {}) => { - if (!InRoomChannel_1.InRoomChannel.validateEvent(event, this.baseApis)) { - return; - } - const createRequest = (event) => { - const channel = new InRoomChannel_1.InRoomChannel(this.baseApis, event.getRoomId()); - return new VerificationRequest_1.VerificationRequest(channel, this.verificationMethods, this.baseApis); - }; - this.handleVerificationEvent(event, this.inRoomVerificationRequests, createRequest, liveEvent); - }; - this.reEmitter = new ReEmitter_1.TypedReEmitter(this); - if (verificationMethods) { - this.verificationMethods = new Map(); - for (const method of verificationMethods) { - if (typeof method === "string") { - if (defaultVerificationMethods[method]) { - this.verificationMethods.set(method, defaultVerificationMethods[method]); - } - } - else if (method["NAME"]) { - this.verificationMethods.set(method["NAME"], method); - } - else { - logger_1.logger.warn(`Excluding unknown verification method ${method}`); - } - } - } - else { - this.verificationMethods = new Map(Object.entries(defaultVerificationMethods)); - } - this.backupManager = new backup_1.BackupManager(baseApis, () => __awaiter(this, void 0, void 0, function* () { - // try to get key from cache - const cachedKey = yield this.getSessionBackupPrivateKey(); - if (cachedKey) { - return cachedKey; - } - // try to get key from secret storage - const storedKey = yield this.getSecret("m.megolm_backup.v1"); - if (storedKey) { - // ensure that the key is in the right format. If not, fix the key and - // store the fixed version - const fixedKey = fixBackupKey(storedKey); - if (fixedKey) { - const keys = yield this.getSecretStorageKey(); - yield this.storeSecret("m.megolm_backup.v1", fixedKey, [keys[0]]); - } - return olmlib.decodeBase64(fixedKey || storedKey); - } - // try to get key from app - if (this.baseApis.cryptoCallbacks && this.baseApis.cryptoCallbacks.getBackupKey) { - return this.baseApis.cryptoCallbacks.getBackupKey(); - } - throw new Error("Unable to get private key"); - })); - this.olmDevice = new OlmDevice_1.OlmDevice(cryptoStore); - this.deviceList = new DeviceList_1.DeviceList(baseApis, cryptoStore, this.olmDevice); - // XXX: This isn't removed at any point, but then none of the event listeners - // this class sets seem to be removed at any point... :/ - this.deviceList.on(CryptoEvent.UserCrossSigningUpdated, this.onDeviceListUserCrossSigningUpdated); - this.reEmitter.reEmit(this.deviceList, [CryptoEvent.DevicesUpdated, CryptoEvent.WillUpdateDevices]); - this.supportedAlgorithms = Array.from(algorithms.DECRYPTION_CLASSES.keys()); - this.outgoingRoomKeyRequestManager = new OutgoingRoomKeyRequestManager_1.OutgoingRoomKeyRequestManager(baseApis, this.deviceId, this.cryptoStore); - this.toDeviceVerificationRequests = new ToDeviceChannel_1.ToDeviceRequests(); - this.inRoomVerificationRequests = new InRoomChannel_1.InRoomRequests(); - const cryptoCallbacks = this.baseApis.cryptoCallbacks || {}; - const cacheCallbacks = (0, CrossSigning_1.createCryptoStoreCacheCallbacks)(cryptoStore, this.olmDevice); - this.crossSigningInfo = new CrossSigning_1.CrossSigningInfo(userId, cryptoCallbacks, cacheCallbacks); - // Yes, we pass the client twice here: see SecretStorage - this.secretStorage = new SecretStorage_1.SecretStorage(baseApis, cryptoCallbacks, baseApis); - this.dehydrationManager = new dehydration_1.DehydrationManager(this); - // Assuming no app-supplied callback, default to getting from SSSS. - if (!cryptoCallbacks.getCrossSigningKey && cryptoCallbacks.getSecretStorageKey) { - cryptoCallbacks.getCrossSigningKey = (type) => __awaiter(this, void 0, void 0, function* () { - return CrossSigning_1.CrossSigningInfo.getFromSecretStorage(type, this.secretStorage); - }); - } - } - /** - * Initialise the crypto module so that it is ready for use - * - * Returns a promise which resolves once the crypto module is ready for use. - * - * @param exportedOlmDevice - (Optional) data from exported device - * that must be re-created. - */ - init({ exportedOlmDevice, pickleKey } = {}) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log("Crypto: initialising Olm..."); - yield global.Olm.init(); - logger_1.logger.log(exportedOlmDevice - ? "Crypto: initialising Olm device from exported device..." - : "Crypto: initialising Olm device..."); - yield this.olmDevice.init({ fromExportedDevice: exportedOlmDevice, pickleKey }); - logger_1.logger.log("Crypto: loading device list..."); - yield this.deviceList.load(); - // build our device keys: these will later be uploaded - this.deviceKeys["ed25519:" + this.deviceId] = this.olmDevice.deviceEd25519Key; - this.deviceKeys["curve25519:" + this.deviceId] = this.olmDevice.deviceCurve25519Key; - logger_1.logger.log("Crypto: fetching own devices..."); - let myDevices = this.deviceList.getRawStoredDevicesForUser(this.userId); - if (!myDevices) { - myDevices = {}; - } - if (!myDevices[this.deviceId]) { - // add our own deviceinfo to the cryptoStore - logger_1.logger.log("Crypto: adding this device to the store..."); - const deviceInfo = { - keys: this.deviceKeys, - algorithms: this.supportedAlgorithms, - verified: DeviceVerification.VERIFIED, - known: true, - }; - myDevices[this.deviceId] = deviceInfo; - this.deviceList.storeDevicesForUser(this.userId, myDevices); - this.deviceList.saveIfDirty(); - } - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.cryptoStore.getCrossSigningKeys(txn, (keys) => { - // can be an empty object after resetting cross-signing keys, see storeTrustedSelfKeys - if (keys && Object.keys(keys).length !== 0) { - logger_1.logger.log("Loaded cross-signing public keys from crypto store"); - this.crossSigningInfo.setKeys(keys); - } - }); - }); - // make sure we are keeping track of our own devices - // (this is important for key backups & things) - this.deviceList.startTrackingDeviceList(this.userId); - logger_1.logger.log("Crypto: checking for key backup..."); - this.backupManager.checkAndStart(); - }); - } - /** - * Whether to trust a others users signatures of their devices. - * If false, devices will only be considered 'verified' if we have - * verified that device individually (effectively disabling cross-signing). - * - * Default: true - * - * @returns True if trusting cross-signed devices - */ - getCryptoTrustCrossSignedDevices() { - return this.trustCrossSignedDevices; - } - /** - * See getCryptoTrustCrossSignedDevices - - * This may be set before initCrypto() is called to ensure no races occur. - * - * @param val - True to trust cross-signed devices - */ - setCryptoTrustCrossSignedDevices(val) { - this.trustCrossSignedDevices = val; - for (const userId of this.deviceList.getKnownUserIds()) { - const devices = this.deviceList.getRawStoredDevicesForUser(userId); - for (const deviceId of Object.keys(devices)) { - const deviceTrust = this.checkDeviceTrust(userId, deviceId); - // If the device is locally verified then isVerified() is always true, - // so this will only have caused the value to change if the device is - // cross-signing verified but not locally verified - if (!deviceTrust.isLocallyVerified() && deviceTrust.isCrossSigningVerified()) { - const deviceObj = this.deviceList.getStoredDevice(userId, deviceId); - this.emit(CryptoEvent.DeviceVerificationChanged, userId, deviceId, deviceObj); - } - } - } - } - /** - * Create a recovery key from a user-supplied passphrase. - * - * @param password - Passphrase string that can be entered by the user - * when restoring the backup as an alternative to entering the recovery key. - * Optional. - * @returns Object with public key metadata, encoded private - * recovery key which should be disposed of after displaying to the user, - * and raw private key to avoid round tripping if needed. - */ - createRecoveryKeyFromPassphrase(password) { - return __awaiter(this, void 0, void 0, function* () { - const decryption = new global.Olm.PkDecryption(); - try { - const keyInfo = {}; - if (password) { - const derivation = yield (0, key_passphrase_1.keyFromPassphrase)(password); - keyInfo.passphrase = { - algorithm: "m.pbkdf2", - iterations: derivation.iterations, - salt: derivation.salt, - }; - keyInfo.pubkey = decryption.init_with_private_key(derivation.key); - } - else { - keyInfo.pubkey = decryption.generate_key(); - } - const privateKey = decryption.get_private_key(); - const encodedPrivateKey = (0, recoverykey_1.encodeRecoveryKey)(privateKey); - return { - keyInfo: keyInfo, - encodedPrivateKey, - privateKey, - }; - } - finally { - decryption === null || decryption === void 0 ? void 0 : decryption.free(); - } - }); - } - /** - * Checks if the user has previously published cross-signing keys - * - * This means downloading the devicelist for the user and checking if the list includes - * the cross-signing pseudo-device. - * - * @internal - */ - userHasCrossSigningKeys() { - return __awaiter(this, void 0, void 0, function* () { - yield this.downloadKeys([this.userId]); - return this.deviceList.getStoredCrossSigningForUser(this.userId) !== null; - }); - } - /** - * Checks whether cross signing: - * - is enabled on this account and trusted by this device - * - has private keys either cached locally or stored in secret storage - * - * If this function returns false, bootstrapCrossSigning() can be used - * to fix things such that it returns true. That is to say, after - * bootstrapCrossSigning() completes successfully, this function should - * return true. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @returns True if cross-signing is ready to be used on this device - */ - isCrossSigningReady() { - return __awaiter(this, void 0, void 0, function* () { - const publicKeysOnDevice = this.crossSigningInfo.getId(); - const privateKeysExistSomewhere = (yield this.crossSigningInfo.isStoredInKeyCache()) || - (yield this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage)); - return !!(publicKeysOnDevice && privateKeysExistSomewhere); - }); - } - /** - * Checks whether secret storage: - * - is enabled on this account - * - is storing cross-signing private keys - * - is storing session backup key (if enabled) - * - * If this function returns false, bootstrapSecretStorage() can be used - * to fix things such that it returns true. That is to say, after - * bootstrapSecretStorage() completes successfully, this function should - * return true. - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @returns True if secret storage is ready to be used on this device - */ - isSecretStorageReady() { - return __awaiter(this, void 0, void 0, function* () { - const secretStorageKeyInAccount = yield this.secretStorage.hasKey(); - const privateKeysInStorage = yield this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage); - const sessionBackupInStorage = !this.backupManager.getKeyBackupEnabled() || (yield this.baseApis.isKeyBackupKeyStored()); - return !!(secretStorageKeyInAccount && privateKeysInStorage && sessionBackupInStorage); - }); - } - /** - * Bootstrap cross-signing by creating keys if needed. If everything is already - * set up, then no changes are made, so this is safe to run to ensure - * cross-signing is ready for use. - * - * This function: - * - creates new cross-signing keys if they are not found locally cached nor in - * secret storage (if it has been setup) - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * - * @param authUploadDeviceSigningKeys - Function - * called to await an interactive auth flow when uploading device signing keys. - * @param setupNewCrossSigning - Optional. Reset even if keys - * already exist. - * Args: - * A function that makes the request requiring auth. Receives the - * auth data as an object. Can be called multiple times, first with an empty - * authDict, to obtain the flows. - */ - bootstrapCrossSigning({ authUploadDeviceSigningKeys, setupNewCrossSigning, } = {}) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log("Bootstrapping cross-signing"); - const delegateCryptoCallbacks = this.baseApis.cryptoCallbacks; - const builder = new EncryptionSetup_1.EncryptionSetupBuilder(this.baseApis.store.accountData, delegateCryptoCallbacks); - const crossSigningInfo = new CrossSigning_1.CrossSigningInfo(this.userId, builder.crossSigningCallbacks, builder.crossSigningCallbacks); - // Reset the cross-signing keys - const resetCrossSigning = () => __awaiter(this, void 0, void 0, function* () { - crossSigningInfo.resetKeys(); - // Sign master key with device key - yield this.signObject(crossSigningInfo.keys.master); - // Store auth flow helper function, as we need to call it when uploading - // to ensure we handle auth errors properly. - builder.addCrossSigningKeys(authUploadDeviceSigningKeys, crossSigningInfo.keys); - // Cross-sign own device - const device = this.deviceList.getStoredDevice(this.userId, this.deviceId); - const deviceSignature = yield crossSigningInfo.signDevice(this.userId, device); - builder.addKeySignature(this.userId, this.deviceId, deviceSignature); - // Sign message key backup with cross-signing master key - if (this.backupManager.backupInfo) { - yield crossSigningInfo.signObject(this.backupManager.backupInfo.auth_data, "master"); - builder.addSessionBackup(this.backupManager.backupInfo); - } - }); - const publicKeysOnDevice = this.crossSigningInfo.getId(); - const privateKeysInCache = yield this.crossSigningInfo.isStoredInKeyCache(); - const privateKeysInStorage = yield this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage); - const privateKeysExistSomewhere = privateKeysInCache || privateKeysInStorage; - // Log all relevant state for easier parsing of debug logs. - logger_1.logger.log({ - setupNewCrossSigning, - publicKeysOnDevice, - privateKeysInCache, - privateKeysInStorage, - privateKeysExistSomewhere, - }); - if (!privateKeysExistSomewhere || setupNewCrossSigning) { - logger_1.logger.log("Cross-signing private keys not found locally or in secret storage, " + "creating new keys"); - // If a user has multiple devices, it important to only call bootstrap - // as part of some UI flow (and not silently during startup), as they - // may have setup cross-signing on a platform which has not saved keys - // to secret storage, and this would reset them. In such a case, you - // should prompt the user to verify any existing devices first (and - // request private keys from those devices) before calling bootstrap. - yield resetCrossSigning(); - } - else if (publicKeysOnDevice && privateKeysInCache) { - logger_1.logger.log("Cross-signing public keys trusted and private keys found locally"); - } - else if (privateKeysInStorage) { - logger_1.logger.log("Cross-signing private keys not found locally, but they are available " + - "in secret storage, reading storage and caching locally"); - yield this.checkOwnCrossSigningTrust({ - allowPrivateKeyRequests: true, - }); - } - // Assuming no app-supplied callback, default to storing new private keys in - // secret storage if it exists. If it does not, it is assumed this will be - // done as part of setting up secret storage later. - const crossSigningPrivateKeys = builder.crossSigningCallbacks.privateKeys; - if (crossSigningPrivateKeys.size && !this.baseApis.cryptoCallbacks.saveCrossSigningKeys) { - const secretStorage = new SecretStorage_1.SecretStorage(builder.accountDataClientAdapter, builder.ssssCryptoCallbacks, undefined); - if (yield secretStorage.hasKey()) { - logger_1.logger.log("Storing new cross-signing private keys in secret storage"); - // This is writing to in-memory account data in - // builder.accountDataClientAdapter so won't fail - yield CrossSigning_1.CrossSigningInfo.storeInSecretStorage(crossSigningPrivateKeys, secretStorage); - } - } - const operation = builder.buildOperation(); - yield operation.apply(this); - // This persists private keys and public keys as trusted, - // only do this if apply succeeded for now as retry isn't in place yet - yield builder.persist(this); - logger_1.logger.log("Cross-signing ready"); - }); - } - /** - * Bootstrap Secure Secret Storage if needed by creating a default key. If everything is - * already set up, then no changes are made, so this is safe to run to ensure secret - * storage is ready for use. - * - * This function - * - creates a new Secure Secret Storage key if no default key exists - * - if a key backup exists, it is migrated to store the key in the Secret - * Storage - * - creates a backup if none exists, and one is requested - * - migrates Secure Secret Storage to use the latest algorithm, if an outdated - * algorithm is found - * - * The Secure Secret Storage API is currently UNSTABLE and may change without notice. - * - * @param createSecretStorageKey - Optional. Function - * called to await a secret storage key creation flow. - * Returns a Promise which resolves to an object with public key metadata, encoded private - * recovery key which should be disposed of after displaying to the user, - * and raw private key to avoid round tripping if needed. - * @param keyBackupInfo - The current key backup object. If passed, - * the passphrase and recovery key from this backup will be used. - * @param setupNewKeyBackup - If true, a new key backup version will be - * created and the private key stored in the new SSSS store. Ignored if keyBackupInfo - * is supplied. - * @param setupNewSecretStorage - Optional. Reset even if keys already exist. - * @param getKeyBackupPassphrase - Optional. Function called to get the user's - * current key backup passphrase. Should return a promise that resolves with a Buffer - * containing the key, or rejects if the key cannot be obtained. - * Returns: - * A promise which resolves to key creation data for - * SecretStorage#addKey: an object with `passphrase` etc fields. - */ - // TODO this does not resolve with what it says it does - bootstrapSecretStorage({ createSecretStorageKey = () => __awaiter(this, void 0, void 0, function* () { return ({}); }), keyBackupInfo, setupNewKeyBackup, setupNewSecretStorage, getKeyBackupPassphrase, } = {}) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log("Bootstrapping Secure Secret Storage"); - const delegateCryptoCallbacks = this.baseApis.cryptoCallbacks; - const builder = new EncryptionSetup_1.EncryptionSetupBuilder(this.baseApis.store.accountData, delegateCryptoCallbacks); - const secretStorage = new SecretStorage_1.SecretStorage(builder.accountDataClientAdapter, builder.ssssCryptoCallbacks, undefined); - // the ID of the new SSSS key, if we create one - let newKeyId = null; - // create a new SSSS key and set it as default - const createSSSS = (opts, privateKey) => __awaiter(this, void 0, void 0, function* () { - if (privateKey) { - opts.key = privateKey; - } - const { keyId, keyInfo } = yield secretStorage.addKey(SecretStorage_1.SECRET_STORAGE_ALGORITHM_V1_AES, opts); - if (privateKey) { - // make the private key available to encrypt 4S secrets - builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey); - } - yield secretStorage.setDefaultKeyId(keyId); - return keyId; - }); - const ensureCanCheckPassphrase = (keyId, keyInfo) => __awaiter(this, void 0, void 0, function* () { - var _a, _b; - if (!keyInfo.mac) { - const key = yield ((_b = (_a = this.baseApis.cryptoCallbacks).getSecretStorageKey) === null || _b === void 0 ? void 0 : _b.call(_a, { keys: { [keyId]: keyInfo } }, "")); - if (key) { - const privateKey = key[1]; - builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey); - const { iv, mac } = yield (0, aes_1.calculateKeyCheck)(privateKey); - keyInfo.iv = iv; - keyInfo.mac = mac; - yield builder.setAccountData(`m.secret_storage.key.${keyId}`, keyInfo); - } - } - }); - const signKeyBackupWithCrossSigning = (keyBackupAuthData) => __awaiter(this, void 0, void 0, function* () { - if (this.crossSigningInfo.getId() && (yield this.crossSigningInfo.isStoredInKeyCache("master"))) { - try { - logger_1.logger.log("Adding cross-signing signature to key backup"); - yield this.crossSigningInfo.signObject(keyBackupAuthData, "master"); - } - catch (e) { - // This step is not critical (just helpful), so we catch here - // and continue if it fails. - logger_1.logger.error("Signing key backup with cross-signing keys failed", e); - } - } - else { - logger_1.logger.warn("Cross-signing keys not available, skipping signature on key backup"); - } - }); - const oldSSSSKey = yield this.getSecretStorageKey(); - const [oldKeyId, oldKeyInfo] = oldSSSSKey || [null, null]; - const storageExists = !setupNewSecretStorage && oldKeyInfo && oldKeyInfo.algorithm === SecretStorage_1.SECRET_STORAGE_ALGORITHM_V1_AES; - // Log all relevant state for easier parsing of debug logs. - logger_1.logger.log({ - keyBackupInfo, - setupNewKeyBackup, - setupNewSecretStorage, - storageExists, - oldKeyInfo, - }); - if (!storageExists && !keyBackupInfo) { - // either we don't have anything, or we've been asked to restart - // from scratch - logger_1.logger.log("Secret storage does not exist, creating new storage key"); - // if we already have a usable default SSSS key and aren't resetting - // SSSS just use it. otherwise, create a new one - // Note: we leave the old SSSS key in place: there could be other - // secrets using it, in theory. We could move them to the new key but a) - // that would mean we'd need to prompt for the old passphrase, and b) - // it's not clear that would be the right thing to do anyway. - const { keyInfo = {}, privateKey } = yield createSecretStorageKey(); - newKeyId = yield createSSSS(keyInfo, privateKey); - } - else if (!storageExists && keyBackupInfo) { - // we have an existing backup, but no SSSS - logger_1.logger.log("Secret storage does not exist, using key backup key"); - // if we have the backup key already cached, use it; otherwise use the - // callback to prompt for the key - const backupKey = (yield this.getSessionBackupPrivateKey()) || (yield (getKeyBackupPassphrase === null || getKeyBackupPassphrase === void 0 ? void 0 : getKeyBackupPassphrase())); - // create a new SSSS key and use the backup key as the new SSSS key - const opts = {}; - if (keyBackupInfo.auth_data.private_key_salt && keyBackupInfo.auth_data.private_key_iterations) { - // FIXME: ??? - opts.passphrase = { - algorithm: "m.pbkdf2", - iterations: keyBackupInfo.auth_data.private_key_iterations, - salt: keyBackupInfo.auth_data.private_key_salt, - bits: 256, - }; - } - newKeyId = yield createSSSS(opts, backupKey); - // store the backup key in secret storage - yield secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(backupKey), [newKeyId]); - // The backup is trusted because the user provided the private key. - // Sign the backup with the cross-signing key so the key backup can - // be trusted via cross-signing. - yield signKeyBackupWithCrossSigning(keyBackupInfo.auth_data); - builder.addSessionBackup(keyBackupInfo); - } - else { - // 4S is already set up - logger_1.logger.log("Secret storage exists"); - if (oldKeyInfo && oldKeyInfo.algorithm === SecretStorage_1.SECRET_STORAGE_ALGORITHM_V1_AES) { - // make sure that the default key has the information needed to - // check the passphrase - yield ensureCanCheckPassphrase(oldKeyId, oldKeyInfo); - } - } - // If we have cross-signing private keys cached, store them in secret - // storage if they are not there already. - if (!this.baseApis.cryptoCallbacks.saveCrossSigningKeys && - (yield this.isCrossSigningReady()) && - (newKeyId || !(yield this.crossSigningInfo.isStoredInSecretStorage(secretStorage)))) { - logger_1.logger.log("Copying cross-signing private keys from cache to secret storage"); - const crossSigningPrivateKeys = yield this.crossSigningInfo.getCrossSigningKeysFromCache(); - // This is writing to in-memory account data in - // builder.accountDataClientAdapter so won't fail - yield CrossSigning_1.CrossSigningInfo.storeInSecretStorage(crossSigningPrivateKeys, secretStorage); - } - if (setupNewKeyBackup && !keyBackupInfo) { - logger_1.logger.log("Creating new message key backup version"); - const info = yield this.baseApis.prepareKeyBackupVersion(null /* random key */, - // don't write to secret storage, as it will write to this.secretStorage. - // Here, we want to capture all the side-effects of bootstrapping, - // and want to write to the local secretStorage object - { secureSecretStorage: false }); - // write the key ourselves to 4S - const privateKey = (0, recoverykey_1.decodeRecoveryKey)(info.recovery_key); - yield secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(privateKey)); - // create keyBackupInfo object to add to builder - const data = { - algorithm: info.algorithm, - auth_data: info.auth_data, - }; - // Sign with cross-signing master key - yield signKeyBackupWithCrossSigning(data.auth_data); - // sign with the device fingerprint - yield this.signObject(data.auth_data); - builder.addSessionBackup(data); - } - // Cache the session backup key - const sessionBackupKey = yield secretStorage.get("m.megolm_backup.v1"); - if (sessionBackupKey) { - logger_1.logger.info("Got session backup key from secret storage: caching"); - // fix up the backup key if it's in the wrong format, and replace - // in secret storage - const fixedBackupKey = fixBackupKey(sessionBackupKey); - if (fixedBackupKey) { - const keyId = newKeyId || oldKeyId; - yield secretStorage.store("m.megolm_backup.v1", fixedBackupKey, keyId ? [keyId] : null); - } - const decodedBackupKey = new Uint8Array(olmlib.decodeBase64(fixedBackupKey || sessionBackupKey)); - builder.addSessionBackupPrivateKeyToCache(decodedBackupKey); - } - else if (this.backupManager.getKeyBackupEnabled()) { - // key backup is enabled but we don't have a session backup key in SSSS: see if we have one in - // the cache or the user can provide one, and if so, write it to SSSS - const backupKey = (yield this.getSessionBackupPrivateKey()) || (yield (getKeyBackupPassphrase === null || getKeyBackupPassphrase === void 0 ? void 0 : getKeyBackupPassphrase())); - if (!backupKey) { - // This will require user intervention to recover from since we don't have the key - // backup key anywhere. The user should probably just set up a new key backup and - // the key for the new backup will be stored. If we hit this scenario in the wild - // with any frequency, we should do more than just log an error. - logger_1.logger.error("Key backup is enabled but couldn't get key backup key!"); - return; - } - logger_1.logger.info("Got session backup key from cache/user that wasn't in SSSS: saving to SSSS"); - yield secretStorage.store("m.megolm_backup.v1", olmlib.encodeBase64(backupKey)); - } - const operation = builder.buildOperation(); - yield operation.apply(this); - // this persists private keys and public keys as trusted, - // only do this if apply succeeded for now as retry isn't in place yet - yield builder.persist(this); - logger_1.logger.log("Secure Secret Storage ready"); - }); - } - addSecretStorageKey(algorithm, opts, keyID) { - return this.secretStorage.addKey(algorithm, opts, keyID); - } - hasSecretStorageKey(keyID) { - return this.secretStorage.hasKey(keyID); - } - getSecretStorageKey(keyID) { - return this.secretStorage.getKey(keyID); - } - storeSecret(name, secret, keys) { - return this.secretStorage.store(name, secret, keys); - } - getSecret(name) { - return this.secretStorage.get(name); - } - isSecretStored(name) { - return this.secretStorage.isStored(name); - } - requestSecret(name, devices) { - if (!devices) { - devices = Object.keys(this.deviceList.getRawStoredDevicesForUser(this.userId)); - } - return this.secretStorage.request(name, devices); - } - getDefaultSecretStorageKeyId() { - return this.secretStorage.getDefaultKeyId(); - } - setDefaultSecretStorageKeyId(k) { - return this.secretStorage.setDefaultKeyId(k); - } - checkSecretStorageKey(key, info) { - return this.secretStorage.checkKey(key, info); - } - /** - * Checks that a given secret storage private key matches a given public key. - * This can be used by the getSecretStorageKey callback to verify that the - * private key it is about to supply is the one that was requested. - * - * @param privateKey - The private key - * @param expectedPublicKey - The public key - * @returns true if the key matches, otherwise false - */ - checkSecretStoragePrivateKey(privateKey, expectedPublicKey) { - let decryption = null; - try { - decryption = new global.Olm.PkDecryption(); - const gotPubkey = decryption.init_with_private_key(privateKey); - // make sure it agrees with the given pubkey - return gotPubkey === expectedPublicKey; - } - finally { - decryption === null || decryption === void 0 ? void 0 : decryption.free(); - } - } - /** - * Fetches the backup private key, if cached - * @returns the key, if any, or null - */ - getSessionBackupPrivateKey() { - return __awaiter(this, void 0, void 0, function* () { - let key = yield new Promise((resolve) => { - // TODO types - this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.cryptoStore.getSecretStorePrivateKey(txn, resolve, "m.megolm_backup.v1"); - }); - }); - // make sure we have a Uint8Array, rather than a string - if (key && typeof key === "string") { - key = new Uint8Array(olmlib.decodeBase64(fixBackupKey(key) || key)); - yield this.storeSessionBackupPrivateKey(key); - } - if (key && key.ciphertext) { - const pickleKey = Buffer.from(this.olmDevice.pickleKey); - const decrypted = yield (0, aes_1.decryptAES)(key, pickleKey, "m.megolm_backup.v1"); - key = olmlib.decodeBase64(decrypted); - } - return key; - }); - } - /** - * Stores the session backup key to the cache - * @param key - the private key - * @returns a promise so you can catch failures - */ - storeSessionBackupPrivateKey(key) { - return __awaiter(this, void 0, void 0, function* () { - if (!(key instanceof Uint8Array)) { - // eslint-disable-next-line @typescript-eslint/no-base-to-string - throw new Error(`storeSessionBackupPrivateKey expects Uint8Array, got ${key}`); - } - const pickleKey = Buffer.from(this.olmDevice.pickleKey); - const encryptedKey = yield (0, aes_1.encryptAES)(olmlib.encodeBase64(key), pickleKey, "m.megolm_backup.v1"); - return this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.cryptoStore.storeSecretStorePrivateKey(txn, "m.megolm_backup.v1", encryptedKey); - }); - }); - } - /** - * Checks that a given cross-signing private key matches a given public key. - * This can be used by the getCrossSigningKey callback to verify that the - * private key it is about to supply is the one that was requested. - * - * @param privateKey - The private key - * @param expectedPublicKey - The public key - * @returns true if the key matches, otherwise false - */ - checkCrossSigningPrivateKey(privateKey, expectedPublicKey) { - let signing = null; - try { - signing = new global.Olm.PkSigning(); - const gotPubkey = signing.init_with_seed(privateKey); - // make sure it agrees with the given pubkey - return gotPubkey === expectedPublicKey; - } - finally { - signing === null || signing === void 0 ? void 0 : signing.free(); - } - } - /** - * Run various follow-up actions after cross-signing keys have changed locally - * (either by resetting the keys for the account or by getting them from secret - * storage), such as signing the current device, upgrading device - * verifications, etc. - */ - afterCrossSigningLocalKeyChange() { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info("Starting cross-signing key change post-processing"); - // sign the current device with the new key, and upload to the server - const device = this.deviceList.getStoredDevice(this.userId, this.deviceId); - const signedDevice = yield this.crossSigningInfo.signDevice(this.userId, device); - logger_1.logger.info(`Starting background key sig upload for ${this.deviceId}`); - const upload = ({ shouldEmit = false }) => { - return this.baseApis - .uploadKeySignatures({ - [this.userId]: { - [this.deviceId]: signedDevice, - }, - }) - .then((response) => { - const { failures } = response || {}; - if (Object.keys(failures || []).length > 0) { - if (shouldEmit) { - this.baseApis.emit(CryptoEvent.KeySignatureUploadFailure, failures, "afterCrossSigningLocalKeyChange", upload); - } - throw new errors_1.KeySignatureUploadError("Key upload failed", { failures }); - } - logger_1.logger.info(`Finished background key sig upload for ${this.deviceId}`); - }) - .catch((e) => { - logger_1.logger.error(`Error during background key sig upload for ${this.deviceId}`, e); - }); - }; - upload({ shouldEmit: true }); - const shouldUpgradeCb = this.baseApis.cryptoCallbacks.shouldUpgradeDeviceVerifications; - if (shouldUpgradeCb) { - logger_1.logger.info("Starting device verification upgrade"); - // Check all users for signatures if upgrade callback present - // FIXME: do this in batches - const users = {}; - for (const [userId, crossSigningInfo] of Object.entries(this.deviceList.crossSigningInfo)) { - const upgradeInfo = yield this.checkForDeviceVerificationUpgrade(userId, CrossSigning_1.CrossSigningInfo.fromStorage(crossSigningInfo, userId)); - if (upgradeInfo) { - users[userId] = upgradeInfo; - } - } - if (Object.keys(users).length > 0) { - logger_1.logger.info(`Found ${Object.keys(users).length} verif users to upgrade`); - try { - const usersToUpgrade = yield shouldUpgradeCb({ users: users }); - if (usersToUpgrade) { - for (const userId of usersToUpgrade) { - if (userId in users) { - yield this.baseApis.setDeviceVerified(userId, users[userId].crossSigningInfo.getId()); - } - } - } - } - catch (e) { - logger_1.logger.log("shouldUpgradeDeviceVerifications threw an error: not upgrading", e); - } - } - logger_1.logger.info("Finished device verification upgrade"); - } - logger_1.logger.info("Finished cross-signing key change post-processing"); - }); - } - /** - * Check if a user's cross-signing key is a candidate for upgrading from device - * verification. - * - * @param userId - the user whose cross-signing information is to be checked - * @param crossSigningInfo - the cross-signing information to check - */ - checkForDeviceVerificationUpgrade(userId, crossSigningInfo) { - return __awaiter(this, void 0, void 0, function* () { - // only upgrade if this is the first cross-signing key that we've seen for - // them, and if their cross-signing key isn't already verified - const trustLevel = this.crossSigningInfo.checkUserTrust(crossSigningInfo); - if (crossSigningInfo.firstUse && !trustLevel.isVerified()) { - const devices = this.deviceList.getRawStoredDevicesForUser(userId); - const deviceIds = yield this.checkForValidDeviceSignature(userId, crossSigningInfo.keys.master, devices); - if (deviceIds.length) { - return { - devices: deviceIds.map((deviceId) => deviceinfo_1.DeviceInfo.fromStorage(devices[deviceId], deviceId)), - crossSigningInfo, - }; - } - } - }); - } - /** - * Check if the cross-signing key is signed by a verified device. - * - * @param userId - the user ID whose key is being checked - * @param key - the key that is being checked - * @param devices - the user's devices. Should be a map from device ID - * to device info - */ - checkForValidDeviceSignature(userId, key, devices) { - return __awaiter(this, void 0, void 0, function* () { - const deviceIds = []; - if (devices && key.signatures && key.signatures[userId]) { - for (const signame of Object.keys(key.signatures[userId])) { - const [, deviceId] = signame.split(":", 2); - if (deviceId in devices && devices[deviceId].verified === DeviceVerification.VERIFIED) { - try { - yield olmlib.verifySignature(this.olmDevice, key, userId, deviceId, devices[deviceId].keys[signame]); - deviceIds.push(deviceId); - } - catch (e) { } - } - } - } - return deviceIds; - }); - } - /** - * Get the user's cross-signing key ID. - * - * @param type - The type of key to get the ID of. One of - * "master", "self_signing", or "user_signing". Defaults to "master". - * - * @returns the key ID - */ - getCrossSigningId(type) { - return this.crossSigningInfo.getId(type); - } - /** - * Get the cross signing information for a given user. - * - * @param userId - the user ID to get the cross-signing info for. - * - * @returns the cross signing information for the user. - */ - getStoredCrossSigningForUser(userId) { - return this.deviceList.getStoredCrossSigningForUser(userId); - } - /** - * Check whether a given user is trusted. - * - * @param userId - The ID of the user to check. - * - * @returns - */ - checkUserTrust(userId) { - const userCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId); - if (!userCrossSigning) { - return new CrossSigning_1.UserTrustLevel(false, false, false); - } - return this.crossSigningInfo.checkUserTrust(userCrossSigning); - } - /** - * Check whether a given device is trusted. - * - * @param userId - The ID of the user whose devices is to be checked. - * @param deviceId - The ID of the device to check - * - * @returns - */ - checkDeviceTrust(userId, deviceId) { - const device = this.deviceList.getStoredDevice(userId, deviceId); - return this.checkDeviceInfoTrust(userId, device); - } - /** - * Check whether a given deviceinfo is trusted. - * - * @param userId - The ID of the user whose devices is to be checked. - * @param device - The device info object to check - * - * @returns - */ - checkDeviceInfoTrust(userId, device) { - const trustedLocally = !!(device === null || device === void 0 ? void 0 : device.isVerified()); - const userCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId); - if (device && userCrossSigning) { - // The trustCrossSignedDevices only affects trust of other people's cross-signing - // signatures - const trustCrossSig = this.trustCrossSignedDevices || userId === this.userId; - return this.crossSigningInfo.checkDeviceTrust(userCrossSigning, device, trustedLocally, trustCrossSig); - } - else { - return new CrossSigning_1.DeviceTrustLevel(false, false, trustedLocally, false); - } - } - /** - * Check whether one of our own devices is cross-signed by our - * user's stored keys, regardless of whether we trust those keys yet. - * - * @param deviceId - The ID of the device to check - * - * @returns true if the device is cross-signed - */ - checkIfOwnDeviceCrossSigned(deviceId) { - var _a; - const device = this.deviceList.getStoredDevice(this.userId, deviceId); - if (!device) - return false; - const userCrossSigning = this.deviceList.getStoredCrossSigningForUser(this.userId); - return ((_a = userCrossSigning === null || userCrossSigning === void 0 ? void 0 : userCrossSigning.checkDeviceTrust(userCrossSigning, device, false, true).isCrossSigningVerified()) !== null && _a !== void 0 ? _a : false); - } - /** - * Check the copy of our cross-signing key that we have in the device list and - * see if we can get the private key. If so, mark it as trusted. - */ - checkOwnCrossSigningTrust({ allowPrivateKeyRequests = false, } = {}) { - return __awaiter(this, void 0, void 0, function* () { - const userId = this.userId; - // Before proceeding, ensure our cross-signing public keys have been - // downloaded via the device list. - yield this.downloadKeys([this.userId]); - // Also check which private keys are locally cached. - const crossSigningPrivateKeys = yield this.crossSigningInfo.getCrossSigningKeysFromCache(); - // If we see an update to our own master key, check it against the master - // key we have and, if it matches, mark it as verified - // First, get the new cross-signing info - const newCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId); - if (!newCrossSigning) { - logger_1.logger.error("Got cross-signing update event for user " + userId + " but no new cross-signing information found!"); - return; - } - const seenPubkey = newCrossSigning.getId(); - const masterChanged = this.crossSigningInfo.getId() !== seenPubkey; - const masterExistsNotLocallyCached = newCrossSigning.getId() && !crossSigningPrivateKeys.has("master"); - if (masterChanged) { - logger_1.logger.info("Got new master public key", seenPubkey); - } - if (allowPrivateKeyRequests && (masterChanged || masterExistsNotLocallyCached)) { - logger_1.logger.info("Attempting to retrieve cross-signing master private key"); - let signing = null; - // It's important for control flow that we leave any errors alone for - // higher levels to handle so that e.g. cancelling access properly - // aborts any larger operation as well. - try { - const ret = yield this.crossSigningInfo.getCrossSigningKey("master", seenPubkey); - signing = ret[1]; - logger_1.logger.info("Got cross-signing master private key"); - } - finally { - signing === null || signing === void 0 ? void 0 : signing.free(); - } - } - const oldSelfSigningId = this.crossSigningInfo.getId("self_signing"); - const oldUserSigningId = this.crossSigningInfo.getId("user_signing"); - // Update the version of our keys in our cross-signing object and the local store - this.storeTrustedSelfKeys(newCrossSigning.keys); - const selfSigningChanged = oldSelfSigningId !== newCrossSigning.getId("self_signing"); - const userSigningChanged = oldUserSigningId !== newCrossSigning.getId("user_signing"); - const selfSigningExistsNotLocallyCached = newCrossSigning.getId("self_signing") && !crossSigningPrivateKeys.has("self_signing"); - const userSigningExistsNotLocallyCached = newCrossSigning.getId("user_signing") && !crossSigningPrivateKeys.has("user_signing"); - const keySignatures = {}; - if (selfSigningChanged) { - logger_1.logger.info("Got new self-signing key", newCrossSigning.getId("self_signing")); - } - if (allowPrivateKeyRequests && (selfSigningChanged || selfSigningExistsNotLocallyCached)) { - logger_1.logger.info("Attempting to retrieve cross-signing self-signing private key"); - let signing = null; - try { - const ret = yield this.crossSigningInfo.getCrossSigningKey("self_signing", newCrossSigning.getId("self_signing")); - signing = ret[1]; - logger_1.logger.info("Got cross-signing self-signing private key"); - } - finally { - signing === null || signing === void 0 ? void 0 : signing.free(); - } - const device = this.deviceList.getStoredDevice(this.userId, this.deviceId); - const signedDevice = yield this.crossSigningInfo.signDevice(this.userId, device); - keySignatures[this.deviceId] = signedDevice; - } - if (userSigningChanged) { - logger_1.logger.info("Got new user-signing key", newCrossSigning.getId("user_signing")); - } - if (allowPrivateKeyRequests && (userSigningChanged || userSigningExistsNotLocallyCached)) { - logger_1.logger.info("Attempting to retrieve cross-signing user-signing private key"); - let signing = null; - try { - const ret = yield this.crossSigningInfo.getCrossSigningKey("user_signing", newCrossSigning.getId("user_signing")); - signing = ret[1]; - logger_1.logger.info("Got cross-signing user-signing private key"); - } - finally { - signing === null || signing === void 0 ? void 0 : signing.free(); - } - } - if (masterChanged) { - const masterKey = this.crossSigningInfo.keys.master; - yield this.signObject(masterKey); - const deviceSig = masterKey.signatures[this.userId]["ed25519:" + this.deviceId]; - // Include only the _new_ device signature in the upload. - // We may have existing signatures from deleted devices, which will cause - // the entire upload to fail. - keySignatures[this.crossSigningInfo.getId()] = Object.assign({}, masterKey, { - signatures: { - [this.userId]: { - ["ed25519:" + this.deviceId]: deviceSig, - }, - }, - }); - } - const keysToUpload = Object.keys(keySignatures); - if (keysToUpload.length) { - const upload = ({ shouldEmit = false }) => { - logger_1.logger.info(`Starting background key sig upload for ${keysToUpload}`); - return this.baseApis - .uploadKeySignatures({ [this.userId]: keySignatures }) - .then((response) => { - const { failures } = response || {}; - logger_1.logger.info(`Finished background key sig upload for ${keysToUpload}`); - if (Object.keys(failures || []).length > 0) { - if (shouldEmit) { - this.baseApis.emit(CryptoEvent.KeySignatureUploadFailure, failures, "checkOwnCrossSigningTrust", upload); - } - throw new errors_1.KeySignatureUploadError("Key upload failed", { failures }); - } - }) - .catch((e) => { - logger_1.logger.error(`Error during background key sig upload for ${keysToUpload}`, e); - }); - }; - upload({ shouldEmit: true }); - } - this.emit(CryptoEvent.UserTrustStatusChanged, userId, this.checkUserTrust(userId)); - if (masterChanged) { - this.emit(CryptoEvent.KeysChanged, {}); - yield this.afterCrossSigningLocalKeyChange(); - } - // Now we may be able to trust our key backup - yield this.backupManager.checkKeyBackup(); - // FIXME: if we previously trusted the backup, should we automatically sign - // the backup with the new key (if not already signed)? - }); - } - /** - * Store a set of keys as our own, trusted, cross-signing keys. - * - * @param keys - The new trusted set of keys - */ - storeTrustedSelfKeys(keys) { - return __awaiter(this, void 0, void 0, function* () { - if (keys) { - this.crossSigningInfo.setKeys(keys); - } - else { - this.crossSigningInfo.clearKeys(); - } - yield this.cryptoStore.doTxn("readwrite", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - this.cryptoStore.storeCrossSigningKeys(txn, this.crossSigningInfo.keys); - }); - }); - } - /** - * Check if the master key is signed by a verified device, and if so, prompt - * the application to mark it as verified. - * - * @param userId - the user ID whose key should be checked - */ - checkDeviceVerifications(userId) { - return __awaiter(this, void 0, void 0, function* () { - const shouldUpgradeCb = this.baseApis.cryptoCallbacks.shouldUpgradeDeviceVerifications; - if (!shouldUpgradeCb) { - // Upgrading skipped when callback is not present. - return; - } - logger_1.logger.info(`Starting device verification upgrade for ${userId}`); - if (this.crossSigningInfo.keys.user_signing) { - const crossSigningInfo = this.deviceList.getStoredCrossSigningForUser(userId); - if (crossSigningInfo) { - const upgradeInfo = yield this.checkForDeviceVerificationUpgrade(userId, crossSigningInfo); - if (upgradeInfo) { - const usersToUpgrade = yield shouldUpgradeCb({ - users: { - [userId]: upgradeInfo, - }, - }); - if (usersToUpgrade.includes(userId)) { - yield this.baseApis.setDeviceVerified(userId, crossSigningInfo.getId()); - } - } - } - } - logger_1.logger.info(`Finished device verification upgrade for ${userId}`); - }); - } - /** - */ - enableLazyLoading() { - this.lazyLoadMembers = true; - } - /** - * Tell the crypto module to register for MatrixClient events which it needs to - * listen for - * - * @param eventEmitter - event source where we can register - * for event notifications - */ - registerEventHandlers(eventEmitter) { - eventEmitter.on(room_member_1.RoomMemberEvent.Membership, this.onMembership); - eventEmitter.on(client_1.ClientEvent.ToDeviceEvent, this.onToDeviceEvent); - eventEmitter.on(room_1.RoomEvent.Timeline, this.onTimelineEvent); - eventEmitter.on(event_2.MatrixEventEvent.Decrypted, this.onTimelineEvent); - } - /** - * @deprecated this does nothing and will be removed in a future version - */ - start() { - logger_1.logger.warn("MatrixClient.crypto.start() is deprecated"); - } - /** Stop background processes related to crypto */ - stop() { - this.outgoingRoomKeyRequestManager.stop(); - this.deviceList.stop(); - this.dehydrationManager.stop(); - } - /** - * Get the Ed25519 key for this device - * - * @returns base64-encoded ed25519 key. - */ - getDeviceEd25519Key() { - return this.olmDevice.deviceEd25519Key; - } - /** - * Get the Curve25519 key for this device - * - * @returns base64-encoded curve25519 key. - */ - getDeviceCurve25519Key() { - return this.olmDevice.deviceCurve25519Key; - } - /** - * Set the global override for whether the client should ever send encrypted - * messages to unverified devices. This provides the default for rooms which - * do not specify a value. - * - * @param value - whether to blacklist all unverified devices by default - * - * @deprecated For external code, use {@link MatrixClient#setGlobalBlacklistUnverifiedDevices}. For - * internal code, set {@link MatrixClient#globalBlacklistUnverifiedDevices} directly. - */ - setGlobalBlacklistUnverifiedDevices(value) { - this.globalBlacklistUnverifiedDevices = value; - } - /** - * @returns whether to blacklist all unverified devices by default - * - * @deprecated For external code, use {@link MatrixClient#getGlobalBlacklistUnverifiedDevices}. For - * internal code, reference {@link MatrixClient#globalBlacklistUnverifiedDevices} directly. - */ - getGlobalBlacklistUnverifiedDevices() { - return this.globalBlacklistUnverifiedDevices; - } - /** - * Upload the device keys to the homeserver. - * @returns A promise that will resolve when the keys are uploaded. - */ - uploadDeviceKeys() { - const deviceKeys = { - algorithms: this.supportedAlgorithms, - device_id: this.deviceId, - keys: this.deviceKeys, - user_id: this.userId, - }; - return this.signObject(deviceKeys).then(() => { - return this.baseApis.uploadKeysRequest({ - device_keys: deviceKeys, - }); - }); - } - /** - * Stores the current one_time_key count which will be handled later (in a call of - * onSyncCompleted). The count is e.g. coming from a /sync response. - * - * @param currentCount - The current count of one_time_keys to be stored - */ - updateOneTimeKeyCount(currentCount) { - if (isFinite(currentCount)) { - this.oneTimeKeyCount = currentCount; - } - else { - throw new TypeError("Parameter for updateOneTimeKeyCount has to be a number"); - } - } - setNeedsNewFallback(needsNewFallback) { - this.needsNewFallback = needsNewFallback; - } - getNeedsNewFallback() { - return !!this.needsNewFallback; - } - // check if it's time to upload one-time keys, and do so if so. - maybeUploadOneTimeKeys() { - // frequency with which to check & upload one-time keys - const uploadPeriod = 1000 * 60; // one minute - // max number of keys to upload at once - // Creating keys can be an expensive operation so we limit the - // number we generate in one go to avoid blocking the application - // for too long. - const maxKeysPerCycle = 5; - if (this.oneTimeKeyCheckInProgress) { - return; - } - const now = Date.now(); - if (this.lastOneTimeKeyCheck !== null && now - this.lastOneTimeKeyCheck < uploadPeriod) { - // we've done a key upload recently. - return; - } - this.lastOneTimeKeyCheck = now; - // We need to keep a pool of one time public keys on the server so that - // other devices can start conversations with us. But we can only store - // a finite number of private keys in the olm Account object. - // To complicate things further then can be a delay between a device - // claiming a public one time key from the server and it sending us a - // message. We need to keep the corresponding private key locally until - // we receive the message. - // But that message might never arrive leaving us stuck with duff - // private keys clogging up our local storage. - // So we need some kind of engineering compromise to balance all of - // these factors. - // Check how many keys we can store in the Account object. - const maxOneTimeKeys = this.olmDevice.maxNumberOfOneTimeKeys(); - // Try to keep at most half that number on the server. This leaves the - // rest of the slots free to hold keys that have been claimed from the - // server but we haven't received a message for. - // If we run out of slots when generating new keys then olm will - // discard the oldest private keys first. This will eventually clean - // out stale private keys that won't receive a message. - const keyLimit = Math.floor(maxOneTimeKeys / 2); - const uploadLoop = (keyCount) => __awaiter(this, void 0, void 0, function* () { - while (keyLimit > keyCount || this.getNeedsNewFallback()) { - // Ask olm to generate new one time keys, then upload them to synapse. - if (keyLimit > keyCount) { - logger_1.logger.info("generating oneTimeKeys"); - const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle); - yield this.olmDevice.generateOneTimeKeys(keysThisLoop); - } - if (this.getNeedsNewFallback()) { - const fallbackKeys = yield this.olmDevice.getFallbackKey(); - // if fallbackKeys is non-empty, we've already generated a - // fallback key, but it hasn't been published yet, so we - // can use that instead of generating a new one - if (!fallbackKeys.curve25519 || Object.keys(fallbackKeys.curve25519).length == 0) { - logger_1.logger.info("generating fallback key"); - if (this.fallbackCleanup) { - // cancel any pending fallback cleanup because generating - // a new fallback key will already drop the old fallback - // that would have been dropped, and we don't want to kill - // the current key - clearTimeout(this.fallbackCleanup); - delete this.fallbackCleanup; - } - yield this.olmDevice.generateFallbackKey(); - } - } - logger_1.logger.info("calling uploadOneTimeKeys"); - const res = yield this.uploadOneTimeKeys(); - if (res.one_time_key_counts && res.one_time_key_counts.signed_curve25519) { - // if the response contains a more up to date value use this - // for the next loop - keyCount = res.one_time_key_counts.signed_curve25519; - } - else { - throw new Error("response for uploading keys does not contain " + "one_time_key_counts.signed_curve25519"); - } - } - }); - this.oneTimeKeyCheckInProgress = true; - Promise.resolve() - .then(() => { - if (this.oneTimeKeyCount !== undefined) { - // We already have the current one_time_key count from a /sync response. - // Use this value instead of asking the server for the current key count. - return Promise.resolve(this.oneTimeKeyCount); - } - // ask the server how many keys we have - return this.baseApis.uploadKeysRequest({}).then((res) => { - return res.one_time_key_counts.signed_curve25519 || 0; - }); - }) - .then((keyCount) => { - // Start the uploadLoop with the current keyCount. The function checks if - // we need to upload new keys or not. - // If there are too many keys on the server then we don't need to - // create any more keys. - return uploadLoop(keyCount); - }) - .catch((e) => { - logger_1.logger.error("Error uploading one-time keys", e.stack || e); - }) - .finally(() => { - // reset oneTimeKeyCount to prevent start uploading based on old data. - // it will be set again on the next /sync-response - this.oneTimeKeyCount = undefined; - this.oneTimeKeyCheckInProgress = false; - }); - } - // returns a promise which resolves to the response - uploadOneTimeKeys() { - return __awaiter(this, void 0, void 0, function* () { - const promises = []; - let fallbackJson; - if (this.getNeedsNewFallback()) { - fallbackJson = {}; - const fallbackKeys = yield this.olmDevice.getFallbackKey(); - for (const [keyId, key] of Object.entries(fallbackKeys.curve25519)) { - const k = { key, fallback: true }; - fallbackJson["signed_curve25519:" + keyId] = k; - promises.push(this.signObject(k)); - } - this.setNeedsNewFallback(false); - } - const oneTimeKeys = yield this.olmDevice.getOneTimeKeys(); - const oneTimeJson = {}; - for (const keyId in oneTimeKeys.curve25519) { - if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) { - const k = { - key: oneTimeKeys.curve25519[keyId], - }; - oneTimeJson["signed_curve25519:" + keyId] = k; - promises.push(this.signObject(k)); - } - } - yield Promise.all(promises); - const requestBody = { - one_time_keys: oneTimeJson, - }; - if (fallbackJson) { - requestBody["org.matrix.msc2732.fallback_keys"] = fallbackJson; - requestBody["fallback_keys"] = fallbackJson; - } - const res = yield this.baseApis.uploadKeysRequest(requestBody); - if (fallbackJson) { - this.fallbackCleanup = setTimeout(() => { - delete this.fallbackCleanup; - this.olmDevice.forgetOldFallbackKey(); - }, 60 * 60 * 1000); - } - yield this.olmDevice.markKeysAsPublished(); - return res; - }); - } - /** - * Download the keys for a list of users and stores the keys in the session - * store. - * @param userIds - The users to fetch. - * @param forceDownload - Always download the keys even if cached. - * - * @returns A promise which resolves to a map `userId->deviceId->{@link DeviceInfo}`. - */ - downloadKeys(userIds, forceDownload) { - return this.deviceList.downloadKeys(userIds, !!forceDownload); - } - /** - * Get the stored device keys for a user id - * - * @param userId - the user to list keys for. - * - * @returns list of devices, or null if we haven't - * managed to get a list of devices for this user yet. - */ - getStoredDevicesForUser(userId) { - return this.deviceList.getStoredDevicesForUser(userId); - } - /** - * Get the stored keys for a single device - * - * - * @returns device, or undefined - * if we don't know about this device - */ - getStoredDevice(userId, deviceId) { - return this.deviceList.getStoredDevice(userId, deviceId); - } - /** - * Save the device list, if necessary - * - * @param delay - Time in ms before which the save actually happens. - * By default, the save is delayed for a short period in order to batch - * multiple writes, but this behaviour can be disabled by passing 0. - * - * @returns true if the data was saved, false if - * it was not (eg. because no changes were pending). The promise - * will only resolve once the data is saved, so may take some time - * to resolve. - */ - saveDeviceList(delay) { - return this.deviceList.saveIfDirty(delay); - } - /** - * Update the blocked/verified state of the given device - * - * @param userId - owner of the device - * @param deviceId - unique identifier for the device or user's - * cross-signing public key ID. - * - * @param verified - whether to mark the device as verified. Null to - * leave unchanged. - * - * @param blocked - whether to mark the device as blocked. Null to - * leave unchanged. - * - * @param known - whether to mark that the user has been made aware of - * the existence of this device. Null to leave unchanged - * - * @param keys - The list of keys that was present - * during the device verification. This will be double checked with the list - * of keys the given device has currently. - * - * @returns updated DeviceInfo - */ - setDeviceVerification(userId, deviceId, verified = null, blocked = null, known = null, keys) { - return __awaiter(this, void 0, void 0, function* () { - // Check if the 'device' is actually a cross signing key - // The js-sdk's verification treats cross-signing keys as devices - // and so uses this method to mark them verified. - const xsk = this.deviceList.getStoredCrossSigningForUser(userId); - if (xsk && xsk.getId() === deviceId) { - if (blocked !== null || known !== null) { - throw new Error("Cannot set blocked or known for a cross-signing key"); - } - if (!verified) { - throw new Error("Cannot set a cross-signing key as unverified"); - } - const gotKeyId = keys ? Object.values(keys)[0] : null; - if (keys && (Object.values(keys).length !== 1 || gotKeyId !== xsk.getId())) { - throw new Error(`Key did not match expected value: expected ${xsk.getId()}, got ${gotKeyId}`); - } - if (!this.crossSigningInfo.getId() && userId === this.crossSigningInfo.userId) { - this.storeTrustedSelfKeys(xsk.keys); - // This will cause our own user trust to change, so emit the event - this.emit(CryptoEvent.UserTrustStatusChanged, this.userId, this.checkUserTrust(userId)); - } - // Now sign the master key with our user signing key (unless it's ourself) - if (userId !== this.userId) { - logger_1.logger.info("Master key " + xsk.getId() + " for " + userId + " marked verified. Signing..."); - const device = yield this.crossSigningInfo.signUser(xsk); - if (device) { - const upload = ({ shouldEmit = false }) => __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info("Uploading signature for " + userId + "..."); - const response = yield this.baseApis.uploadKeySignatures({ - [userId]: { - [deviceId]: device, - }, - }); - const { failures } = response || {}; - if (Object.keys(failures || []).length > 0) { - if (shouldEmit) { - this.baseApis.emit(CryptoEvent.KeySignatureUploadFailure, failures, "setDeviceVerification", upload); - } - /* Throwing here causes the process to be cancelled and the other - * user to be notified */ - throw new errors_1.KeySignatureUploadError("Key upload failed", { failures }); - } - }); - yield upload({ shouldEmit: true }); - // This will emit events when it comes back down the sync - // (we could do local echo to speed things up) - } - return device; // TODO types - } - else { - return xsk; - } - } - const devices = this.deviceList.getRawStoredDevicesForUser(userId); - if (!devices || !devices[deviceId]) { - throw new Error("Unknown device " + userId + ":" + deviceId); - } - const dev = devices[deviceId]; - let verificationStatus = dev.verified; - if (verified) { - if (keys) { - for (const [keyId, key] of Object.entries(keys)) { - if (dev.keys[keyId] !== key) { - throw new Error(`Key did not match expected value: expected ${key}, got ${dev.keys[keyId]}`); - } - } - } - verificationStatus = DeviceVerification.VERIFIED; - } - else if (verified !== null && verificationStatus == DeviceVerification.VERIFIED) { - verificationStatus = DeviceVerification.UNVERIFIED; - } - if (blocked) { - verificationStatus = DeviceVerification.BLOCKED; - } - else if (blocked !== null && verificationStatus == DeviceVerification.BLOCKED) { - verificationStatus = DeviceVerification.UNVERIFIED; - } - let knownStatus = dev.known; - if (known !== null) { - knownStatus = known; - } - if (dev.verified !== verificationStatus || dev.known !== knownStatus) { - dev.verified = verificationStatus; - dev.known = knownStatus; - this.deviceList.storeDevicesForUser(userId, devices); - this.deviceList.saveIfDirty(); - } - // do cross-signing - if (verified && userId === this.userId) { - logger_1.logger.info("Own device " + deviceId + " marked verified: signing"); - // Signing only needed if other device not already signed - let device; - const deviceTrust = this.checkDeviceTrust(userId, deviceId); - if (deviceTrust.isCrossSigningVerified()) { - logger_1.logger.log(`Own device ${deviceId} already cross-signing verified`); - } - else { - device = (yield this.crossSigningInfo.signDevice(userId, deviceinfo_1.DeviceInfo.fromStorage(dev, deviceId))); - } - if (device) { - const upload = ({ shouldEmit = false }) => __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info("Uploading signature for " + deviceId); - const response = yield this.baseApis.uploadKeySignatures({ - [userId]: { - [deviceId]: device, - }, - }); - const { failures } = response || {}; - if (Object.keys(failures || []).length > 0) { - if (shouldEmit) { - this.baseApis.emit(CryptoEvent.KeySignatureUploadFailure, failures, "setDeviceVerification", upload); - } - throw new errors_1.KeySignatureUploadError("Key upload failed", { failures }); - } - }); - yield upload({ shouldEmit: true }); - // XXX: we'll need to wait for the device list to be updated - } - } - const deviceObj = deviceinfo_1.DeviceInfo.fromStorage(dev, deviceId); - this.emit(CryptoEvent.DeviceVerificationChanged, userId, deviceId, deviceObj); - return deviceObj; - }); - } - findVerificationRequestDMInProgress(roomId) { - return this.inRoomVerificationRequests.findRequestInProgress(roomId); - } - getVerificationRequestsToDeviceInProgress(userId) { - return this.toDeviceVerificationRequests.getRequestsInProgress(userId); - } - requestVerificationDM(userId, roomId) { - const existingRequest = this.inRoomVerificationRequests.findRequestInProgress(roomId); - if (existingRequest) { - return Promise.resolve(existingRequest); - } - const channel = new InRoomChannel_1.InRoomChannel(this.baseApis, roomId, userId); - return this.requestVerificationWithChannel(userId, channel, this.inRoomVerificationRequests); - } - requestVerification(userId, devices) { - if (!devices) { - devices = Object.keys(this.deviceList.getRawStoredDevicesForUser(userId)); - } - const existingRequest = this.toDeviceVerificationRequests.findRequestInProgress(userId, devices); - if (existingRequest) { - return Promise.resolve(existingRequest); - } - const channel = new ToDeviceChannel_1.ToDeviceChannel(this.baseApis, userId, devices, ToDeviceChannel_1.ToDeviceChannel.makeTransactionId()); - return this.requestVerificationWithChannel(userId, channel, this.toDeviceVerificationRequests); - } - requestVerificationWithChannel(userId, channel, requestsMap) { - return __awaiter(this, void 0, void 0, function* () { - let request = new VerificationRequest_1.VerificationRequest(channel, this.verificationMethods, this.baseApis); - // if transaction id is already known, add request - if (channel.transactionId) { - requestsMap.setRequestByChannel(channel, request); - } - yield request.sendRequest(); - // don't replace the request created by a racing remote echo - const racingRequest = requestsMap.getRequestByChannel(channel); - if (racingRequest) { - request = racingRequest; - } - else { - logger_1.logger.log(`Crypto: adding new request to ` + `requestsByTxnId with id ${channel.transactionId} ${channel.roomId}`); - requestsMap.setRequestByChannel(channel, request); - } - return request; - }); - } - beginKeyVerification(method, userId, deviceId, transactionId = null) { - let request; - if (transactionId) { - request = this.toDeviceVerificationRequests.getRequestBySenderAndTxnId(userId, transactionId); - if (!request) { - throw new Error(`No request found for user ${userId} with ` + `transactionId ${transactionId}`); - } - } - else { - transactionId = ToDeviceChannel_1.ToDeviceChannel.makeTransactionId(); - const channel = new ToDeviceChannel_1.ToDeviceChannel(this.baseApis, userId, [deviceId], transactionId, deviceId); - request = new VerificationRequest_1.VerificationRequest(channel, this.verificationMethods, this.baseApis); - this.toDeviceVerificationRequests.setRequestBySenderAndTxnId(userId, transactionId, request); - } - return request.beginKeyVerification(method, { userId, deviceId }); - } - legacyDeviceVerification(userId, deviceId, method) { - return __awaiter(this, void 0, void 0, function* () { - const transactionId = ToDeviceChannel_1.ToDeviceChannel.makeTransactionId(); - const channel = new ToDeviceChannel_1.ToDeviceChannel(this.baseApis, userId, [deviceId], transactionId, deviceId); - const request = new VerificationRequest_1.VerificationRequest(channel, this.verificationMethods, this.baseApis); - this.toDeviceVerificationRequests.setRequestBySenderAndTxnId(userId, transactionId, request); - const verifier = request.beginKeyVerification(method, { userId, deviceId }); - // either reject by an error from verify() while sending .start - // or resolve when the request receives the - // local (fake remote) echo for sending the .start event - yield Promise.race([verifier.verify(), request.waitFor((r) => r.started)]); - return request; - }); - } - /** - * Get information on the active olm sessions with a user - *

- * Returns a map from device id to an object with keys 'deviceIdKey' (the - * device's curve25519 identity key) and 'sessions' (an array of objects in the - * same format as that returned by - * {@link OlmDevice#getSessionInfoForDevice}). - *

- * This method is provided for debugging purposes. - * - * @param userId - id of user to inspect - */ - getOlmSessionsForUser(userId) { - return __awaiter(this, void 0, void 0, function* () { - const devices = this.getStoredDevicesForUser(userId) || []; - const result = {}; - for (const device of devices) { - const deviceKey = device.getIdentityKey(); - const sessions = yield this.olmDevice.getSessionInfoForDevice(deviceKey); - result[device.deviceId] = { - deviceIdKey: deviceKey, - sessions: sessions, - }; - } - return result; - }); - } - /** - * Get the device which sent an event - * - * @param event - event to be checked - */ - getEventSenderDeviceInfo(event) { - const senderKey = event.getSenderKey(); - const algorithm = event.getWireContent().algorithm; - if (!senderKey || !algorithm) { - return null; - } - if (event.isKeySourceUntrusted()) { - // we got the key for this event from a source that we consider untrusted - return null; - } - // senderKey is the Curve25519 identity key of the device which the event - // was sent from. In the case of Megolm, it's actually the Curve25519 - // identity key of the device which set up the Megolm session. - const device = this.deviceList.getDeviceByIdentityKey(algorithm, senderKey); - if (device === null) { - // we haven't downloaded the details of this device yet. - return null; - } - // so far so good, but now we need to check that the sender of this event - // hadn't advertised someone else's Curve25519 key as their own. We do that - // by checking the Ed25519 claimed by the event (or, in the case of megolm, - // the event which set up the megolm session), to check that it matches the - // fingerprint of the purported sending device. - // - // (see https://github.com/vector-im/vector-web/issues/2215) - const claimedKey = event.getClaimedEd25519Key(); - if (!claimedKey) { - logger_1.logger.warn("Event " + event.getId() + " claims no ed25519 key: " + "cannot verify sending device"); - return null; - } - if (claimedKey !== device.getFingerprint()) { - logger_1.logger.warn("Event " + - event.getId() + - " claims ed25519 key " + - claimedKey + - " but sender device has key " + - device.getFingerprint()); - return null; - } - return device; - } - /** - * Get information about the encryption of an event - * - * @param event - event to be checked - * - * @returns An object with the fields: - * - encrypted: whether the event is encrypted (if not encrypted, some of the - * other properties may not be set) - * - senderKey: the sender's key - * - algorithm: the algorithm used to encrypt the event - * - authenticated: whether we can be sure that the owner of the senderKey - * sent the event - * - sender: the sender's device information, if available - * - mismatchedSender: if the event's ed25519 and curve25519 keys don't match - * (only meaningful if `sender` is set) - */ - getEventEncryptionInfo(event) { - var _a, _b; - const ret = {}; - ret.senderKey = (_a = event.getSenderKey()) !== null && _a !== void 0 ? _a : undefined; - ret.algorithm = event.getWireContent().algorithm; - if (!ret.senderKey || !ret.algorithm) { - ret.encrypted = false; - return ret; - } - ret.encrypted = true; - if (event.isKeySourceUntrusted()) { - // we got the key this event from somewhere else - // TODO: check if we can trust the forwarders. - ret.authenticated = false; - } - else { - ret.authenticated = true; - } - // senderKey is the Curve25519 identity key of the device which the event - // was sent from. In the case of Megolm, it's actually the Curve25519 - // identity key of the device which set up the Megolm session. - ret.sender = (_b = this.deviceList.getDeviceByIdentityKey(ret.algorithm, ret.senderKey)) !== null && _b !== void 0 ? _b : undefined; - // so far so good, but now we need to check that the sender of this event - // hadn't advertised someone else's Curve25519 key as their own. We do that - // by checking the Ed25519 claimed by the event (or, in the case of megolm, - // the event which set up the megolm session), to check that it matches the - // fingerprint of the purported sending device. - // - // (see https://github.com/vector-im/vector-web/issues/2215) - const claimedKey = event.getClaimedEd25519Key(); - if (!claimedKey) { - logger_1.logger.warn("Event " + event.getId() + " claims no ed25519 key: " + "cannot verify sending device"); - ret.mismatchedSender = true; - } - if (ret.sender && claimedKey !== ret.sender.getFingerprint()) { - logger_1.logger.warn("Event " + - event.getId() + - " claims ed25519 key " + - claimedKey + - "but sender device has key " + - ret.sender.getFingerprint()); - ret.mismatchedSender = true; - } - return ret; - } - /** - * Forces the current outbound group session to be discarded such - * that another one will be created next time an event is sent. - * - * @param roomId - The ID of the room to discard the session for - * - * This should not normally be necessary. - */ - forceDiscardSession(roomId) { - const alg = this.roomEncryptors.get(roomId); - if (alg === undefined) - throw new Error("Room not encrypted"); - if (alg.forceDiscardSession === undefined) { - throw new Error("Room encryption algorithm doesn't support session discarding"); - } - alg.forceDiscardSession(); - return Promise.resolve(); - } - /** - * Configure a room to use encryption (ie, save a flag in the cryptoStore). - * - * @param roomId - The room ID to enable encryption in. - * - * @param config - The encryption config for the room. - * - * @param inhibitDeviceQuery - true to suppress device list query for - * users in the room (for now). In case lazy loading is enabled, - * the device query is always inhibited as the members are not tracked. - * - * @deprecated It is normally incorrect to call this method directly. Encryption - * is enabled by receiving an `m.room.encryption` event (which we may have sent - * previously). - */ - setRoomEncryption(roomId, config, inhibitDeviceQuery) { - return __awaiter(this, void 0, void 0, function* () { - const room = this.clientStore.getRoom(roomId); - if (!room) { - throw new Error(`Unable to enable encryption tracking devices in unknown room ${roomId}`); - } - yield this.setRoomEncryptionImpl(room, config); - if (!this.lazyLoadMembers && !inhibitDeviceQuery) { - this.deviceList.refreshOutdatedDeviceLists(); - } - }); - } - /** - * Set up encryption for a room. - * - * This is called when an m.room.encryption event is received. It saves a flag - * for the room in the cryptoStore (if it wasn't already set), sets up an "encryptor" for - * the room, and enables device-list tracking for the room. - * - * It does not initiate a device list query for the room. That is normally - * done once we finish processing the sync, in onSyncCompleted. - * - * @param room - The room to enable encryption in. - * @param config - The encryption config for the room. - */ - setRoomEncryptionImpl(room, config) { - return __awaiter(this, void 0, void 0, function* () { - const roomId = room.roomId; - // ignore crypto events with no algorithm defined - // This will happen if a crypto event is redacted before we fetch the room state - // It would otherwise just throw later as an unknown algorithm would, but we may - // as well catch this here - if (!config.algorithm) { - logger_1.logger.log("Ignoring setRoomEncryption with no algorithm"); - return; - } - // if state is being replayed from storage, we might already have a configuration - // for this room as they are persisted as well. - // We just need to make sure the algorithm is initialized in this case. - // However, if the new config is different, - // we should bail out as room encryption can't be changed once set. - const existingConfig = this.roomList.getRoomEncryption(roomId); - if (existingConfig) { - if (JSON.stringify(existingConfig) != JSON.stringify(config)) { - logger_1.logger.error("Ignoring m.room.encryption event which requests " + "a change of config in " + roomId); - return; - } - } - // if we already have encryption in this room, we should ignore this event, - // as it would reset the encryption algorithm. - // This is at least expected to be called twice, as sync calls onCryptoEvent - // for both the timeline and state sections in the /sync response, - // the encryption event would appear in both. - // If it's called more than twice though, - // it signals a bug on client or server. - const existingAlg = this.roomEncryptors.get(roomId); - if (existingAlg) { - return; - } - // _roomList.getRoomEncryption will not race with _roomList.setRoomEncryption - // because it first stores in memory. We should await the promise only - // after all the in-memory state (roomEncryptors and _roomList) has been updated - // to avoid races when calling this method multiple times. Hence keep a hold of the promise. - let storeConfigPromise = null; - if (!existingConfig) { - storeConfigPromise = this.roomList.setRoomEncryption(roomId, config); - } - const AlgClass = algorithms.ENCRYPTION_CLASSES.get(config.algorithm); - if (!AlgClass) { - throw new Error("Unable to encrypt with " + config.algorithm); - } - const alg = new AlgClass({ - userId: this.userId, - deviceId: this.deviceId, - crypto: this, - olmDevice: this.olmDevice, - baseApis: this.baseApis, - roomId, - config, - }); - this.roomEncryptors.set(roomId, alg); - if (storeConfigPromise) { - yield storeConfigPromise; - } - logger_1.logger.log(`Enabling encryption in ${roomId}`); - // we don't want to force a download of the full membership list of this room, but as soon as we have that - // list we can start tracking the device list. - if (room.membersLoaded()) { - yield this.trackRoomDevicesImpl(room); - } - else { - // wait for the membership list to be loaded - const onState = (_state) => { - room.off(room_state_1.RoomStateEvent.Update, onState); - if (room.membersLoaded()) { - this.trackRoomDevicesImpl(room).catch((e) => { - logger_1.logger.error(`Error enabling device tracking in ${roomId}`, e); - }); - } - }; - room.on(room_state_1.RoomStateEvent.Update, onState); - } - }); - } - /** - * Make sure we are tracking the device lists for all users in this room. - * - * @param roomId - The room ID to start tracking devices in. - * @returns when all devices for the room have been fetched and marked to track - * @deprecated there's normally no need to call this function: device list tracking - * will be enabled as soon as we have the full membership list. - */ - trackRoomDevices(roomId) { - const room = this.clientStore.getRoom(roomId); - if (!room) { - throw new Error(`Unable to start tracking devices in unknown room ${roomId}`); - } - return this.trackRoomDevicesImpl(room); - } - /** - * Make sure we are tracking the device lists for all users in this room. - * - * This is normally called when we are about to send an encrypted event, to make sure - * we have all the devices in the room; but it is also called when processing an - * m.room.encryption state event (if lazy-loading is disabled), or when members are - * loaded (if lazy-loading is enabled), to prepare the device list. - * - * @param room - Room to enable device-list tracking in - */ - trackRoomDevicesImpl(room) { - const roomId = room.roomId; - const trackMembers = () => __awaiter(this, void 0, void 0, function* () { - // not an encrypted room - if (!this.roomEncryptors.has(roomId)) { - return; - } - logger_1.logger.log(`Starting to track devices for room ${roomId} ...`); - const members = yield room.getEncryptionTargetMembers(); - members.forEach((m) => { - this.deviceList.startTrackingDeviceList(m.userId); - }); - }); - let promise = this.roomDeviceTrackingState[roomId]; - if (!promise) { - promise = trackMembers(); - this.roomDeviceTrackingState[roomId] = promise.catch((err) => { - delete this.roomDeviceTrackingState[roomId]; - throw err; - }); - } - return promise; - } - /** - * Try to make sure we have established olm sessions for all known devices for - * the given users. - * - * @param users - list of user ids - * @param force - If true, force a new Olm session to be created. Default false. - * - * @returns resolves once the sessions are complete, to - * an Object mapping from userId to deviceId to - * {@link OlmSessionResult} - */ - ensureOlmSessionsForUsers(users, force) { - // map user Id → DeviceInfo[] - const devicesByUser = new Map(); - for (const userId of users) { - const userDevices = []; - devicesByUser.set(userId, userDevices); - const devices = this.getStoredDevicesForUser(userId) || []; - for (const deviceInfo of devices) { - const key = deviceInfo.getIdentityKey(); - if (key == this.olmDevice.deviceCurve25519Key) { - // don't bother setting up session to ourself - continue; - } - if (deviceInfo.verified == DeviceVerification.BLOCKED) { - // don't bother setting up sessions with blocked users - continue; - } - userDevices.push(deviceInfo); - } - } - return olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, force); - } - /** - * Get a list containing all of the room keys - * - * @returns a list of session export objects - */ - exportRoomKeys() { - return __awaiter(this, void 0, void 0, function* () { - const exportedSessions = []; - yield this.cryptoStore.doTxn("readonly", [indexeddb_crypto_store_1.IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => { - if (s === null) - return; - const sess = this.olmDevice.exportInboundGroupSession(s.senderKey, s.sessionId, s.sessionData); - delete sess.first_known_index; - sess.algorithm = olmlib.MEGOLM_ALGORITHM; - exportedSessions.push(sess); - }); - }); - return exportedSessions; - }); - } - /** - * Import a list of room keys previously exported by exportRoomKeys - * - * @param keys - a list of session export objects - * @returns a promise which resolves once the keys have been imported - */ - importRoomKeys(keys, opts = {}) { - let successes = 0; - let failures = 0; - const total = keys.length; - function updateProgress() { - var _a; - (_a = opts.progressCallback) === null || _a === void 0 ? void 0 : _a.call(opts, { - stage: "load_keys", - successes, - failures, - total, - }); - } - return Promise.all(keys.map((key) => { - if (!key.room_id || !key.algorithm) { - logger_1.logger.warn("ignoring room key entry with missing fields", key); - failures++; - if (opts.progressCallback) { - updateProgress(); - } - return null; - } - const alg = this.getRoomDecryptor(key.room_id, key.algorithm); - return alg.importRoomKey(key, opts).finally(() => { - successes++; - if (opts.progressCallback) { - updateProgress(); - } - }); - })).then(); - } - /** - * Counts the number of end to end session keys that are waiting to be backed up - * @returns Promise which resolves to the number of sessions requiring backup - */ - countSessionsNeedingBackup() { - return this.backupManager.countSessionsNeedingBackup(); - } - /** - * Perform any background tasks that can be done before a message is ready to - * send, in order to speed up sending of the message. - * - * @param room - the room the event is in - */ - prepareToEncrypt(room) { - const alg = this.roomEncryptors.get(room.roomId); - if (alg) { - alg.prepareToEncrypt(room); - } - } - /** - * Encrypt an event according to the configuration of the room. - * - * @param event - event to be sent - * - * @param room - destination room. - * - * @returns Promise which resolves when the event has been - * encrypted, or null if nothing was needed - */ - encryptEvent(event, room) { - return __awaiter(this, void 0, void 0, function* () { - const roomId = event.getRoomId(); - const alg = this.roomEncryptors.get(roomId); - if (!alg) { - // MatrixClient has already checked that this room should be encrypted, - // so this is an unexpected situation. - throw new Error("Room " + - roomId + - " was previously configured to use encryption, but is " + - "no longer. Perhaps the homeserver is hiding the " + - "configuration event."); - } - // wait for all the room devices to be loaded - yield this.trackRoomDevicesImpl(room); - let content = event.getContent(); - // If event has an m.relates_to then we need - // to put this on the wrapping event instead - const mRelatesTo = content["m.relates_to"]; - if (mRelatesTo) { - // Clone content here so we don't remove `m.relates_to` from the local-echo - content = Object.assign({}, content); - delete content["m.relates_to"]; - } - // Treat element's performance metrics the same as `m.relates_to` (when present) - const elementPerfMetrics = content["io.element.performance_metrics"]; - if (elementPerfMetrics) { - content = Object.assign({}, content); - delete content["io.element.performance_metrics"]; - } - const encryptedContent = (yield alg.encryptMessage(room, event.getType(), content)); - if (mRelatesTo) { - encryptedContent["m.relates_to"] = mRelatesTo; - } - if (elementPerfMetrics) { - encryptedContent["io.element.performance_metrics"] = elementPerfMetrics; - } - event.makeEncrypted("m.room.encrypted", encryptedContent, this.olmDevice.deviceCurve25519Key, this.olmDevice.deviceEd25519Key); - }); - } - /** - * Decrypt a received event - * - * - * @returns resolves once we have - * finished decrypting. Rejects with an `algorithms.DecryptionError` if there - * is a problem decrypting the event. - */ - decryptEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - if (event.isRedacted()) { - // Try to decrypt the redaction event, to support encrypted - // redaction reasons. If we can't decrypt, just fall back to using - // the original redacted_because. - const redactionEvent = new event_2.MatrixEvent(Object.assign({ room_id: event.getRoomId() }, event.getUnsigned().redacted_because)); - let redactedBecause = event.getUnsigned().redacted_because; - if (redactionEvent.isEncrypted()) { - try { - const decryptedEvent = yield this.decryptEvent(redactionEvent); - redactedBecause = decryptedEvent.clearEvent; - } - catch (e) { - logger_1.logger.warn("Decryption of redaction failed. Falling back to unencrypted event.", e); - } - } - return { - clearEvent: { - room_id: event.getRoomId(), - type: "m.room.message", - content: {}, - unsigned: { - redacted_because: redactedBecause, - }, - }, - }; - } - else { - const content = event.getWireContent(); - const alg = this.getRoomDecryptor(event.getRoomId(), content.algorithm); - return alg.decryptEvent(event); - } - }); - } - /** - * Handle the notification from /sync or /keys/changes that device lists have - * been changed. - * - * @param syncData - Object containing sync tokens associated with this sync - * @param syncDeviceLists - device_lists field from /sync, or response from - * /keys/changes - */ - handleDeviceListChanges(syncData, syncDeviceLists) { - return __awaiter(this, void 0, void 0, function* () { - // Initial syncs don't have device change lists. We'll either get the complete list - // of changes for the interval or will have invalidated everything in willProcessSync - if (!syncData.oldSyncToken) - return; - // Here, we're relying on the fact that we only ever save the sync data after - // sucessfully saving the device list data, so we're guaranteed that the device - // list store is at least as fresh as the sync token from the sync store, ie. - // any device changes received in sync tokens prior to the 'next' token here - // have been processed and are reflected in the current device list. - // If we didn't make this assumption, we'd have to use the /keys/changes API - // to get key changes between the sync token in the device list and the 'old' - // sync token used here to make sure we didn't miss any. - yield this.evalDeviceListChanges(syncDeviceLists); - }); - } - /** - * Send a request for some room keys, if we have not already done so - * - * @param resend - whether to resend the key request if there is - * already one - * - * @returns a promise that resolves when the key request is queued - */ - requestRoomKey(requestBody, recipients, resend = false) { - return this.outgoingRoomKeyRequestManager - .queueRoomKeyRequest(requestBody, recipients, resend) - .then(() => { - if (this.sendKeyRequestsImmediately) { - this.outgoingRoomKeyRequestManager.sendQueuedRequests(); - } - }) - .catch((e) => { - // this normally means we couldn't talk to the store - logger_1.logger.error("Error requesting key for event", e); - }); - } - /** - * Cancel any earlier room key request - * - * @param requestBody - parameters to match for cancellation - */ - cancelRoomKeyRequest(requestBody) { - this.outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody).catch((e) => { - logger_1.logger.warn("Error clearing pending room key requests", e); - }); - } - /** - * Re-send any outgoing key requests, eg after verification - * @returns - */ - cancelAndResendAllOutgoingKeyRequests() { - return __awaiter(this, void 0, void 0, function* () { - yield this.outgoingRoomKeyRequestManager.cancelAndResendAllOutgoingRequests(); - }); - } - /** - * handle an m.room.encryption event - * - * @param room - in which the event was received - * @param event - encryption event to be processed - */ - onCryptoEvent(room, event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getContent(); - yield this.setRoomEncryptionImpl(room, content); - }); - } - /** - * Called before the result of a sync is processed - * - * @param syncData - the data from the 'MatrixClient.sync' event - */ - onSyncWillProcess(syncData) { - return __awaiter(this, void 0, void 0, function* () { - if (!syncData.oldSyncToken) { - // If there is no old sync token, we start all our tracking from - // scratch, so mark everything as untracked. onCryptoEvent will - // be called for all e2e rooms during the processing of the sync, - // at which point we'll start tracking all the users of that room. - logger_1.logger.log("Initial sync performed - resetting device tracking state"); - this.deviceList.stopTrackingAllDeviceLists(); - // we always track our own device list (for key backups etc) - this.deviceList.startTrackingDeviceList(this.userId); - this.roomDeviceTrackingState = {}; - } - this.sendKeyRequestsImmediately = false; - }); - } - /** - * handle the completion of a /sync - * - * This is called after the processing of each successful /sync response. - * It is an opportunity to do a batch process on the information received. - * - * @param syncData - the data from the 'MatrixClient.sync' event - */ - onSyncCompleted(syncData) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - this.deviceList.setSyncToken((_a = syncData.nextSyncToken) !== null && _a !== void 0 ? _a : null); - this.deviceList.saveIfDirty(); - // we always track our own device list (for key backups etc) - this.deviceList.startTrackingDeviceList(this.userId); - this.deviceList.refreshOutdatedDeviceLists(); - // we don't start uploading one-time keys until we've caught up with - // to-device messages, to help us avoid throwing away one-time-keys that we - // are about to receive messages for - // (https://github.com/vector-im/element-web/issues/2782). - if (!syncData.catchingUp) { - this.maybeUploadOneTimeKeys(); - this.processReceivedRoomKeyRequests(); - // likewise don't start requesting keys until we've caught up - // on to_device messages, otherwise we'll request keys that we're - // just about to get. - this.outgoingRoomKeyRequestManager.sendQueuedRequests(); - // Sync has finished so send key requests straight away. - this.sendKeyRequestsImmediately = true; - } - }); - } - /** - * Trigger the appropriate invalidations and removes for a given - * device list - * - * @param deviceLists - device_lists field from /sync, or response from - * /keys/changes - */ - evalDeviceListChanges(deviceLists) { - return __awaiter(this, void 0, void 0, function* () { - if (Array.isArray(deviceLists === null || deviceLists === void 0 ? void 0 : deviceLists.changed)) { - deviceLists.changed.forEach((u) => { - this.deviceList.invalidateUserDeviceList(u); - }); - } - if (Array.isArray(deviceLists === null || deviceLists === void 0 ? void 0 : deviceLists.left) && deviceLists.left.length) { - // Check we really don't share any rooms with these users - // any more: the server isn't required to give us the - // exact correct set. - const e2eUserIds = new Set(yield this.getTrackedE2eUsers()); - deviceLists.left.forEach((u) => { - if (!e2eUserIds.has(u)) { - this.deviceList.stopTrackingDeviceList(u); - } - }); - } - }); - } - /** - * Get a list of all the IDs of users we share an e2e room with - * for which we are tracking devices already - * - * @returns List of user IDs - */ - getTrackedE2eUsers() { - return __awaiter(this, void 0, void 0, function* () { - const e2eUserIds = []; - for (const room of this.getTrackedE2eRooms()) { - const members = yield room.getEncryptionTargetMembers(); - for (const member of members) { - e2eUserIds.push(member.userId); - } - } - return e2eUserIds; - }); - } - /** - * Get a list of the e2e-enabled rooms we are members of, - * and for which we are already tracking the devices - * - * @returns - */ - getTrackedE2eRooms() { - return this.clientStore.getRooms().filter((room) => { - // check for rooms with encryption enabled - const alg = this.roomEncryptors.get(room.roomId); - if (!alg) { - return false; - } - if (!this.roomDeviceTrackingState[room.roomId]) { - return false; - } - // ignore any rooms which we have left - const myMembership = room.getMyMembership(); - return myMembership === "join" || myMembership === "invite"; - }); - } - /** - * Encrypts and sends a given object via Olm to-device messages to a given - * set of devices. - * @param userDeviceInfoArr - the devices to send to - * @param payload - fields to include in the encrypted payload - * @returns Promise which - * resolves once the message has been encrypted and sent to the given - * userDeviceMap, and returns the `{ contentMap, deviceInfoByDeviceId }` - * of the successfully sent messages. - */ - encryptAndSendToDevices(userDeviceInfoArr, payload) { - return __awaiter(this, void 0, void 0, function* () { - const toDeviceBatch = { - eventType: event_1.EventType.RoomMessageEncrypted, - batch: [], - }; - try { - yield Promise.all(userDeviceInfoArr.map(({ userId, deviceInfo }) => __awaiter(this, void 0, void 0, function* () { - const deviceId = deviceInfo.deviceId; - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - toDeviceBatch.batch.push({ - userId, - deviceId, - payload: encryptedContent, - }); - yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, new Map([[userId, [deviceInfo]]])); - yield olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.userId, this.deviceId, this.olmDevice, userId, deviceInfo, payload); - }))); - // prune out any devices that encryptMessageForDevice could not encrypt for, - // in which case it will have just not added anything to the ciphertext object. - // There's no point sending messages to devices if we couldn't encrypt to them, - // since that's effectively a blank message. - toDeviceBatch.batch = toDeviceBatch.batch.filter((msg) => { - if (Object.keys(msg.payload.ciphertext).length > 0) { - return true; - } - else { - logger_1.logger.log(`No ciphertext for device ${msg.userId}:${msg.deviceId}: pruning`); - return false; - } - }); - try { - yield this.baseApis.queueToDevice(toDeviceBatch); - } - catch (e) { - logger_1.logger.error("sendToDevice failed", e); - throw e; - } - } - catch (e) { - logger_1.logger.error("encryptAndSendToDevices promises failed", e); - throw e; - } - }); - } - preprocessToDeviceMessages(events) { - return __awaiter(this, void 0, void 0, function* () { - // all we do here is filter out encrypted to-device messages with the wrong algorithm. Decryption - // happens later in decryptEvent, via the EventMapper - return events.filter((toDevice) => { - var _a; - if (toDevice.type === event_1.EventType.RoomMessageEncrypted && - !["m.olm.v1.curve25519-aes-sha2"].includes((_a = toDevice.content) === null || _a === void 0 ? void 0 : _a.algorithm)) { - logger_1.logger.log("Ignoring invalid encrypted to-device event from " + toDevice.sender); - return false; - } - return true; - }); - }); - } - preprocessOneTimeKeyCounts(oneTimeKeysCounts) { - const currentCount = oneTimeKeysCounts.get("signed_curve25519") || 0; - this.updateOneTimeKeyCount(currentCount); - return Promise.resolve(); - } - preprocessUnusedFallbackKeys(unusedFallbackKeys) { - this.setNeedsNewFallback(!unusedFallbackKeys.has("signed_curve25519")); - return Promise.resolve(); - } - /** - * Handle a key event - * - * @internal - * @param event - key event - */ - onRoomKeyEvent(event) { - const content = event.getContent(); - if (!content.room_id || !content.algorithm) { - logger_1.logger.error("key event is missing fields"); - return; - } - if (!this.backupManager.checkedForBackup) { - // don't bother awaiting on this - the important thing is that we retry if we - // haven't managed to check before - this.backupManager.checkAndStart(); - } - const alg = this.getRoomDecryptor(content.room_id, content.algorithm); - alg.onRoomKeyEvent(event); - } - /** - * Handle a key withheld event - * - * @internal - * @param event - key withheld event - */ - onRoomKeyWithheldEvent(event) { - const content = event.getContent(); - if ((content.code !== "m.no_olm" && (!content.room_id || !content.session_id)) || - !content.algorithm || - !content.sender_key) { - logger_1.logger.error("key withheld event is missing fields"); - return; - } - logger_1.logger.info(`Got room key withheld event from ${event.getSender()} ` + - `for ${content.algorithm} session ${content.sender_key}|${content.session_id} ` + - `in room ${content.room_id} with code ${content.code} (${content.reason})`); - const alg = this.getRoomDecryptor(content.room_id, content.algorithm); - if (alg.onRoomKeyWithheldEvent) { - alg.onRoomKeyWithheldEvent(event); - } - if (!content.room_id) { - // retry decryption for all events sent by the sender_key. This will - // update the events to show a message indicating that the olm session was - // wedged. - const roomDecryptors = this.getRoomDecryptors(content.algorithm); - for (const decryptor of roomDecryptors) { - decryptor.retryDecryptionFromSender(content.sender_key); - } - } - } - /** - * Handle a general key verification event. - * - * @internal - * @param event - verification start event - */ - onKeyVerificationMessage(event) { - if (!ToDeviceChannel_1.ToDeviceChannel.validateEvent(event, this.baseApis)) { - return; - } - const createRequest = (event) => { - if (!ToDeviceChannel_1.ToDeviceChannel.canCreateRequest(ToDeviceChannel_1.ToDeviceChannel.getEventType(event))) { - return; - } - const content = event.getContent(); - const deviceId = content && content.from_device; - if (!deviceId) { - return; - } - const userId = event.getSender(); - const channel = new ToDeviceChannel_1.ToDeviceChannel(this.baseApis, userId, [deviceId]); - return new VerificationRequest_1.VerificationRequest(channel, this.verificationMethods, this.baseApis); - }; - this.handleVerificationEvent(event, this.toDeviceVerificationRequests, createRequest); - } - handleVerificationEvent(event, requestsMap, createRequest, isLiveEvent = true) { - return __awaiter(this, void 0, void 0, function* () { - // Wait for event to get its final ID with pendingEventOrdering: "chronological", since DM channels depend on it. - if (event.isSending() && event.status != event_2.EventStatus.SENT) { - let eventIdListener; - let statusListener; - try { - yield new Promise((resolve, reject) => { - eventIdListener = resolve; - statusListener = () => { - if (event.status == event_2.EventStatus.CANCELLED) { - reject(new Error("Event status set to CANCELLED.")); - } - }; - event.once(event_2.MatrixEventEvent.LocalEventIdReplaced, eventIdListener); - event.on(event_2.MatrixEventEvent.Status, statusListener); - }); - } - catch (err) { - logger_1.logger.error("error while waiting for the verification event to be sent: ", err); - return; - } - finally { - event.removeListener(event_2.MatrixEventEvent.LocalEventIdReplaced, eventIdListener); - event.removeListener(event_2.MatrixEventEvent.Status, statusListener); - } - } - let request = requestsMap.getRequest(event); - let isNewRequest = false; - if (!request) { - request = createRequest(event); - // a request could not be made from this event, so ignore event - if (!request) { - logger_1.logger.log(`Crypto: could not find VerificationRequest for ` + - `${event.getType()}, and could not create one, so ignoring.`); - return; - } - isNewRequest = true; - requestsMap.setRequest(event, request); - } - event.setVerificationRequest(request); - try { - yield request.channel.handleEvent(event, request, isLiveEvent); - } - catch (err) { - logger_1.logger.error("error while handling verification event", err); - } - const shouldEmit = isNewRequest && - !request.initiatedByMe && - !request.invalid && // check it has enough events to pass the UNSENT stage - !request.observeOnly; - if (shouldEmit) { - this.baseApis.emit(CryptoEvent.VerificationRequest, request); - } - }); - } - /** - * Handle a toDevice event that couldn't be decrypted - * - * @internal - * @param event - undecryptable event - */ - onToDeviceBadEncrypted(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getWireContent(); - const sender = event.getSender(); - const algorithm = content.algorithm; - const deviceKey = content.sender_key; - this.baseApis.emit(client_1.ClientEvent.UndecryptableToDeviceEvent, event); - // retry decryption for all events sent by the sender_key. This will - // update the events to show a message indicating that the olm session was - // wedged. - const retryDecryption = () => { - const roomDecryptors = this.getRoomDecryptors(olmlib.MEGOLM_ALGORITHM); - for (const decryptor of roomDecryptors) { - decryptor.retryDecryptionFromSender(deviceKey); - } - }; - if (sender === undefined || deviceKey === undefined || deviceKey === undefined) { - return; - } - // check when we last forced a new session with this device: if we've already done so - // recently, don't do it again. - const lastNewSessionDevices = this.lastNewSessionForced.getOrCreate(sender); - const lastNewSessionForced = lastNewSessionDevices.getOrCreate(deviceKey); - if (lastNewSessionForced + MIN_FORCE_SESSION_INTERVAL_MS > Date.now()) { - logger_1.logger.debug("New session already forced with device " + - sender + - ":" + - deviceKey + - " at " + - lastNewSessionForced + - ": not forcing another"); - yield this.olmDevice.recordSessionProblem(deviceKey, "wedged", true); - retryDecryption(); - return; - } - // establish a new olm session with this device since we're failing to decrypt messages - // on a current session. - // Note that an undecryptable message from another device could easily be spoofed - - // is there anything we can do to mitigate this? - let device = this.deviceList.getDeviceByIdentityKey(algorithm, deviceKey); - if (!device) { - // if we don't know about the device, fetch the user's devices again - // and retry before giving up - yield this.downloadKeys([sender], false); - device = this.deviceList.getDeviceByIdentityKey(algorithm, deviceKey); - if (!device) { - logger_1.logger.info("Couldn't find device for identity key " + deviceKey + ": not re-establishing session"); - yield this.olmDevice.recordSessionProblem(deviceKey, "wedged", false); - retryDecryption(); - return; - } - } - const devicesByUser = new Map([[sender, [device]]]); - yield olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, true); - lastNewSessionDevices.set(deviceKey, Date.now()); - // Now send a blank message on that session so the other side knows about it. - // (The keyshare request is sent in the clear so that won't do) - // We send this first such that, as long as the toDevice messages arrive in the - // same order we sent them, the other end will get this first, set up the new session, - // then get the keyshare request and send the key over this new session (because it - // is the session it has most recently received a message on). - const encryptedContent = { - algorithm: olmlib.OLM_ALGORITHM, - sender_key: this.olmDevice.deviceCurve25519Key, - ciphertext: {}, - [event_1.ToDeviceMessageId]: (0, uuid_1.v4)(), - }; - yield olmlib.encryptMessageForDevice(encryptedContent.ciphertext, this.userId, this.deviceId, this.olmDevice, sender, device, { type: "m.dummy" }); - yield this.olmDevice.recordSessionProblem(deviceKey, "wedged", true); - retryDecryption(); - yield this.baseApis.sendToDevice("m.room.encrypted", new Map([[sender, new Map([[device.deviceId, encryptedContent]])]])); - // Most of the time this probably won't be necessary since we'll have queued up a key request when - // we failed to decrypt the message and will be waiting a bit for the key to arrive before sending - // it. This won't always be the case though so we need to re-send any that have already been sent - // to avoid races. - const requestsToResend = yield this.outgoingRoomKeyRequestManager.getOutgoingSentRoomKeyRequest(sender, device.deviceId); - for (const keyReq of requestsToResend) { - this.requestRoomKey(keyReq.requestBody, keyReq.recipients, true); - } - }); - } - /** - * Handle a change in the membership state of a member of a room - * - * @internal - * @param event - event causing the change - * @param member - user whose membership changed - * @param oldMembership - previous membership - */ - onRoomMembership(event, member, oldMembership) { - // this event handler is registered on the *client* (as opposed to the room - // member itself), which means it is only called on changes to the *live* - // membership state (ie, it is not called when we back-paginate, nor when - // we load the state in the initialsync). - // - // Further, it is automatically registered and called when new members - // arrive in the room. - var _a; - const roomId = member.roomId; - const alg = this.roomEncryptors.get(roomId); - if (!alg) { - // not encrypting in this room - return; - } - // only mark users in this room as tracked if we already started tracking in this room - // this way we don't start device queries after sync on behalf of this room which we won't use - // the result of anyway, as we'll need to do a query again once all the members are fetched - // by calling _trackRoomDevices - if (roomId in this.roomDeviceTrackingState) { - if (member.membership == "join") { - logger_1.logger.log("Join event for " + member.userId + " in " + roomId); - // make sure we are tracking the deviceList for this user - this.deviceList.startTrackingDeviceList(member.userId); - } - else if (member.membership == "invite" && - ((_a = this.clientStore.getRoom(roomId)) === null || _a === void 0 ? void 0 : _a.shouldEncryptForInvitedMembers())) { - logger_1.logger.log("Invite event for " + member.userId + " in " + roomId); - this.deviceList.startTrackingDeviceList(member.userId); - } - } - alg.onRoomMembership(event, member, oldMembership); - } - /** - * Called when we get an m.room_key_request event. - * - * @internal - * @param event - key request event - */ - onRoomKeyRequestEvent(event) { - const content = event.getContent(); - if (content.action === "request") { - // Queue it up for now, because they tend to arrive before the room state - // events at initial sync, and we want to see if we know anything about the - // room before passing them on to the app. - const req = new IncomingRoomKeyRequest(event); - this.receivedRoomKeyRequests.push(req); - } - else if (content.action === "request_cancellation") { - const req = new IncomingRoomKeyRequestCancellation(event); - this.receivedRoomKeyRequestCancellations.push(req); - } - } - /** - * Process any m.room_key_request events which were queued up during the - * current sync. - * - * @internal - */ - processReceivedRoomKeyRequests() { - return __awaiter(this, void 0, void 0, function* () { - if (this.processingRoomKeyRequests) { - // we're still processing last time's requests; keep queuing new ones - // up for now. - return; - } - this.processingRoomKeyRequests = true; - try { - // we need to grab and clear the queues in the synchronous bit of this method, - // so that we don't end up racing with the next /sync. - const requests = this.receivedRoomKeyRequests; - this.receivedRoomKeyRequests = []; - const cancellations = this.receivedRoomKeyRequestCancellations; - this.receivedRoomKeyRequestCancellations = []; - // Process all of the requests, *then* all of the cancellations. - // - // This makes sure that if we get a request and its cancellation in the - // same /sync result, then we process the request before the - // cancellation (and end up with a cancelled request), rather than the - // cancellation before the request (and end up with an outstanding - // request which should have been cancelled.) - yield Promise.all(requests.map((req) => this.processReceivedRoomKeyRequest(req))); - yield Promise.all(cancellations.map((cancellation) => this.processReceivedRoomKeyRequestCancellation(cancellation))); - } - catch (e) { - logger_1.logger.error(`Error processing room key requsts: ${e}`); - } - finally { - this.processingRoomKeyRequests = false; - } - }); - } - /** - * Helper for processReceivedRoomKeyRequests - * - */ - processReceivedRoomKeyRequest(req) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.userId; - const deviceId = req.deviceId; - const body = req.requestBody; - const roomId = body.room_id; - const alg = body.algorithm; - logger_1.logger.log(`m.room_key_request from ${userId}:${deviceId}` + - ` for ${roomId} / ${body.session_id} (id ${req.requestId})`); - if (userId !== this.userId) { - if (!this.roomEncryptors.get(roomId)) { - logger_1.logger.debug(`room key request for unencrypted room ${roomId}`); - return; - } - const encryptor = this.roomEncryptors.get(roomId); - const device = this.deviceList.getStoredDevice(userId, deviceId); - if (!device) { - logger_1.logger.debug(`Ignoring keyshare for unknown device ${userId}:${deviceId}`); - return; - } - try { - yield encryptor.reshareKeyWithDevice(body.sender_key, body.session_id, userId, device); - } - catch (e) { - logger_1.logger.warn("Failed to re-share keys for session " + - body.session_id + - " with device " + - userId + - ":" + - device.deviceId, e); - } - return; - } - if (deviceId === this.deviceId) { - // We'll always get these because we send room key requests to - // '*' (ie. 'all devices') which includes the sending device, - // so ignore requests from ourself because apart from it being - // very silly, it won't work because an Olm session cannot send - // messages to itself. - // The log here is probably superfluous since we know this will - // always happen, but let's log anyway for now just in case it - // causes issues. - logger_1.logger.log("Ignoring room key request from ourselves"); - return; - } - // todo: should we queue up requests we don't yet have keys for, - // in case they turn up later? - // if we don't have a decryptor for this room/alg, we don't have - // the keys for the requested events, and can drop the requests. - if (!this.roomDecryptors.has(roomId)) { - logger_1.logger.log(`room key request for unencrypted room ${roomId}`); - return; - } - const decryptor = this.roomDecryptors.get(roomId).get(alg); - if (!decryptor) { - logger_1.logger.log(`room key request for unknown alg ${alg} in room ${roomId}`); - return; - } - if (!(yield decryptor.hasKeysForKeyRequest(req))) { - logger_1.logger.log(`room key request for unknown session ${roomId} / ` + body.session_id); - return; - } - req.share = () => { - decryptor.shareKeysWithDevice(req); - }; - // if the device is verified already, share the keys - if (this.checkDeviceTrust(userId, deviceId).isVerified()) { - logger_1.logger.log("device is already verified: sharing keys"); - req.share(); - return; - } - this.emit(CryptoEvent.RoomKeyRequest, req); - }); - } - /** - * Helper for processReceivedRoomKeyRequests - * - */ - processReceivedRoomKeyRequestCancellation(cancellation) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`m.room_key_request cancellation for ${cancellation.userId}:` + - `${cancellation.deviceId} (id ${cancellation.requestId})`); - // we should probably only notify the app of cancellations we told it - // about, but we don't currently have a record of that, so we just pass - // everything through. - this.emit(CryptoEvent.RoomKeyRequestCancellation, cancellation); - }); - } - /** - * Get a decryptor for a given room and algorithm. - * - * If we already have a decryptor for the given room and algorithm, return - * it. Otherwise try to instantiate it. - * - * @internal - * - * @param roomId - room id for decryptor. If undefined, a temporary - * decryptor is instantiated. - * - * @param algorithm - crypto algorithm - * - * @throws {@link DecryptionError} if the algorithm is unknown - */ - getRoomDecryptor(roomId, algorithm) { - let decryptors; - let alg; - if (roomId) { - decryptors = this.roomDecryptors.get(roomId); - if (!decryptors) { - decryptors = new Map(); - this.roomDecryptors.set(roomId, decryptors); - } - alg = decryptors.get(algorithm); - if (alg) { - return alg; - } - } - const AlgClass = algorithms.DECRYPTION_CLASSES.get(algorithm); - if (!AlgClass) { - throw new algorithms.DecryptionError("UNKNOWN_ENCRYPTION_ALGORITHM", 'Unknown encryption algorithm "' + algorithm + '".'); - } - alg = new AlgClass({ - userId: this.userId, - crypto: this, - olmDevice: this.olmDevice, - baseApis: this.baseApis, - roomId: roomId !== null && roomId !== void 0 ? roomId : undefined, - }); - if (decryptors) { - decryptors.set(algorithm, alg); - } - return alg; - } - /** - * Get all the room decryptors for a given encryption algorithm. - * - * @param algorithm - The encryption algorithm - * - * @returns An array of room decryptors - */ - getRoomDecryptors(algorithm) { - const decryptors = []; - for (const d of this.roomDecryptors.values()) { - if (d.has(algorithm)) { - decryptors.push(d.get(algorithm)); - } - } - return decryptors; - } - /** - * sign the given object with our ed25519 key - * - * @param obj - Object to which we will add a 'signatures' property - */ - signObject(obj) { - return __awaiter(this, void 0, void 0, function* () { - const sigs = new Map(Object.entries(obj.signatures || {})); - const unsigned = obj.unsigned; - delete obj.signatures; - delete obj.unsigned; - const userSignatures = sigs.get(this.userId) || {}; - sigs.set(this.userId, userSignatures); - userSignatures["ed25519:" + this.deviceId] = yield this.olmDevice.sign(another_json_1.default.stringify(obj)); - obj.signatures = (0, utils_1.recursiveMapToObject)(sigs); - if (unsigned !== undefined) - obj.unsigned = unsigned; - }); - } -} -exports.Crypto = Crypto; -/** - * Fix up the backup key, that may be in the wrong format due to a bug in a - * migration step. Some backup keys were stored as a comma-separated list of - * integers, rather than a base64-encoded byte array. If this function is - * passed a string that looks like a list of integers rather than a base64 - * string, it will attempt to convert it to the right format. - * - * @param key - the key to check - * @returns If the key is in the wrong format, then the fixed - * key will be returned. Otherwise null will be returned. - * - */ -function fixBackupKey(key) { - if (typeof key !== "string" || key.indexOf(",") < 0) { - return null; - } - const fixedKey = Uint8Array.from(key.split(","), (x) => parseInt(x)); - return olmlib.encodeBase64(fixedKey); -} -exports.fixBackupKey = fixBackupKey; -/** - * Represents a received m.room_key_request event - */ -class IncomingRoomKeyRequest { - constructor(event) { - const content = event.getContent(); - this.userId = event.getSender(); - this.deviceId = content.requesting_device_id; - this.requestId = content.request_id; - this.requestBody = content.body || {}; - this.share = () => { - throw new Error("don't know how to share keys for this request yet"); - }; - } -} -exports.IncomingRoomKeyRequest = IncomingRoomKeyRequest; -/** - * Represents a received m.room_key_request cancellation - */ -class IncomingRoomKeyRequestCancellation { - constructor(event) { - const content = event.getContent(); - this.userId = event.getSender(); - this.deviceId = content.requesting_device_id; - this.requestId = content.request_id; - } -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) - -},{"../@types/event":306,"../ReEmitter":317,"../client":321,"../errors":359,"../logger":374,"../models/event":383,"../models/room":392,"../models/room-member":389,"../models/room-state":390,"../models/typed-event-emitter":395,"../utils":416,"./CrossSigning":324,"./DeviceList":325,"./EncryptionSetup":326,"./OlmDevice":327,"./OutgoingRoomKeyRequestManager":328,"./SecretStorage":330,"./aes":331,"./algorithms":333,"./backup":337,"./dehydration":339,"./deviceinfo":340,"./key_passphrase":342,"./olmlib":343,"./recoverykey":344,"./store/indexeddb-crypto-store":346,"./verification/IllegalMethod":351,"./verification/QRCode":352,"./verification/SAS":353,"./verification/request/InRoomChannel":355,"./verification/request/ToDeviceChannel":356,"./verification/request/VerificationRequest":357,"another-json":1,"buffer":68,"uuid":287}],342:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2018 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.deriveKey = exports.keyFromPassphrase = exports.keyFromAuthData = void 0; -const randomstring_1 = require("../randomstring"); -const crypto_1 = require("./crypto"); -const DEFAULT_ITERATIONS = 500000; -const DEFAULT_BITSIZE = 256; -function keyFromAuthData(authData, password) { - if (!global.Olm) { - throw new Error("Olm is not available"); - } - if (!authData.private_key_salt || !authData.private_key_iterations) { - throw new Error("Salt and/or iterations not found: " + "this backup cannot be restored with a passphrase"); - } - return deriveKey(password, authData.private_key_salt, authData.private_key_iterations, authData.private_key_bits || DEFAULT_BITSIZE); -} -exports.keyFromAuthData = keyFromAuthData; -function keyFromPassphrase(password) { - return __awaiter(this, void 0, void 0, function* () { - if (!global.Olm) { - throw new Error("Olm is not available"); - } - const salt = (0, randomstring_1.randomString)(32); - const key = yield deriveKey(password, salt, DEFAULT_ITERATIONS, DEFAULT_BITSIZE); - return { key, salt, iterations: DEFAULT_ITERATIONS }; - }); -} -exports.keyFromPassphrase = keyFromPassphrase; -function deriveKey(password, salt, iterations, numBits = DEFAULT_BITSIZE) { - return __awaiter(this, void 0, void 0, function* () { - if (!crypto_1.subtleCrypto || !crypto_1.TextEncoder) { - throw new Error("Password-based backup is not available on this platform"); - } - const key = yield crypto_1.subtleCrypto.importKey("raw", new crypto_1.TextEncoder().encode(password), { name: "PBKDF2" }, false, [ - "deriveBits", - ]); - const keybits = yield crypto_1.subtleCrypto.deriveBits({ - name: "PBKDF2", - salt: new crypto_1.TextEncoder().encode(salt), - iterations: iterations, - hash: "SHA-512", - }, key, numBits); - return new Uint8Array(keybits); - }); -} -exports.deriveKey = deriveKey; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../randomstring":398,"./crypto":338}],343:[function(require,module,exports){ -(function (global,Buffer){(function (){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.decodeBase64 = exports.encodeUnpaddedBase64 = exports.encodeBase64 = exports.isOlmEncrypted = exports.pkVerify = exports.pkSign = exports.verifySignature = exports.ensureOlmSessionsForDevices = exports.getExistingOlmSessions = exports.encryptMessageForDevice = exports.MEGOLM_BACKUP_ALGORITHM = exports.MEGOLM_ALGORITHM = exports.OLM_ALGORITHM = void 0; -/** - * Utilities common to olm encryption algorithms - */ -const another_json_1 = __importDefault(require("another-json")); -const logger_1 = require("../logger"); -const event_1 = require("../@types/event"); -const utils_1 = require("../utils"); -var Algorithm; -(function (Algorithm) { - Algorithm["Olm"] = "m.olm.v1.curve25519-aes-sha2"; - Algorithm["Megolm"] = "m.megolm.v1.aes-sha2"; - Algorithm["MegolmBackup"] = "m.megolm_backup.v1.curve25519-aes-sha2"; -})(Algorithm || (Algorithm = {})); -/** - * matrix algorithm tag for olm - */ -exports.OLM_ALGORITHM = Algorithm.Olm; -/** - * matrix algorithm tag for megolm - */ -exports.MEGOLM_ALGORITHM = Algorithm.Megolm; -/** - * matrix algorithm tag for megolm backups - */ -exports.MEGOLM_BACKUP_ALGORITHM = Algorithm.MegolmBackup; -/** - * Encrypt an event payload for an Olm device - * - * @param resultsObject - The `ciphertext` property - * of the m.room.encrypted event to which to add our result - * - * @param olmDevice - olm.js wrapper - * @param payloadFields - fields to include in the encrypted payload - * - * Returns a promise which resolves (to undefined) when the payload - * has been encrypted into `resultsObject` - */ -function encryptMessageForDevice(resultsObject, ourUserId, ourDeviceId, olmDevice, recipientUserId, recipientDevice, payloadFields) { - return __awaiter(this, void 0, void 0, function* () { - const deviceKey = recipientDevice.getIdentityKey(); - const sessionId = yield olmDevice.getSessionIdForDevice(deviceKey); - if (sessionId === null) { - // If we don't have a session for a device then - // we can't encrypt a message for it. - logger_1.logger.log(`[olmlib.encryptMessageForDevice] Unable to find Olm session for device ` + - `${recipientUserId}:${recipientDevice.deviceId}`); - return; - } - logger_1.logger.log(`[olmlib.encryptMessageForDevice] Using Olm session ${sessionId} for device ` + - `${recipientUserId}:${recipientDevice.deviceId}`); - const payload = Object.assign({ sender: ourUserId, - // TODO this appears to no longer be used whatsoever - sender_device: ourDeviceId, - // Include the Ed25519 key so that the recipient knows what - // device this message came from. - // We don't need to include the curve25519 key since the - // recipient will already know this from the olm headers. - // When combined with the device keys retrieved from the - // homeserver signed by the ed25519 key this proves that - // the curve25519 key and the ed25519 key are owned by - // the same device. - keys: { - ed25519: olmDevice.deviceEd25519Key, - }, - // include the recipient device details in the payload, - // to avoid unknown key attacks, per - // https://github.com/vector-im/vector-web/issues/2483 - recipient: recipientUserId, recipient_keys: { - ed25519: recipientDevice.getFingerprint(), - } }, payloadFields); - // TODO: technically, a bunch of that stuff only needs to be included for - // pre-key messages: after that, both sides know exactly which devices are - // involved in the session. If we're looking to reduce data transfer in the - // future, we could elide them for subsequent messages. - resultsObject[deviceKey] = yield olmDevice.encryptMessage(deviceKey, sessionId, JSON.stringify(payload)); - }); -} -exports.encryptMessageForDevice = encryptMessageForDevice; -/** - * Get the existing olm sessions for the given devices, and the devices that - * don't have olm sessions. - * - * - * - * @param devicesByUser - map from userid to list of devices to ensure sessions for - * - * @returns resolves to an array. The first element of the array is a - * a map of user IDs to arrays of deviceInfo, representing the devices that - * don't have established olm sessions. The second element of the array is - * a map from userId to deviceId to {@link OlmSessionResult} - */ -function getExistingOlmSessions(olmDevice, baseApis, devicesByUser) { - return __awaiter(this, void 0, void 0, function* () { - // map user Id → DeviceInfo[] - const devicesWithoutSession = new utils_1.MapWithDefault(() => []); - // map user Id → device Id → IExistingOlmSession - const sessions = new utils_1.MapWithDefault(() => new Map()); - const promises = []; - for (const [userId, devices] of Object.entries(devicesByUser)) { - for (const deviceInfo of devices) { - const deviceId = deviceInfo.deviceId; - const key = deviceInfo.getIdentityKey(); - promises.push((() => __awaiter(this, void 0, void 0, function* () { - const sessionId = yield olmDevice.getSessionIdForDevice(key, true); - if (sessionId === null) { - devicesWithoutSession.getOrCreate(userId).push(deviceInfo); - } - else { - sessions.getOrCreate(userId).set(deviceId, { - device: deviceInfo, - sessionId: sessionId, - }); - } - }))()); - } - } - yield Promise.all(promises); - return [devicesWithoutSession, sessions]; - }); -} -exports.getExistingOlmSessions = getExistingOlmSessions; -/** - * Try to make sure we have established olm sessions for the given devices. - * - * @param devicesByUser - map from userid to list of devices to ensure sessions for - * - * @param force - If true, establish a new session even if one - * already exists. - * - * @param otkTimeout - The timeout in milliseconds when requesting - * one-time keys for establishing new olm sessions. - * - * @param failedServers - An array to fill with remote servers that - * failed to respond to one-time-key requests. - * - * @param log - A possibly customised log - * - * @returns resolves once the sessions are complete, to - * an Object mapping from userId to deviceId to - * {@link OlmSessionResult} - */ -function ensureOlmSessionsForDevices(olmDevice, baseApis, devicesByUser, force = false, otkTimeout, failedServers, log = logger_1.logger) { - var _a, _b, _c; - return __awaiter(this, void 0, void 0, function* () { - const devicesWithoutSession = [ - // [userId, deviceId], ... - ]; - // map user Id → device Id → IExistingOlmSession - const result = new Map(); - // map device key → resolve session fn - const resolveSession = new Map(); - // Mark all sessions this task intends to update as in progress. It is - // important to do this for all devices this task cares about in a single - // synchronous operation, as otherwise it is possible to have deadlocks - // where multiple tasks wait indefinitely on another task to update some set - // of common devices. - for (const devices of devicesByUser.values()) { - for (const deviceInfo of devices) { - const key = deviceInfo.getIdentityKey(); - if (key === olmDevice.deviceCurve25519Key) { - // We don't start sessions with ourself, so there's no need to - // mark it in progress. - continue; - } - if (!olmDevice.sessionsInProgress[key]) { - // pre-emptively mark the session as in-progress to avoid race - // conditions. If we find that we already have a session, then - // we'll resolve - olmDevice.sessionsInProgress[key] = new Promise((resolve) => { - resolveSession.set(key, (v) => { - delete olmDevice.sessionsInProgress[key]; - resolve(v); - }); - }); - } - } - } - for (const [userId, devices] of devicesByUser) { - const resultDevices = new Map(); - result.set(userId, resultDevices); - for (const deviceInfo of devices) { - const deviceId = deviceInfo.deviceId; - const key = deviceInfo.getIdentityKey(); - if (key === olmDevice.deviceCurve25519Key) { - // We should never be trying to start a session with ourself. - // Apart from talking to yourself being the first sign of madness, - // olm sessions can't do this because they get confused when - // they get a message and see that the 'other side' has started a - // new chain when this side has an active sender chain. - // If you see this message being logged in the wild, we should find - // the thing that is trying to send Olm messages to itself and fix it. - log.info("Attempted to start session with ourself! Ignoring"); - // We must fill in the section in the return value though, as callers - // expect it to be there. - resultDevices.set(deviceId, { - device: deviceInfo, - sessionId: null, - }); - continue; - } - const forWhom = `for ${key} (${userId}:${deviceId})`; - const sessionId = yield olmDevice.getSessionIdForDevice(key, !!resolveSession.get(key), log); - const resolveSessionFn = resolveSession.get(key); - if (sessionId !== null && resolveSessionFn) { - // we found a session, but we had marked the session as - // in-progress, so resolve it now, which will unmark it and - // unblock anything that was waiting - resolveSessionFn(); - } - if (sessionId === null || force) { - if (force) { - log.info(`Forcing new Olm session ${forWhom}`); - } - else { - log.info(`Making new Olm session ${forWhom}`); - } - devicesWithoutSession.push([userId, deviceId]); - } - resultDevices.set(deviceId, { - device: deviceInfo, - sessionId: sessionId, - }); - } - } - if (devicesWithoutSession.length === 0) { - return result; - } - const oneTimeKeyAlgorithm = "signed_curve25519"; - let res; - let taskDetail = `one-time keys for ${devicesWithoutSession.length} devices`; - try { - log.debug(`Claiming ${taskDetail}`); - res = yield baseApis.claimOneTimeKeys(devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout); - log.debug(`Claimed ${taskDetail}`); - } - catch (e) { - for (const resolver of resolveSession.values()) { - resolver(); - } - log.log(`Failed to claim ${taskDetail}`, e, devicesWithoutSession); - throw e; - } - if (failedServers && "failures" in res) { - failedServers.push(...Object.keys(res.failures)); - } - const otkResult = res.one_time_keys || {}; - const promises = []; - for (const [userId, devices] of devicesByUser) { - const userRes = otkResult[userId] || {}; - for (const deviceInfo of devices) { - const deviceId = deviceInfo.deviceId; - const key = deviceInfo.getIdentityKey(); - if (key === olmDevice.deviceCurve25519Key) { - // We've already logged about this above. Skip here too - // otherwise we'll log saying there are no one-time keys - // which will be confusing. - continue; - } - if (((_b = (_a = result.get(userId)) === null || _a === void 0 ? void 0 : _a.get(deviceId)) === null || _b === void 0 ? void 0 : _b.sessionId) && !force) { - // we already have a result for this device - continue; - } - const deviceRes = userRes[deviceId] || {}; - let oneTimeKey = null; - for (const keyId in deviceRes) { - if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) { - oneTimeKey = deviceRes[keyId]; - } - } - if (!oneTimeKey) { - log.warn(`No one-time keys (alg=${oneTimeKeyAlgorithm}) ` + `for device ${userId}:${deviceId}`); - (_c = resolveSession.get(key)) === null || _c === void 0 ? void 0 : _c(); - continue; - } - promises.push(_verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo).then((sid) => { - var _a, _b; - (_a = resolveSession.get(key)) === null || _a === void 0 ? void 0 : _a(sid !== null && sid !== void 0 ? sid : undefined); - const deviceInfo = (_b = result.get(userId)) === null || _b === void 0 ? void 0 : _b.get(deviceId); - if (deviceInfo) - deviceInfo.sessionId = sid; - }, (e) => { - var _a; - (_a = resolveSession.get(key)) === null || _a === void 0 ? void 0 : _a(); - throw e; - })); - } - } - taskDetail = `Olm sessions for ${promises.length} devices`; - log.debug(`Starting ${taskDetail}`); - yield Promise.all(promises); - log.debug(`Started ${taskDetail}`); - return result; - }); -} -exports.ensureOlmSessionsForDevices = ensureOlmSessionsForDevices; -function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) { - return __awaiter(this, void 0, void 0, function* () { - const deviceId = deviceInfo.deviceId; - try { - yield verifySignature(olmDevice, oneTimeKey, userId, deviceId, deviceInfo.getFingerprint()); - } - catch (e) { - logger_1.logger.error("Unable to verify signature on one-time key for device " + userId + ":" + deviceId + ":", e); - return null; - } - let sid; - try { - sid = yield olmDevice.createOutboundSession(deviceInfo.getIdentityKey(), oneTimeKey.key); - } - catch (e) { - // possibly a bad key - logger_1.logger.error("Error starting olm session with device " + userId + ":" + deviceId + ": " + e); - return null; - } - logger_1.logger.log("Started new olm sessionid " + sid + " for device " + userId + ":" + deviceId); - return sid; - }); -} -/** - * Verify the signature on an object - * - * @param olmDevice - olm wrapper to use for verify op - * - * @param obj - object to check signature on. - * - * @param signingUserId - ID of the user whose signature should be checked - * - * @param signingDeviceId - ID of the device whose signature should be checked - * - * @param signingKey - base64-ed ed25519 public key - * - * Returns a promise which resolves (to undefined) if the the signature is good, - * or rejects with an Error if it is bad. - */ -function verifySignature(olmDevice, obj, signingUserId, signingDeviceId, signingKey) { - return __awaiter(this, void 0, void 0, function* () { - const signKeyId = "ed25519:" + signingDeviceId; - const signatures = obj.signatures || {}; - const userSigs = signatures[signingUserId] || {}; - const signature = userSigs[signKeyId]; - if (!signature) { - throw Error("No signature"); - } - // prepare the canonical json: remove unsigned and signatures, and stringify with anotherjson - const mangledObj = Object.assign({}, obj); - if ("unsigned" in mangledObj) { - delete mangledObj.unsigned; - } - delete mangledObj.signatures; - const json = another_json_1.default.stringify(mangledObj); - olmDevice.verifySignature(signingKey, json, signature); - }); -} -exports.verifySignature = verifySignature; -/** - * Sign a JSON object using public key cryptography - * @param obj - Object to sign. The object will be modified to include - * the new signature - * @param key - the signing object or the private key - * seed - * @param userId - The user ID who owns the signing key - * @param pubKey - The public key (ignored if key is a seed) - * @returns the signature for the object - */ -function pkSign(obj, key, userId, pubKey) { - let createdKey = false; - if (key instanceof Uint8Array) { - const keyObj = new global.Olm.PkSigning(); - pubKey = keyObj.init_with_seed(key); - key = keyObj; - createdKey = true; - } - const sigs = obj.signatures || {}; - delete obj.signatures; - const unsigned = obj.unsigned; - if (obj.unsigned) - delete obj.unsigned; - try { - const mysigs = sigs[userId] || {}; - sigs[userId] = mysigs; - return (mysigs["ed25519:" + pubKey] = key.sign(another_json_1.default.stringify(obj))); - } - finally { - obj.signatures = sigs; - if (unsigned) - obj.unsigned = unsigned; - if (createdKey) { - key.free(); - } - } -} -exports.pkSign = pkSign; -/** - * Verify a signed JSON object - * @param obj - Object to verify - * @param pubKey - The public key to use to verify - * @param userId - The user ID who signed the object - */ -function pkVerify(obj, pubKey, userId) { - const keyId = "ed25519:" + pubKey; - if (!(obj.signatures && obj.signatures[userId] && obj.signatures[userId][keyId])) { - throw new Error("No signature"); - } - const signature = obj.signatures[userId][keyId]; - const util = new global.Olm.Utility(); - const sigs = obj.signatures; - delete obj.signatures; - const unsigned = obj.unsigned; - if (obj.unsigned) - delete obj.unsigned; - try { - util.ed25519_verify(pubKey, another_json_1.default.stringify(obj), signature); - } - finally { - obj.signatures = sigs; - if (unsigned) - obj.unsigned = unsigned; - util.free(); - } -} -exports.pkVerify = pkVerify; -/** - * Check that an event was encrypted using olm. - */ -function isOlmEncrypted(event) { - if (!event.getSenderKey()) { - logger_1.logger.error("Event has no sender key (not encrypted?)"); - return false; - } - if (event.getWireType() !== event_1.EventType.RoomMessageEncrypted || - !["m.olm.v1.curve25519-aes-sha2"].includes(event.getWireContent().algorithm)) { - logger_1.logger.error("Event was not encrypted using an appropriate algorithm"); - return false; - } - return true; -} -exports.isOlmEncrypted = isOlmEncrypted; -/** - * Encode a typed array of uint8 as base64. - * @param uint8Array - The data to encode. - * @returns The base64. - */ -function encodeBase64(uint8Array) { - return Buffer.from(uint8Array).toString("base64"); -} -exports.encodeBase64 = encodeBase64; -/** - * Encode a typed array of uint8 as unpadded base64. - * @param uint8Array - The data to encode. - * @returns The unpadded base64. - */ -function encodeUnpaddedBase64(uint8Array) { - return encodeBase64(uint8Array).replace(/=+$/g, ""); -} -exports.encodeUnpaddedBase64 = encodeUnpaddedBase64; -/** - * Decode a base64 string to a typed array of uint8. - * @param base64 - The base64 to decode. - * @returns The decoded data. - */ -function decodeBase64(base64) { - return Buffer.from(base64, "base64"); -} -exports.decodeBase64 = decodeBase64; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) - -},{"../@types/event":306,"../logger":374,"../utils":416,"another-json":1,"buffer":68}],344:[function(require,module,exports){ -(function (global,Buffer){(function (){ -"use strict"; -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.decodeRecoveryKey = exports.encodeRecoveryKey = void 0; -const bs58 = __importStar(require("bs58")); -// picked arbitrarily but to try & avoid clashing with any bitcoin ones -// (which are also base58 encoded, but bitcoin's involve a lot more hashing) -const OLM_RECOVERY_KEY_PREFIX = [0x8b, 0x01]; -function encodeRecoveryKey(key) { - var _a; - const buf = Buffer.alloc(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1); - buf.set(OLM_RECOVERY_KEY_PREFIX, 0); - buf.set(key, OLM_RECOVERY_KEY_PREFIX.length); - let parity = 0; - for (let i = 0; i < buf.length - 1; ++i) { - parity ^= buf[i]; - } - buf[buf.length - 1] = parity; - const base58key = bs58.encode(buf); - return (_a = base58key.match(/.{1,4}/g)) === null || _a === void 0 ? void 0 : _a.join(" "); -} -exports.encodeRecoveryKey = encodeRecoveryKey; -function decodeRecoveryKey(recoveryKey) { - const result = bs58.decode(recoveryKey.replace(/ /g, "")); - let parity = 0; - for (const b of result) { - parity ^= b; - } - if (parity !== 0) { - throw new Error("Incorrect parity"); - } - for (let i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; ++i) { - if (result[i] !== OLM_RECOVERY_KEY_PREFIX[i]) { - throw new Error("Incorrect prefix"); - } - } - if (result.length !== OLM_RECOVERY_KEY_PREFIX.length + global.Olm.PRIVATE_KEY_LENGTH + 1) { - throw new Error("Incorrect length"); - } - return Uint8Array.from(result.slice(OLM_RECOVERY_KEY_PREFIX.length, OLM_RECOVERY_KEY_PREFIX.length + global.Olm.PRIVATE_KEY_LENGTH)); -} -exports.decodeRecoveryKey = decodeRecoveryKey; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) - -},{"bs58":65,"buffer":68}],345:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.upgradeDatabase = exports.VERSION = exports.Backend = void 0; -const logger_1 = require("../../logger"); -const utils = __importStar(require("../../utils")); -const PROFILE_TRANSACTIONS = false; -/** - * Implementation of a CryptoStore which is backed by an existing - * IndexedDB connection. Generally you want IndexedDBCryptoStore - * which connects to the database and defers to one of these. - */ -class Backend { - /** - */ - constructor(db) { - this.db = db; - this.nextTxnId = 0; - // make sure we close the db on `onversionchange` - otherwise - // attempts to delete the database will block (and subsequent - // attempts to re-create it will also block). - db.onversionchange = () => { - logger_1.logger.log(`versionchange for indexeddb ${this.db.name}: closing`); - db.close(); - }; - } - startup() { - return __awaiter(this, void 0, void 0, function* () { - // No work to do, as the startup is done by the caller (e.g IndexedDBCryptoStore) - // by passing us a ready IDBDatabase instance - return this; - }); - } - deleteAllData() { - return __awaiter(this, void 0, void 0, function* () { - throw Error("This is not implemented, call IDBFactory::deleteDatabase(dbName) instead."); - }); - } - /** - * Look for an existing outgoing room key request, and if none is found, - * add a new one - * - * - * @returns resolves to - * {@link OutgoingRoomKeyRequest}: either the - * same instance as passed in, or the existing one. - */ - getOrAddOutgoingRoomKeyRequest(request) { - const requestBody = request.requestBody; - return new Promise((resolve, reject) => { - const txn = this.db.transaction("outgoingRoomKeyRequests", "readwrite"); - txn.onerror = reject; - // first see if we already have an entry for this request. - this._getOutgoingRoomKeyRequest(txn, requestBody, (existing) => { - if (existing) { - // this entry matches the request - return it. - logger_1.logger.log(`already have key request outstanding for ` + - `${requestBody.room_id} / ${requestBody.session_id}: ` + - `not sending another`); - resolve(existing); - return; - } - // we got to the end of the list without finding a match - // - add the new request. - logger_1.logger.log(`enqueueing key request for ${requestBody.room_id} / ` + requestBody.session_id); - txn.oncomplete = () => { - resolve(request); - }; - const store = txn.objectStore("outgoingRoomKeyRequests"); - store.add(request); - }); - }); - } - /** - * Look for an existing room key request - * - * @param requestBody - existing request to look for - * - * @returns resolves to the matching - * {@link OutgoingRoomKeyRequest}, or null if - * not found - */ - getOutgoingRoomKeyRequest(requestBody) { - return new Promise((resolve, reject) => { - const txn = this.db.transaction("outgoingRoomKeyRequests", "readonly"); - txn.onerror = reject; - this._getOutgoingRoomKeyRequest(txn, requestBody, (existing) => { - resolve(existing); - }); - }); - } - /** - * look for an existing room key request in the db - * - * @internal - * @param txn - database transaction - * @param requestBody - existing request to look for - * @param callback - function to call with the results of the - * search. Either passed a matching - * {@link OutgoingRoomKeyRequest}, or null if - * not found. - */ - // eslint-disable-next-line @typescript-eslint/naming-convention - _getOutgoingRoomKeyRequest(txn, requestBody, callback) { - const store = txn.objectStore("outgoingRoomKeyRequests"); - const idx = store.index("session"); - const cursorReq = idx.openCursor([requestBody.room_id, requestBody.session_id]); - cursorReq.onsuccess = () => { - const cursor = cursorReq.result; - if (!cursor) { - // no match found - callback(null); - return; - } - const existing = cursor.value; - if (utils.deepCompare(existing.requestBody, requestBody)) { - // got a match - callback(existing); - return; - } - // look at the next entry in the index - cursor.continue(); - }; - } - /** - * Look for room key requests by state - * - * @param wantedStates - list of acceptable states - * - * @returns resolves to the a - * {@link OutgoingRoomKeyRequest}, or null if - * there are no pending requests in those states. If there are multiple - * requests in those states, an arbitrary one is chosen. - */ - getOutgoingRoomKeyRequestByState(wantedStates) { - if (wantedStates.length === 0) { - return Promise.resolve(null); - } - // this is a bit tortuous because we need to make sure we do the lookup - // in a single transaction, to avoid having a race with the insertion - // code. - // index into the wantedStates array - let stateIndex = 0; - let result; - function onsuccess() { - const cursor = this.result; - if (cursor) { - // got a match - result = cursor.value; - return; - } - // try the next state in the list - stateIndex++; - if (stateIndex >= wantedStates.length) { - // no matches - return; - } - const wantedState = wantedStates[stateIndex]; - const cursorReq = this.source.openCursor(wantedState); - cursorReq.onsuccess = onsuccess; - } - const txn = this.db.transaction("outgoingRoomKeyRequests", "readonly"); - const store = txn.objectStore("outgoingRoomKeyRequests"); - const wantedState = wantedStates[stateIndex]; - const cursorReq = store.index("state").openCursor(wantedState); - cursorReq.onsuccess = onsuccess; - return promiseifyTxn(txn).then(() => result); - } - /** - * - * @returns All elements in a given state - */ - getAllOutgoingRoomKeyRequestsByState(wantedState) { - return new Promise((resolve, reject) => { - const txn = this.db.transaction("outgoingRoomKeyRequests", "readonly"); - const store = txn.objectStore("outgoingRoomKeyRequests"); - const index = store.index("state"); - const request = index.getAll(wantedState); - request.onsuccess = () => resolve(request.result); - request.onerror = () => reject(request.error); - }); - } - getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates) { - let stateIndex = 0; - const results = []; - function onsuccess() { - const cursor = this.result; - if (cursor) { - const keyReq = cursor.value; - if (keyReq.recipients.some((recipient) => recipient.userId === userId && recipient.deviceId === deviceId)) { - results.push(keyReq); - } - cursor.continue(); - } - else { - // try the next state in the list - stateIndex++; - if (stateIndex >= wantedStates.length) { - // no matches - return; - } - const wantedState = wantedStates[stateIndex]; - const cursorReq = this.source.openCursor(wantedState); - cursorReq.onsuccess = onsuccess; - } - } - const txn = this.db.transaction("outgoingRoomKeyRequests", "readonly"); - const store = txn.objectStore("outgoingRoomKeyRequests"); - const wantedState = wantedStates[stateIndex]; - const cursorReq = store.index("state").openCursor(wantedState); - cursorReq.onsuccess = onsuccess; - return promiseifyTxn(txn).then(() => results); - } - /** - * Look for an existing room key request by id and state, and update it if - * found - * - * @param requestId - ID of request to update - * @param expectedState - state we expect to find the request in - * @param updates - name/value map of updates to apply - * - * @returns resolves to - * {@link OutgoingRoomKeyRequest} - * updated request, or null if no matching row was found - */ - updateOutgoingRoomKeyRequest(requestId, expectedState, updates) { - let result = null; - function onsuccess() { - const cursor = this.result; - if (!cursor) { - return; - } - const data = cursor.value; - if (data.state != expectedState) { - logger_1.logger.warn(`Cannot update room key request from ${expectedState} ` + - `as it was already updated to ${data.state}`); - return; - } - Object.assign(data, updates); - cursor.update(data); - result = data; - } - const txn = this.db.transaction("outgoingRoomKeyRequests", "readwrite"); - const cursorReq = txn.objectStore("outgoingRoomKeyRequests").openCursor(requestId); - cursorReq.onsuccess = onsuccess; - return promiseifyTxn(txn).then(() => result); - } - /** - * Look for an existing room key request by id and state, and delete it if - * found - * - * @param requestId - ID of request to update - * @param expectedState - state we expect to find the request in - * - * @returns resolves once the operation is completed - */ - deleteOutgoingRoomKeyRequest(requestId, expectedState) { - const txn = this.db.transaction("outgoingRoomKeyRequests", "readwrite"); - const cursorReq = txn.objectStore("outgoingRoomKeyRequests").openCursor(requestId); - cursorReq.onsuccess = () => { - const cursor = cursorReq.result; - if (!cursor) { - return; - } - const data = cursor.value; - if (data.state != expectedState) { - logger_1.logger.warn(`Cannot delete room key request in state ${data.state} ` + `(expected ${expectedState})`); - return; - } - cursor.delete(); - }; - return promiseifyTxn(txn); - } - // Olm Account - getAccount(txn, func) { - const objectStore = txn.objectStore("account"); - const getReq = objectStore.get("-"); - getReq.onsuccess = function () { - try { - func(getReq.result || null); - } - catch (e) { - abortWithException(txn, e); - } - }; - } - storeAccount(txn, accountPickle) { - const objectStore = txn.objectStore("account"); - objectStore.put(accountPickle, "-"); - } - getCrossSigningKeys(txn, func) { - const objectStore = txn.objectStore("account"); - const getReq = objectStore.get("crossSigningKeys"); - getReq.onsuccess = function () { - try { - func(getReq.result || null); - } - catch (e) { - abortWithException(txn, e); - } - }; - } - getSecretStorePrivateKey(txn, func, type) { - const objectStore = txn.objectStore("account"); - const getReq = objectStore.get(`ssss_cache:${type}`); - getReq.onsuccess = function () { - try { - func(getReq.result || null); - } - catch (e) { - abortWithException(txn, e); - } - }; - } - storeCrossSigningKeys(txn, keys) { - const objectStore = txn.objectStore("account"); - objectStore.put(keys, "crossSigningKeys"); - } - storeSecretStorePrivateKey(txn, type, key) { - const objectStore = txn.objectStore("account"); - objectStore.put(key, `ssss_cache:${type}`); - } - // Olm Sessions - countEndToEndSessions(txn, func) { - const objectStore = txn.objectStore("sessions"); - const countReq = objectStore.count(); - countReq.onsuccess = function () { - try { - func(countReq.result); - } - catch (e) { - abortWithException(txn, e); - } - }; - } - getEndToEndSessions(deviceKey, txn, func) { - const objectStore = txn.objectStore("sessions"); - const idx = objectStore.index("deviceKey"); - const getReq = idx.openCursor(deviceKey); - const results = {}; - getReq.onsuccess = function () { - const cursor = getReq.result; - if (cursor) { - results[cursor.value.sessionId] = { - session: cursor.value.session, - lastReceivedMessageTs: cursor.value.lastReceivedMessageTs, - }; - cursor.continue(); - } - else { - try { - func(results); - } - catch (e) { - abortWithException(txn, e); - } - } - }; - } - getEndToEndSession(deviceKey, sessionId, txn, func) { - const objectStore = txn.objectStore("sessions"); - const getReq = objectStore.get([deviceKey, sessionId]); - getReq.onsuccess = function () { - try { - if (getReq.result) { - func({ - session: getReq.result.session, - lastReceivedMessageTs: getReq.result.lastReceivedMessageTs, - }); - } - else { - func(null); - } - } - catch (e) { - abortWithException(txn, e); - } - }; - } - getAllEndToEndSessions(txn, func) { - const objectStore = txn.objectStore("sessions"); - const getReq = objectStore.openCursor(); - getReq.onsuccess = function () { - try { - const cursor = getReq.result; - if (cursor) { - func(cursor.value); - cursor.continue(); - } - else { - func(null); - } - } - catch (e) { - abortWithException(txn, e); - } - }; - } - storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { - const objectStore = txn.objectStore("sessions"); - objectStore.put({ - deviceKey, - sessionId, - session: sessionInfo.session, - lastReceivedMessageTs: sessionInfo.lastReceivedMessageTs, - }); - } - storeEndToEndSessionProblem(deviceKey, type, fixed) { - return __awaiter(this, void 0, void 0, function* () { - const txn = this.db.transaction("session_problems", "readwrite"); - const objectStore = txn.objectStore("session_problems"); - objectStore.put({ - deviceKey, - type, - fixed, - time: Date.now(), - }); - yield promiseifyTxn(txn); - }); - } - getEndToEndSessionProblem(deviceKey, timestamp) { - return __awaiter(this, void 0, void 0, function* () { - let result = null; - const txn = this.db.transaction("session_problems", "readwrite"); - const objectStore = txn.objectStore("session_problems"); - const index = objectStore.index("deviceKey"); - const req = index.getAll(deviceKey); - req.onsuccess = () => { - const problems = req.result; - if (!problems.length) { - result = null; - return; - } - problems.sort((a, b) => { - return a.time - b.time; - }); - const lastProblem = problems[problems.length - 1]; - for (const problem of problems) { - if (problem.time > timestamp) { - result = Object.assign({}, problem, { fixed: lastProblem.fixed }); - return; - } - } - if (lastProblem.fixed) { - result = null; - } - else { - result = lastProblem; - } - }; - yield promiseifyTxn(txn); - return result; - }); - } - // FIXME: we should probably prune this when devices get deleted - filterOutNotifiedErrorDevices(devices) { - return __awaiter(this, void 0, void 0, function* () { - const txn = this.db.transaction("notified_error_devices", "readwrite"); - const objectStore = txn.objectStore("notified_error_devices"); - const ret = []; - yield Promise.all(devices.map((device) => { - return new Promise((resolve) => { - const { userId, deviceInfo } = device; - const getReq = objectStore.get([userId, deviceInfo.deviceId]); - getReq.onsuccess = function () { - if (!getReq.result) { - objectStore.put({ userId, deviceId: deviceInfo.deviceId }); - ret.push(device); - } - resolve(); - }; - }); - })); - return ret; - }); - } - // Inbound group sessions - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { - let session = false; - let withheld = false; - const objectStore = txn.objectStore("inbound_group_sessions"); - const getReq = objectStore.get([senderCurve25519Key, sessionId]); - getReq.onsuccess = function () { - try { - if (getReq.result) { - session = getReq.result.session; - } - else { - session = null; - } - if (withheld !== false) { - func(session, withheld); - } - } - catch (e) { - abortWithException(txn, e); - } - }; - const withheldObjectStore = txn.objectStore("inbound_group_sessions_withheld"); - const withheldGetReq = withheldObjectStore.get([senderCurve25519Key, sessionId]); - withheldGetReq.onsuccess = function () { - try { - if (withheldGetReq.result) { - withheld = withheldGetReq.result.session; - } - else { - withheld = null; - } - if (session !== false) { - func(session, withheld); - } - } - catch (e) { - abortWithException(txn, e); - } - }; - } - getAllEndToEndInboundGroupSessions(txn, func) { - const objectStore = txn.objectStore("inbound_group_sessions"); - const getReq = objectStore.openCursor(); - getReq.onsuccess = function () { - const cursor = getReq.result; - if (cursor) { - try { - func({ - senderKey: cursor.value.senderCurve25519Key, - sessionId: cursor.value.sessionId, - sessionData: cursor.value.session, - }); - } - catch (e) { - abortWithException(txn, e); - } - cursor.continue(); - } - else { - try { - func(null); - } - catch (e) { - abortWithException(txn, e); - } - } - }; - } - addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - const objectStore = txn.objectStore("inbound_group_sessions"); - const addReq = objectStore.add({ - senderCurve25519Key, - sessionId, - session: sessionData, - }); - addReq.onerror = (ev) => { - var _a; - if (((_a = addReq.error) === null || _a === void 0 ? void 0 : _a.name) === "ConstraintError") { - // This stops the error from triggering the txn's onerror - ev.stopPropagation(); - // ...and this stops it from aborting the transaction - ev.preventDefault(); - logger_1.logger.log("Ignoring duplicate inbound group session: " + senderCurve25519Key + " / " + sessionId); - } - else { - abortWithException(txn, new Error("Failed to add inbound group session: " + addReq.error)); - } - }; - } - storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - const objectStore = txn.objectStore("inbound_group_sessions"); - objectStore.put({ - senderCurve25519Key, - sessionId, - session: sessionData, - }); - } - storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn) { - const objectStore = txn.objectStore("inbound_group_sessions_withheld"); - objectStore.put({ - senderCurve25519Key, - sessionId, - session: sessionData, - }); - } - getEndToEndDeviceData(txn, func) { - const objectStore = txn.objectStore("device_data"); - const getReq = objectStore.get("-"); - getReq.onsuccess = function () { - try { - func(getReq.result || null); - } - catch (e) { - abortWithException(txn, e); - } - }; - } - storeEndToEndDeviceData(deviceData, txn) { - const objectStore = txn.objectStore("device_data"); - objectStore.put(deviceData, "-"); - } - storeEndToEndRoom(roomId, roomInfo, txn) { - const objectStore = txn.objectStore("rooms"); - objectStore.put(roomInfo, roomId); - } - getEndToEndRooms(txn, func) { - const rooms = {}; - const objectStore = txn.objectStore("rooms"); - const getReq = objectStore.openCursor(); - getReq.onsuccess = function () { - const cursor = getReq.result; - if (cursor) { - rooms[cursor.key] = cursor.value; - cursor.continue(); - } - else { - try { - func(rooms); - } - catch (e) { - abortWithException(txn, e); - } - } - }; - } - // session backups - getSessionsNeedingBackup(limit) { - return new Promise((resolve, reject) => { - const sessions = []; - const txn = this.db.transaction(["sessions_needing_backup", "inbound_group_sessions"], "readonly"); - txn.onerror = reject; - txn.oncomplete = function () { - resolve(sessions); - }; - const objectStore = txn.objectStore("sessions_needing_backup"); - const sessionStore = txn.objectStore("inbound_group_sessions"); - const getReq = objectStore.openCursor(); - getReq.onsuccess = function () { - const cursor = getReq.result; - if (cursor) { - const sessionGetReq = sessionStore.get(cursor.key); - sessionGetReq.onsuccess = function () { - sessions.push({ - senderKey: sessionGetReq.result.senderCurve25519Key, - sessionId: sessionGetReq.result.sessionId, - sessionData: sessionGetReq.result.session, - }); - }; - if (!limit || sessions.length < limit) { - cursor.continue(); - } - } - }; - }); - } - countSessionsNeedingBackup(txn) { - if (!txn) { - txn = this.db.transaction("sessions_needing_backup", "readonly"); - } - const objectStore = txn.objectStore("sessions_needing_backup"); - return new Promise((resolve, reject) => { - const req = objectStore.count(); - req.onerror = reject; - req.onsuccess = () => resolve(req.result); - }); - } - unmarkSessionsNeedingBackup(sessions, txn) { - return __awaiter(this, void 0, void 0, function* () { - if (!txn) { - txn = this.db.transaction("sessions_needing_backup", "readwrite"); - } - const objectStore = txn.objectStore("sessions_needing_backup"); - yield Promise.all(sessions.map((session) => { - return new Promise((resolve, reject) => { - const req = objectStore.delete([session.senderKey, session.sessionId]); - req.onsuccess = resolve; - req.onerror = reject; - }); - })); - }); - } - markSessionsNeedingBackup(sessions, txn) { - return __awaiter(this, void 0, void 0, function* () { - if (!txn) { - txn = this.db.transaction("sessions_needing_backup", "readwrite"); - } - const objectStore = txn.objectStore("sessions_needing_backup"); - yield Promise.all(sessions.map((session) => { - return new Promise((resolve, reject) => { - const req = objectStore.put({ - senderCurve25519Key: session.senderKey, - sessionId: session.sessionId, - }); - req.onsuccess = resolve; - req.onerror = reject; - }); - })); - }); - } - addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId, txn) { - if (!txn) { - txn = this.db.transaction("shared_history_inbound_group_sessions", "readwrite"); - } - const objectStore = txn.objectStore("shared_history_inbound_group_sessions"); - const req = objectStore.get([roomId]); - req.onsuccess = () => { - const { sessions } = req.result || { sessions: [] }; - sessions.push([senderKey, sessionId]); - objectStore.put({ roomId, sessions }); - }; - } - getSharedHistoryInboundGroupSessions(roomId, txn) { - if (!txn) { - txn = this.db.transaction("shared_history_inbound_group_sessions", "readonly"); - } - const objectStore = txn.objectStore("shared_history_inbound_group_sessions"); - const req = objectStore.get([roomId]); - return new Promise((resolve, reject) => { - req.onsuccess = () => { - const { sessions } = req.result || { sessions: [] }; - resolve(sessions); - }; - req.onerror = reject; - }); - } - addParkedSharedHistory(roomId, parkedData, txn) { - if (!txn) { - txn = this.db.transaction("parked_shared_history", "readwrite"); - } - const objectStore = txn.objectStore("parked_shared_history"); - const req = objectStore.get([roomId]); - req.onsuccess = () => { - const { parked } = req.result || { parked: [] }; - parked.push(parkedData); - objectStore.put({ roomId, parked }); - }; - } - takeParkedSharedHistory(roomId, txn) { - if (!txn) { - txn = this.db.transaction("parked_shared_history", "readwrite"); - } - const cursorReq = txn.objectStore("parked_shared_history").openCursor(roomId); - return new Promise((resolve, reject) => { - cursorReq.onsuccess = () => { - const cursor = cursorReq.result; - if (!cursor) { - resolve([]); - return; - } - const data = cursor.value; - cursor.delete(); - resolve(data); - }; - cursorReq.onerror = reject; - }); - } - doTxn(mode, stores, func, log = logger_1.logger) { - let startTime; - let description; - if (PROFILE_TRANSACTIONS) { - const txnId = this.nextTxnId++; - startTime = Date.now(); - description = `${mode} crypto store transaction ${txnId} in ${stores}`; - log.debug(`Starting ${description}`); - } - const txn = this.db.transaction(stores, mode); - const promise = promiseifyTxn(txn); - const result = func(txn); - if (PROFILE_TRANSACTIONS) { - promise.then(() => { - const elapsedTime = Date.now() - startTime; - log.debug(`Finished ${description}, took ${elapsedTime} ms`); - }, () => { - const elapsedTime = Date.now() - startTime; - log.error(`Failed ${description}, took ${elapsedTime} ms`); - }); - } - return promise.then(() => { - return result; - }); - } -} -exports.Backend = Backend; -const DB_MIGRATIONS = [ - (db) => { - createDatabase(db); - }, - (db) => { - db.createObjectStore("account"); - }, - (db) => { - const sessionsStore = db.createObjectStore("sessions", { - keyPath: ["deviceKey", "sessionId"], - }); - sessionsStore.createIndex("deviceKey", "deviceKey"); - }, - (db) => { - db.createObjectStore("inbound_group_sessions", { - keyPath: ["senderCurve25519Key", "sessionId"], - }); - }, - (db) => { - db.createObjectStore("device_data"); - }, - (db) => { - db.createObjectStore("rooms"); - }, - (db) => { - db.createObjectStore("sessions_needing_backup", { - keyPath: ["senderCurve25519Key", "sessionId"], - }); - }, - (db) => { - db.createObjectStore("inbound_group_sessions_withheld", { - keyPath: ["senderCurve25519Key", "sessionId"], - }); - }, - (db) => { - const problemsStore = db.createObjectStore("session_problems", { - keyPath: ["deviceKey", "time"], - }); - problemsStore.createIndex("deviceKey", "deviceKey"); - db.createObjectStore("notified_error_devices", { - keyPath: ["userId", "deviceId"], - }); - }, - (db) => { - db.createObjectStore("shared_history_inbound_group_sessions", { - keyPath: ["roomId"], - }); - }, - (db) => { - db.createObjectStore("parked_shared_history", { - keyPath: ["roomId"], - }); - }, - // Expand as needed. -]; -exports.VERSION = DB_MIGRATIONS.length; -function upgradeDatabase(db, oldVersion) { - logger_1.logger.log(`Upgrading IndexedDBCryptoStore from version ${oldVersion}` + ` to ${exports.VERSION}`); - DB_MIGRATIONS.forEach((migration, index) => { - if (oldVersion <= index) - migration(db); - }); -} -exports.upgradeDatabase = upgradeDatabase; -function createDatabase(db) { - const outgoingRoomKeyRequestsStore = db.createObjectStore("outgoingRoomKeyRequests", { keyPath: "requestId" }); - // we assume that the RoomKeyRequestBody will have room_id and session_id - // properties, to make the index efficient. - outgoingRoomKeyRequestsStore.createIndex("session", ["requestBody.room_id", "requestBody.session_id"]); - outgoingRoomKeyRequestsStore.createIndex("state", "state"); -} -/* - * Aborts a transaction with a given exception - * The transaction promise will be rejected with this exception. - */ -function abortWithException(txn, e) { - // We cheekily stick our exception onto the transaction object here - // We could alternatively make the thing we pass back to the app - // an object containing the transaction and exception. - txn._mx_abortexception = e; - try { - txn.abort(); - } - catch (e) { - // sometimes we won't be able to abort the transaction - // (ie. if it's aborted or completed) - } -} -function promiseifyTxn(txn) { - return new Promise((resolve, reject) => { - txn.oncomplete = () => { - if (txn._mx_abortexception !== undefined) { - reject(txn._mx_abortexception); - } - resolve(null); - }; - txn.onerror = (event) => { - if (txn._mx_abortexception !== undefined) { - reject(txn._mx_abortexception); - } - else { - logger_1.logger.log("Error performing indexeddb txn", event); - reject(txn.error); - } - }; - txn.onabort = (event) => { - if (txn._mx_abortexception !== undefined) { - reject(txn._mx_abortexception); - } - else { - logger_1.logger.log("Error performing indexeddb txn", event); - reject(txn.error); - } - }; - }); -} - -},{"../../logger":374,"../../utils":416}],346:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IndexedDBCryptoStore = void 0; -const logger_1 = require("../../logger"); -const localStorage_crypto_store_1 = require("./localStorage-crypto-store"); -const memory_crypto_store_1 = require("./memory-crypto-store"); -const IndexedDBCryptoStoreBackend = __importStar(require("./indexeddb-crypto-store-backend")); -const errors_1 = require("../../errors"); -const IndexedDBHelpers = __importStar(require("../../indexeddb-helpers")); -/** - * Internal module. indexeddb storage for e2e. - */ -/** - * An implementation of CryptoStore, which is normally backed by an indexeddb, - * but with fallback to MemoryCryptoStore. - */ -class IndexedDBCryptoStore { - static exists(indexedDB, dbName) { - return IndexedDBHelpers.exists(indexedDB, dbName); - } - /** - * Create a new IndexedDBCryptoStore - * - * @param indexedDB - global indexedDB instance - * @param dbName - name of db to connect to - */ - constructor(indexedDB, dbName) { - this.indexedDB = indexedDB; - this.dbName = dbName; - } - /** - * Ensure the database exists and is up-to-date, or fall back to - * a local storage or in-memory store. - * - * This must be called before the store can be used. - * - * @returns resolves to either an IndexedDBCryptoStoreBackend.Backend, - * or a MemoryCryptoStore - */ - startup() { - if (this.backendPromise) { - return this.backendPromise; - } - this.backendPromise = new Promise((resolve, reject) => { - if (!this.indexedDB) { - reject(new Error("no indexeddb support available")); - return; - } - logger_1.logger.log(`connecting to indexeddb ${this.dbName}`); - const req = this.indexedDB.open(this.dbName, IndexedDBCryptoStoreBackend.VERSION); - req.onupgradeneeded = (ev) => { - const db = req.result; - const oldVersion = ev.oldVersion; - IndexedDBCryptoStoreBackend.upgradeDatabase(db, oldVersion); - }; - req.onblocked = () => { - logger_1.logger.log(`can't yet open IndexedDBCryptoStore because it is open elsewhere`); - }; - req.onerror = (ev) => { - logger_1.logger.log("Error connecting to indexeddb", ev); - reject(req.error); - }; - req.onsuccess = () => { - const db = req.result; - logger_1.logger.log(`connected to indexeddb ${this.dbName}`); - resolve(new IndexedDBCryptoStoreBackend.Backend(db)); - }; - }) - .then((backend) => { - // Edge has IndexedDB but doesn't support compund keys which we use fairly extensively. - // Try a dummy query which will fail if the browser doesn't support compund keys, so - // we can fall back to a different backend. - return backend - .doTxn("readonly", [ - IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS, - IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD, - ], (txn) => { - backend.getEndToEndInboundGroupSession("", "", txn, () => { }); - }) - .then(() => backend); - }) - .catch((e) => { - if (e.name === "VersionError") { - logger_1.logger.warn("Crypto DB is too new for us to use!", e); - // don't fall back to a different store: the user has crypto data - // in this db so we should use it or nothing at all. - throw new errors_1.InvalidCryptoStoreError(errors_1.InvalidCryptoStoreState.TooNew); - } - logger_1.logger.warn(`unable to connect to indexeddb ${this.dbName}` + `: falling back to localStorage store: ${e}`); - try { - return new localStorage_crypto_store_1.LocalStorageCryptoStore(global.localStorage); - } - catch (e) { - logger_1.logger.warn(`unable to open localStorage: falling back to in-memory store: ${e}`); - return new memory_crypto_store_1.MemoryCryptoStore(); - } - }) - .then((backend) => { - this.backend = backend; - return backend; - }); - return this.backendPromise; - } - /** - * Delete all data from this store. - * - * @returns resolves when the store has been cleared. - */ - deleteAllData() { - return new Promise((resolve, reject) => { - if (!this.indexedDB) { - reject(new Error("no indexeddb support available")); - return; - } - logger_1.logger.log(`Removing indexeddb instance: ${this.dbName}`); - const req = this.indexedDB.deleteDatabase(this.dbName); - req.onblocked = () => { - logger_1.logger.log(`can't yet delete IndexedDBCryptoStore because it is open elsewhere`); - }; - req.onerror = (ev) => { - logger_1.logger.log("Error deleting data from indexeddb", ev); - reject(req.error); - }; - req.onsuccess = () => { - logger_1.logger.log(`Removed indexeddb instance: ${this.dbName}`); - resolve(); - }; - }).catch((e) => { - // in firefox, with indexedDB disabled, this fails with a - // DOMError. We treat this as non-fatal, so that people can - // still use the app. - logger_1.logger.warn(`unable to delete IndexedDBCryptoStore: ${e}`); - }); - } - /** - * Look for an existing outgoing room key request, and if none is found, - * add a new one - * - * - * @returns resolves to - * {@link OutgoingRoomKeyRequest}: either the - * same instance as passed in, or the existing one. - */ - getOrAddOutgoingRoomKeyRequest(request) { - return this.backend.getOrAddOutgoingRoomKeyRequest(request); - } - /** - * Look for an existing room key request - * - * @param requestBody - existing request to look for - * - * @returns resolves to the matching - * {@link OutgoingRoomKeyRequest}, or null if - * not found - */ - getOutgoingRoomKeyRequest(requestBody) { - return this.backend.getOutgoingRoomKeyRequest(requestBody); - } - /** - * Look for room key requests by state - * - * @param wantedStates - list of acceptable states - * - * @returns resolves to the a - * {@link OutgoingRoomKeyRequest}, or null if - * there are no pending requests in those states. If there are multiple - * requests in those states, an arbitrary one is chosen. - */ - getOutgoingRoomKeyRequestByState(wantedStates) { - return this.backend.getOutgoingRoomKeyRequestByState(wantedStates); - } - /** - * Look for room key requests by state – - * unlike above, return a list of all entries in one state. - * - * @returns Returns an array of requests in the given state - */ - getAllOutgoingRoomKeyRequestsByState(wantedState) { - return this.backend.getAllOutgoingRoomKeyRequestsByState(wantedState); - } - /** - * Look for room key requests by target device and state - * - * @param userId - Target user ID - * @param deviceId - Target device ID - * @param wantedStates - list of acceptable states - * - * @returns resolves to a list of all the - * {@link OutgoingRoomKeyRequest} - */ - getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates) { - return this.backend.getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates); - } - /** - * Look for an existing room key request by id and state, and update it if - * found - * - * @param requestId - ID of request to update - * @param expectedState - state we expect to find the request in - * @param updates - name/value map of updates to apply - * - * @returns resolves to - * {@link OutgoingRoomKeyRequest} - * updated request, or null if no matching row was found - */ - updateOutgoingRoomKeyRequest(requestId, expectedState, updates) { - return this.backend.updateOutgoingRoomKeyRequest(requestId, expectedState, updates); - } - /** - * Look for an existing room key request by id and state, and delete it if - * found - * - * @param requestId - ID of request to update - * @param expectedState - state we expect to find the request in - * - * @returns resolves once the operation is completed - */ - deleteOutgoingRoomKeyRequest(requestId, expectedState) { - return this.backend.deleteOutgoingRoomKeyRequest(requestId, expectedState); - } - // Olm Account - /* - * Get the account pickle from the store. - * This requires an active transaction. See doTxn(). - * - * @param txn - An active transaction. See doTxn(). - * @param func - Called with the account pickle - */ - getAccount(txn, func) { - this.backend.getAccount(txn, func); - } - /** - * Write the account pickle to the store. - * This requires an active transaction. See doTxn(). - * - * @param txn - An active transaction. See doTxn(). - * @param accountPickle - The new account pickle to store. - */ - storeAccount(txn, accountPickle) { - this.backend.storeAccount(txn, accountPickle); - } - /** - * Get the public part of the cross-signing keys (eg. self-signing key, - * user signing key). - * - * @param txn - An active transaction. See doTxn(). - * @param func - Called with the account keys object: - * `{ key_type: base64 encoded seed }` where key type = user_signing_key_seed or self_signing_key_seed - */ - getCrossSigningKeys(txn, func) { - this.backend.getCrossSigningKeys(txn, func); - } - /** - * @param txn - An active transaction. See doTxn(). - * @param func - Called with the private key - * @param type - A key type - */ - getSecretStorePrivateKey(txn, func, type) { - this.backend.getSecretStorePrivateKey(txn, func, type); - } - /** - * Write the cross-signing keys back to the store - * - * @param txn - An active transaction. See doTxn(). - * @param keys - keys object as getCrossSigningKeys() - */ - storeCrossSigningKeys(txn, keys) { - this.backend.storeCrossSigningKeys(txn, keys); - } - /** - * Write the cross-signing private keys back to the store - * - * @param txn - An active transaction. See doTxn(). - * @param type - The type of cross-signing private key to store - * @param key - keys object as getCrossSigningKeys() - */ - storeSecretStorePrivateKey(txn, type, key) { - this.backend.storeSecretStorePrivateKey(txn, type, key); - } - // Olm sessions - /** - * Returns the number of end-to-end sessions in the store - * @param txn - An active transaction. See doTxn(). - * @param func - Called with the count of sessions - */ - countEndToEndSessions(txn, func) { - this.backend.countEndToEndSessions(txn, func); - } - /** - * Retrieve a specific end-to-end session between the logged-in user - * and another device. - * @param deviceKey - The public key of the other device. - * @param sessionId - The ID of the session to retrieve - * @param txn - An active transaction. See doTxn(). - * @param func - Called with A map from sessionId - * to session information object with 'session' key being the - * Base64 end-to-end session and lastReceivedMessageTs being the - * timestamp in milliseconds at which the session last received - * a message. - */ - getEndToEndSession(deviceKey, sessionId, txn, func) { - this.backend.getEndToEndSession(deviceKey, sessionId, txn, func); - } - /** - * Retrieve the end-to-end sessions between the logged-in user and another - * device. - * @param deviceKey - The public key of the other device. - * @param txn - An active transaction. See doTxn(). - * @param func - Called with A map from sessionId - * to session information object with 'session' key being the - * Base64 end-to-end session and lastReceivedMessageTs being the - * timestamp in milliseconds at which the session last received - * a message. - */ - getEndToEndSessions(deviceKey, txn, func) { - this.backend.getEndToEndSessions(deviceKey, txn, func); - } - /** - * Retrieve all end-to-end sessions - * @param txn - An active transaction. See doTxn(). - * @param func - Called one for each session with - * an object with, deviceKey, lastReceivedMessageTs, sessionId - * and session keys. - */ - getAllEndToEndSessions(txn, func) { - this.backend.getAllEndToEndSessions(txn, func); - } - /** - * Store a session between the logged-in user and another device - * @param deviceKey - The public key of the other device. - * @param sessionId - The ID for this end-to-end session. - * @param sessionInfo - Session information object - * @param txn - An active transaction. See doTxn(). - */ - storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { - this.backend.storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn); - } - storeEndToEndSessionProblem(deviceKey, type, fixed) { - return this.backend.storeEndToEndSessionProblem(deviceKey, type, fixed); - } - getEndToEndSessionProblem(deviceKey, timestamp) { - return this.backend.getEndToEndSessionProblem(deviceKey, timestamp); - } - filterOutNotifiedErrorDevices(devices) { - return this.backend.filterOutNotifiedErrorDevices(devices); - } - // Inbound group sessions - /** - * Retrieve the end-to-end inbound group session for a given - * server key and session ID - * @param senderCurve25519Key - The sender's curve 25519 key - * @param sessionId - The ID of the session - * @param txn - An active transaction. See doTxn(). - * @param func - Called with A map from sessionId - * to Base64 end-to-end session. - */ - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { - this.backend.getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func); - } - /** - * Fetches all inbound group sessions in the store - * @param txn - An active transaction. See doTxn(). - * @param func - Called once for each group session - * in the store with an object having keys `{senderKey, sessionId, sessionData}`, - * then once with null to indicate the end of the list. - */ - getAllEndToEndInboundGroupSessions(txn, func) { - this.backend.getAllEndToEndInboundGroupSessions(txn, func); - } - /** - * Adds an end-to-end inbound group session to the store. - * If there already exists an inbound group session with the same - * senderCurve25519Key and sessionID, the session will not be added. - * @param senderCurve25519Key - The sender's curve 25519 key - * @param sessionId - The ID of the session - * @param sessionData - The session data structure - * @param txn - An active transaction. See doTxn(). - */ - addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - this.backend.addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn); - } - /** - * Writes an end-to-end inbound group session to the store. - * If there already exists an inbound group session with the same - * senderCurve25519Key and sessionID, it will be overwritten. - * @param senderCurve25519Key - The sender's curve 25519 key - * @param sessionId - The ID of the session - * @param sessionData - The session data structure - * @param txn - An active transaction. See doTxn(). - */ - storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - this.backend.storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn); - } - storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn) { - this.backend.storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn); - } - // End-to-end device tracking - /** - * Store the state of all tracked devices - * This contains devices for each user, a tracking state for each user - * and a sync token matching the point in time the snapshot represents. - * These all need to be written out in full each time such that the snapshot - * is always consistent, so they are stored in one object. - * - * @param txn - An active transaction. See doTxn(). - */ - storeEndToEndDeviceData(deviceData, txn) { - this.backend.storeEndToEndDeviceData(deviceData, txn); - } - /** - * Get the state of all tracked devices - * - * @param txn - An active transaction. See doTxn(). - * @param func - Function called with the - * device data - */ - getEndToEndDeviceData(txn, func) { - this.backend.getEndToEndDeviceData(txn, func); - } - // End to End Rooms - /** - * Store the end-to-end state for a room. - * @param roomId - The room's ID. - * @param roomInfo - The end-to-end info for the room. - * @param txn - An active transaction. See doTxn(). - */ - storeEndToEndRoom(roomId, roomInfo, txn) { - this.backend.storeEndToEndRoom(roomId, roomInfo, txn); - } - /** - * Get an object of `roomId->roomInfo` for all e2e rooms in the store - * @param txn - An active transaction. See doTxn(). - * @param func - Function called with the end-to-end encrypted rooms - */ - getEndToEndRooms(txn, func) { - this.backend.getEndToEndRooms(txn, func); - } - // session backups - /** - * Get the inbound group sessions that need to be backed up. - * @param limit - The maximum number of sessions to retrieve. 0 - * for no limit. - * @returns resolves to an array of inbound group sessions - */ - getSessionsNeedingBackup(limit) { - return this.backend.getSessionsNeedingBackup(limit); - } - /** - * Count the inbound group sessions that need to be backed up. - * @param txn - An active transaction. See doTxn(). (optional) - * @returns resolves to the number of sessions - */ - countSessionsNeedingBackup(txn) { - return this.backend.countSessionsNeedingBackup(txn); - } - /** - * Unmark sessions as needing to be backed up. - * @param sessions - The sessions that need to be backed up. - * @param txn - An active transaction. See doTxn(). (optional) - * @returns resolves when the sessions are unmarked - */ - unmarkSessionsNeedingBackup(sessions, txn) { - return this.backend.unmarkSessionsNeedingBackup(sessions, txn); - } - /** - * Mark sessions as needing to be backed up. - * @param sessions - The sessions that need to be backed up. - * @param txn - An active transaction. See doTxn(). (optional) - * @returns resolves when the sessions are marked - */ - markSessionsNeedingBackup(sessions, txn) { - return this.backend.markSessionsNeedingBackup(sessions, txn); - } - /** - * Add a shared-history group session for a room. - * @param roomId - The room that the key belongs to - * @param senderKey - The sender's curve 25519 key - * @param sessionId - The ID of the session - * @param txn - An active transaction. See doTxn(). (optional) - */ - addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId, txn) { - this.backend.addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId, txn); - } - /** - * Get the shared-history group session for a room. - * @param roomId - The room that the key belongs to - * @param txn - An active transaction. See doTxn(). (optional) - * @returns Promise which resolves to an array of [senderKey, sessionId] - */ - getSharedHistoryInboundGroupSessions(roomId, txn) { - return this.backend.getSharedHistoryInboundGroupSessions(roomId, txn); - } - /** - * Park a shared-history group session for a room we may be invited to later. - */ - addParkedSharedHistory(roomId, parkedData, txn) { - this.backend.addParkedSharedHistory(roomId, parkedData, txn); - } - /** - * Pop out all shared-history group sessions for a room. - */ - takeParkedSharedHistory(roomId, txn) { - return this.backend.takeParkedSharedHistory(roomId, txn); - } - /** - * Perform a transaction on the crypto store. Any store methods - * that require a transaction (txn) object to be passed in may - * only be called within a callback of either this function or - * one of the store functions operating on the same transaction. - * - * @param mode - 'readwrite' if you need to call setter - * functions with this transaction. Otherwise, 'readonly'. - * @param stores - List IndexedDBCryptoStore.STORE_* - * options representing all types of object that will be - * accessed or written to with this transaction. - * @param func - Function called with the - * transaction object: an opaque object that should be passed - * to store functions. - * @param log - A possibly customised log - * @returns Promise that resolves with the result of the `func` - * when the transaction is complete. If the backend is - * async (ie. the indexeddb backend) any of the callback - * functions throwing an exception will cause this promise to - * reject with that exception. On synchronous backends, the - * exception will propagate to the caller of the getFoo method. - */ - doTxn(mode, stores, func, log) { - return this.backend.doTxn(mode, stores, func, log); - } -} -exports.IndexedDBCryptoStore = IndexedDBCryptoStore; -IndexedDBCryptoStore.STORE_ACCOUNT = "account"; -IndexedDBCryptoStore.STORE_SESSIONS = "sessions"; -IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS = "inbound_group_sessions"; -IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD = "inbound_group_sessions_withheld"; -IndexedDBCryptoStore.STORE_SHARED_HISTORY_INBOUND_GROUP_SESSIONS = "shared_history_inbound_group_sessions"; -IndexedDBCryptoStore.STORE_PARKED_SHARED_HISTORY = "parked_shared_history"; -IndexedDBCryptoStore.STORE_DEVICE_DATA = "device_data"; -IndexedDBCryptoStore.STORE_ROOMS = "rooms"; -IndexedDBCryptoStore.STORE_BACKUP = "sessions_needing_backup"; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../../errors":359,"../../indexeddb-helpers":372,"../../logger":374,"./indexeddb-crypto-store-backend":345,"./localStorage-crypto-store":347,"./memory-crypto-store":348}],347:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LocalStorageCryptoStore = void 0; -const logger_1 = require("../../logger"); -const memory_crypto_store_1 = require("./memory-crypto-store"); -const utils_1 = require("../../utils"); -/** - * Internal module. Partial localStorage backed storage for e2e. - * This is not a full crypto store, just the in-memory store with - * some things backed by localStorage. It exists because indexedDB - * is broken in Firefox private mode or set to, "will not remember - * history". - */ -const E2E_PREFIX = "crypto."; -const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account"; -const KEY_CROSS_SIGNING_KEYS = E2E_PREFIX + "cross_signing_keys"; -const KEY_NOTIFIED_ERROR_DEVICES = E2E_PREFIX + "notified_error_devices"; -const KEY_DEVICE_DATA = E2E_PREFIX + "device_data"; -const KEY_INBOUND_SESSION_PREFIX = E2E_PREFIX + "inboundgroupsessions/"; -const KEY_INBOUND_SESSION_WITHHELD_PREFIX = E2E_PREFIX + "inboundgroupsessions.withheld/"; -const KEY_ROOMS_PREFIX = E2E_PREFIX + "rooms/"; -const KEY_SESSIONS_NEEDING_BACKUP = E2E_PREFIX + "sessionsneedingbackup"; -function keyEndToEndSessions(deviceKey) { - return E2E_PREFIX + "sessions/" + deviceKey; -} -function keyEndToEndSessionProblems(deviceKey) { - return E2E_PREFIX + "session.problems/" + deviceKey; -} -function keyEndToEndInboundGroupSession(senderKey, sessionId) { - return KEY_INBOUND_SESSION_PREFIX + senderKey + "/" + sessionId; -} -function keyEndToEndInboundGroupSessionWithheld(senderKey, sessionId) { - return KEY_INBOUND_SESSION_WITHHELD_PREFIX + senderKey + "/" + sessionId; -} -function keyEndToEndRoomsPrefix(roomId) { - return KEY_ROOMS_PREFIX + roomId; -} -class LocalStorageCryptoStore extends memory_crypto_store_1.MemoryCryptoStore { - static exists(store) { - var _a; - const length = store.length; - for (let i = 0; i < length; i++) { - if ((_a = store.key(i)) === null || _a === void 0 ? void 0 : _a.startsWith(E2E_PREFIX)) { - return true; - } - } - return false; - } - constructor(store) { - super(); - this.store = store; - } - // Olm Sessions - countEndToEndSessions(txn, func) { - var _a; - let count = 0; - for (let i = 0; i < this.store.length; ++i) { - if ((_a = this.store.key(i)) === null || _a === void 0 ? void 0 : _a.startsWith(keyEndToEndSessions(""))) - ++count; - } - func(count); - } - // eslint-disable-next-line @typescript-eslint/naming-convention - _getEndToEndSessions(deviceKey) { - const sessions = getJsonItem(this.store, keyEndToEndSessions(deviceKey)); - const fixedSessions = {}; - // fix up any old sessions to be objects rather than just the base64 pickle - for (const [sid, val] of Object.entries(sessions || {})) { - if (typeof val === "string") { - fixedSessions[sid] = { - session: val, - }; - } - else { - fixedSessions[sid] = val; - } - } - return fixedSessions; - } - getEndToEndSession(deviceKey, sessionId, txn, func) { - const sessions = this._getEndToEndSessions(deviceKey); - func(sessions[sessionId] || {}); - } - getEndToEndSessions(deviceKey, txn, func) { - func(this._getEndToEndSessions(deviceKey) || {}); - } - getAllEndToEndSessions(txn, func) { - var _a; - for (let i = 0; i < this.store.length; ++i) { - if ((_a = this.store.key(i)) === null || _a === void 0 ? void 0 : _a.startsWith(keyEndToEndSessions(""))) { - const deviceKey = this.store.key(i).split("/")[1]; - for (const sess of Object.values(this._getEndToEndSessions(deviceKey))) { - func(sess); - } - } - } - } - storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { - const sessions = this._getEndToEndSessions(deviceKey) || {}; - sessions[sessionId] = sessionInfo; - setJsonItem(this.store, keyEndToEndSessions(deviceKey), sessions); - } - storeEndToEndSessionProblem(deviceKey, type, fixed) { - return __awaiter(this, void 0, void 0, function* () { - const key = keyEndToEndSessionProblems(deviceKey); - const problems = getJsonItem(this.store, key) || []; - problems.push({ type, fixed, time: Date.now() }); - problems.sort((a, b) => { - return a.time - b.time; - }); - setJsonItem(this.store, key, problems); - }); - } - getEndToEndSessionProblem(deviceKey, timestamp) { - return __awaiter(this, void 0, void 0, function* () { - const key = keyEndToEndSessionProblems(deviceKey); - const problems = getJsonItem(this.store, key) || []; - if (!problems.length) { - return null; - } - const lastProblem = problems[problems.length - 1]; - for (const problem of problems) { - if (problem.time > timestamp) { - return Object.assign({}, problem, { fixed: lastProblem.fixed }); - } - } - if (lastProblem.fixed) { - return null; - } - else { - return lastProblem; - } - }); - } - filterOutNotifiedErrorDevices(devices) { - return __awaiter(this, void 0, void 0, function* () { - const notifiedErrorDevices = getJsonItem(this.store, KEY_NOTIFIED_ERROR_DEVICES) || {}; - const ret = []; - for (const device of devices) { - const { userId, deviceInfo } = device; - if (userId in notifiedErrorDevices) { - if (!(deviceInfo.deviceId in notifiedErrorDevices[userId])) { - ret.push(device); - (0, utils_1.safeSet)(notifiedErrorDevices[userId], deviceInfo.deviceId, true); - } - } - else { - ret.push(device); - (0, utils_1.safeSet)(notifiedErrorDevices, userId, { [deviceInfo.deviceId]: true }); - } - } - setJsonItem(this.store, KEY_NOTIFIED_ERROR_DEVICES, notifiedErrorDevices); - return ret; - }); - } - // Inbound Group Sessions - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { - func(getJsonItem(this.store, keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId)), getJsonItem(this.store, keyEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId))); - } - getAllEndToEndInboundGroupSessions(txn, func) { - for (let i = 0; i < this.store.length; ++i) { - const key = this.store.key(i); - if (key === null || key === void 0 ? void 0 : key.startsWith(KEY_INBOUND_SESSION_PREFIX)) { - // we can't use split, as the components we are trying to split out - // might themselves contain '/' characters. We rely on the - // senderKey being a (32-byte) curve25519 key, base64-encoded - // (hence 43 characters long). - func({ - senderKey: key.slice(KEY_INBOUND_SESSION_PREFIX.length, KEY_INBOUND_SESSION_PREFIX.length + 43), - sessionId: key.slice(KEY_INBOUND_SESSION_PREFIX.length + 44), - sessionData: getJsonItem(this.store, key), - }); - } - } - func(null); - } - addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - const existing = getJsonItem(this.store, keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId)); - if (!existing) { - this.storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn); - } - } - storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - setJsonItem(this.store, keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId), sessionData); - } - storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn) { - setJsonItem(this.store, keyEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId), sessionData); - } - getEndToEndDeviceData(txn, func) { - func(getJsonItem(this.store, KEY_DEVICE_DATA)); - } - storeEndToEndDeviceData(deviceData, txn) { - setJsonItem(this.store, KEY_DEVICE_DATA, deviceData); - } - storeEndToEndRoom(roomId, roomInfo, txn) { - setJsonItem(this.store, keyEndToEndRoomsPrefix(roomId), roomInfo); - } - getEndToEndRooms(txn, func) { - const result = {}; - const prefix = keyEndToEndRoomsPrefix(""); - for (let i = 0; i < this.store.length; ++i) { - const key = this.store.key(i); - if (key === null || key === void 0 ? void 0 : key.startsWith(prefix)) { - const roomId = key.slice(prefix.length); - result[roomId] = getJsonItem(this.store, key); - } - } - func(result); - } - getSessionsNeedingBackup(limit) { - const sessionsNeedingBackup = getJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {}; - const sessions = []; - for (const session in sessionsNeedingBackup) { - if (Object.prototype.hasOwnProperty.call(sessionsNeedingBackup, session)) { - // see getAllEndToEndInboundGroupSessions for the magic number explanations - const senderKey = session.slice(0, 43); - const sessionId = session.slice(44); - this.getEndToEndInboundGroupSession(senderKey, sessionId, null, (sessionData) => { - sessions.push({ - senderKey: senderKey, - sessionId: sessionId, - sessionData: sessionData, - }); - }); - if (limit && sessions.length >= limit) { - break; - } - } - } - return Promise.resolve(sessions); - } - countSessionsNeedingBackup() { - const sessionsNeedingBackup = getJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {}; - return Promise.resolve(Object.keys(sessionsNeedingBackup).length); - } - unmarkSessionsNeedingBackup(sessions) { - const sessionsNeedingBackup = getJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {}; - for (const session of sessions) { - delete sessionsNeedingBackup[session.senderKey + "/" + session.sessionId]; - } - setJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP, sessionsNeedingBackup); - return Promise.resolve(); - } - markSessionsNeedingBackup(sessions) { - const sessionsNeedingBackup = getJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {}; - for (const session of sessions) { - sessionsNeedingBackup[session.senderKey + "/" + session.sessionId] = true; - } - setJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP, sessionsNeedingBackup); - return Promise.resolve(); - } - /** - * Delete all data from this store. - * - * @returns Promise which resolves when the store has been cleared. - */ - deleteAllData() { - this.store.removeItem(KEY_END_TO_END_ACCOUNT); - return Promise.resolve(); - } - // Olm account - getAccount(txn, func) { - const accountPickle = getJsonItem(this.store, KEY_END_TO_END_ACCOUNT); - func(accountPickle); - } - storeAccount(txn, accountPickle) { - setJsonItem(this.store, KEY_END_TO_END_ACCOUNT, accountPickle); - } - getCrossSigningKeys(txn, func) { - const keys = getJsonItem(this.store, KEY_CROSS_SIGNING_KEYS); - func(keys); - } - getSecretStorePrivateKey(txn, func, type) { - const key = getJsonItem(this.store, E2E_PREFIX + `ssss_cache.${type}`); - func(key); - } - storeCrossSigningKeys(txn, keys) { - setJsonItem(this.store, KEY_CROSS_SIGNING_KEYS, keys); - } - storeSecretStorePrivateKey(txn, type, key) { - setJsonItem(this.store, E2E_PREFIX + `ssss_cache.${type}`, key); - } - doTxn(mode, stores, func) { - return Promise.resolve(func(null)); - } -} -exports.LocalStorageCryptoStore = LocalStorageCryptoStore; -function getJsonItem(store, key) { - try { - // if the key is absent, store.getItem() returns null, and - // JSON.parse(null) === null, so this returns null. - return JSON.parse(store.getItem(key)); - } - catch (e) { - logger_1.logger.log("Error: Failed to get key %s: %s", key, e.message); - logger_1.logger.log(e.stack); - } - return null; -} -function setJsonItem(store, key, val) { - store.setItem(key, JSON.stringify(val)); -} - -},{"../../logger":374,"../../utils":416,"./memory-crypto-store":348}],348:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MemoryCryptoStore = void 0; -const logger_1 = require("../../logger"); -const utils = __importStar(require("../../utils")); -const utils_1 = require("../../utils"); -/** - * Internal module. in-memory storage for e2e. - */ -class MemoryCryptoStore { - constructor() { - this.outgoingRoomKeyRequests = []; - this.account = null; - this.crossSigningKeys = null; - this.privateKeys = {}; - this.sessions = {}; - this.sessionProblems = {}; - this.notifiedErrorDevices = {}; - this.inboundGroupSessions = {}; - this.inboundGroupSessionsWithheld = {}; - // Opaque device data object - this.deviceData = null; - this.rooms = {}; - this.sessionsNeedingBackup = {}; - this.sharedHistoryInboundGroupSessions = {}; - this.parkedSharedHistory = new Map(); // keyed by room ID - } - /** - * Ensure the database exists and is up-to-date. - * - * This must be called before the store can be used. - * - * @returns resolves to the store. - */ - startup() { - return __awaiter(this, void 0, void 0, function* () { - // No startup work to do for the memory store. - return this; - }); - } - /** - * Delete all data from this store. - * - * @returns Promise which resolves when the store has been cleared. - */ - deleteAllData() { - return Promise.resolve(); - } - /** - * Look for an existing outgoing room key request, and if none is found, - * add a new one - * - * - * @returns resolves to - * {@link OutgoingRoomKeyRequest}: either the - * same instance as passed in, or the existing one. - */ - getOrAddOutgoingRoomKeyRequest(request) { - const requestBody = request.requestBody; - return utils.promiseTry(() => { - // first see if we already have an entry for this request. - const existing = this._getOutgoingRoomKeyRequest(requestBody); - if (existing) { - // this entry matches the request - return it. - logger_1.logger.log(`already have key request outstanding for ` + - `${requestBody.room_id} / ${requestBody.session_id}: ` + - `not sending another`); - return existing; - } - // we got to the end of the list without finding a match - // - add the new request. - logger_1.logger.log(`enqueueing key request for ${requestBody.room_id} / ` + requestBody.session_id); - this.outgoingRoomKeyRequests.push(request); - return request; - }); - } - /** - * Look for an existing room key request - * - * @param requestBody - existing request to look for - * - * @returns resolves to the matching - * {@link OutgoingRoomKeyRequest}, or null if - * not found - */ - getOutgoingRoomKeyRequest(requestBody) { - return Promise.resolve(this._getOutgoingRoomKeyRequest(requestBody)); - } - /** - * Looks for existing room key request, and returns the result synchronously. - * - * @internal - * - * @param requestBody - existing request to look for - * - * @returns - * the matching request, or null if not found - */ - // eslint-disable-next-line @typescript-eslint/naming-convention - _getOutgoingRoomKeyRequest(requestBody) { - for (const existing of this.outgoingRoomKeyRequests) { - if (utils.deepCompare(existing.requestBody, requestBody)) { - return existing; - } - } - return null; - } - /** - * Look for room key requests by state - * - * @param wantedStates - list of acceptable states - * - * @returns resolves to the a - * {@link OutgoingRoomKeyRequest}, or null if - * there are no pending requests in those states - */ - getOutgoingRoomKeyRequestByState(wantedStates) { - for (const req of this.outgoingRoomKeyRequests) { - for (const state of wantedStates) { - if (req.state === state) { - return Promise.resolve(req); - } - } - } - return Promise.resolve(null); - } - /** - * - * @returns All OutgoingRoomKeyRequests in state - */ - getAllOutgoingRoomKeyRequestsByState(wantedState) { - return Promise.resolve(this.outgoingRoomKeyRequests.filter((r) => r.state == wantedState)); - } - getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates) { - const results = []; - for (const req of this.outgoingRoomKeyRequests) { - for (const state of wantedStates) { - if (req.state === state && - req.recipients.some((recipient) => recipient.userId === userId && recipient.deviceId === deviceId)) { - results.push(req); - } - } - } - return Promise.resolve(results); - } - /** - * Look for an existing room key request by id and state, and update it if - * found - * - * @param requestId - ID of request to update - * @param expectedState - state we expect to find the request in - * @param updates - name/value map of updates to apply - * - * @returns resolves to - * {@link OutgoingRoomKeyRequest} - * updated request, or null if no matching row was found - */ - updateOutgoingRoomKeyRequest(requestId, expectedState, updates) { - for (const req of this.outgoingRoomKeyRequests) { - if (req.requestId !== requestId) { - continue; - } - if (req.state !== expectedState) { - logger_1.logger.warn(`Cannot update room key request from ${expectedState} ` + - `as it was already updated to ${req.state}`); - return Promise.resolve(null); - } - Object.assign(req, updates); - return Promise.resolve(req); - } - return Promise.resolve(null); - } - /** - * Look for an existing room key request by id and state, and delete it if - * found - * - * @param requestId - ID of request to update - * @param expectedState - state we expect to find the request in - * - * @returns resolves once the operation is completed - */ - deleteOutgoingRoomKeyRequest(requestId, expectedState) { - for (let i = 0; i < this.outgoingRoomKeyRequests.length; i++) { - const req = this.outgoingRoomKeyRequests[i]; - if (req.requestId !== requestId) { - continue; - } - if (req.state != expectedState) { - logger_1.logger.warn(`Cannot delete room key request in state ${req.state} ` + `(expected ${expectedState})`); - return Promise.resolve(null); - } - this.outgoingRoomKeyRequests.splice(i, 1); - return Promise.resolve(req); - } - return Promise.resolve(null); - } - // Olm Account - getAccount(txn, func) { - func(this.account); - } - storeAccount(txn, accountPickle) { - this.account = accountPickle; - } - getCrossSigningKeys(txn, func) { - func(this.crossSigningKeys); - } - getSecretStorePrivateKey(txn, func, type) { - const result = this.privateKeys[type]; - func(result || null); - } - storeCrossSigningKeys(txn, keys) { - this.crossSigningKeys = keys; - } - storeSecretStorePrivateKey(txn, type, key) { - this.privateKeys[type] = key; - } - // Olm Sessions - countEndToEndSessions(txn, func) { - func(Object.keys(this.sessions).length); - } - getEndToEndSession(deviceKey, sessionId, txn, func) { - const deviceSessions = this.sessions[deviceKey] || {}; - func(deviceSessions[sessionId] || null); - } - getEndToEndSessions(deviceKey, txn, func) { - func(this.sessions[deviceKey] || {}); - } - getAllEndToEndSessions(txn, func) { - Object.entries(this.sessions).forEach(([deviceKey, deviceSessions]) => { - Object.entries(deviceSessions).forEach(([sessionId, session]) => { - func(Object.assign(Object.assign({}, session), { deviceKey, - sessionId })); - }); - }); - } - storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { - let deviceSessions = this.sessions[deviceKey]; - if (deviceSessions === undefined) { - deviceSessions = {}; - this.sessions[deviceKey] = deviceSessions; - } - deviceSessions[sessionId] = sessionInfo; - } - storeEndToEndSessionProblem(deviceKey, type, fixed) { - return __awaiter(this, void 0, void 0, function* () { - const problems = (this.sessionProblems[deviceKey] = this.sessionProblems[deviceKey] || []); - problems.push({ type, fixed, time: Date.now() }); - problems.sort((a, b) => { - return a.time - b.time; - }); - }); - } - getEndToEndSessionProblem(deviceKey, timestamp) { - return __awaiter(this, void 0, void 0, function* () { - const problems = this.sessionProblems[deviceKey] || []; - if (!problems.length) { - return null; - } - const lastProblem = problems[problems.length - 1]; - for (const problem of problems) { - if (problem.time > timestamp) { - return Object.assign({}, problem, { fixed: lastProblem.fixed }); - } - } - if (lastProblem.fixed) { - return null; - } - else { - return lastProblem; - } - }); - } - filterOutNotifiedErrorDevices(devices) { - return __awaiter(this, void 0, void 0, function* () { - const notifiedErrorDevices = this.notifiedErrorDevices; - const ret = []; - for (const device of devices) { - const { userId, deviceInfo } = device; - if (userId in notifiedErrorDevices) { - if (!(deviceInfo.deviceId in notifiedErrorDevices[userId])) { - ret.push(device); - (0, utils_1.safeSet)(notifiedErrorDevices[userId], deviceInfo.deviceId, true); - } - } - else { - ret.push(device); - (0, utils_1.safeSet)(notifiedErrorDevices, userId, { [deviceInfo.deviceId]: true }); - } - } - return ret; - }); - } - // Inbound Group Sessions - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { - const k = senderCurve25519Key + "/" + sessionId; - func(this.inboundGroupSessions[k] || null, this.inboundGroupSessionsWithheld[k] || null); - } - getAllEndToEndInboundGroupSessions(txn, func) { - for (const key of Object.keys(this.inboundGroupSessions)) { - // we can't use split, as the components we are trying to split out - // might themselves contain '/' characters. We rely on the - // senderKey being a (32-byte) curve25519 key, base64-encoded - // (hence 43 characters long). - func({ - senderKey: key.slice(0, 43), - sessionId: key.slice(44), - sessionData: this.inboundGroupSessions[key], - }); - } - func(null); - } - addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - const k = senderCurve25519Key + "/" + sessionId; - if (this.inboundGroupSessions[k] === undefined) { - this.inboundGroupSessions[k] = sessionData; - } - } - storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - this.inboundGroupSessions[senderCurve25519Key + "/" + sessionId] = sessionData; - } - storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn) { - const k = senderCurve25519Key + "/" + sessionId; - this.inboundGroupSessionsWithheld[k] = sessionData; - } - // Device Data - getEndToEndDeviceData(txn, func) { - func(this.deviceData); - } - storeEndToEndDeviceData(deviceData, txn) { - this.deviceData = deviceData; - } - // E2E rooms - storeEndToEndRoom(roomId, roomInfo, txn) { - this.rooms[roomId] = roomInfo; - } - getEndToEndRooms(txn, func) { - func(this.rooms); - } - getSessionsNeedingBackup(limit) { - const sessions = []; - for (const session in this.sessionsNeedingBackup) { - if (this.inboundGroupSessions[session]) { - sessions.push({ - senderKey: session.slice(0, 43), - sessionId: session.slice(44), - sessionData: this.inboundGroupSessions[session], - }); - if (limit && session.length >= limit) { - break; - } - } - } - return Promise.resolve(sessions); - } - countSessionsNeedingBackup() { - return Promise.resolve(Object.keys(this.sessionsNeedingBackup).length); - } - unmarkSessionsNeedingBackup(sessions) { - for (const session of sessions) { - const sessionKey = session.senderKey + "/" + session.sessionId; - delete this.sessionsNeedingBackup[sessionKey]; - } - return Promise.resolve(); - } - markSessionsNeedingBackup(sessions) { - for (const session of sessions) { - const sessionKey = session.senderKey + "/" + session.sessionId; - this.sessionsNeedingBackup[sessionKey] = true; - } - return Promise.resolve(); - } - addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId) { - const sessions = this.sharedHistoryInboundGroupSessions[roomId] || []; - sessions.push([senderKey, sessionId]); - this.sharedHistoryInboundGroupSessions[roomId] = sessions; - } - getSharedHistoryInboundGroupSessions(roomId) { - return Promise.resolve(this.sharedHistoryInboundGroupSessions[roomId] || []); - } - addParkedSharedHistory(roomId, parkedData) { - var _a; - const parked = (_a = this.parkedSharedHistory.get(roomId)) !== null && _a !== void 0 ? _a : []; - parked.push(parkedData); - this.parkedSharedHistory.set(roomId, parked); - } - takeParkedSharedHistory(roomId) { - var _a; - const parked = (_a = this.parkedSharedHistory.get(roomId)) !== null && _a !== void 0 ? _a : []; - this.parkedSharedHistory.delete(roomId); - return Promise.resolve(parked); - } - // Session key backups - doTxn(mode, stores, func) { - return Promise.resolve(func(null)); - } -} -exports.MemoryCryptoStore = MemoryCryptoStore; - -},{"../../logger":374,"../../utils":416}],349:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 New Vector Ltd -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VerificationBase = exports.VerificationEvent = exports.SwitchStartEventError = void 0; -/** - * Base class for verification methods. - */ -const event_1 = require("../../models/event"); -const event_2 = require("../../@types/event"); -const logger_1 = require("../../logger"); -const deviceinfo_1 = require("../deviceinfo"); -const Error_1 = require("./Error"); -const CrossSigning_1 = require("../CrossSigning"); -const typed_event_emitter_1 = require("../../models/typed-event-emitter"); -const timeoutException = new Error("Verification timed out"); -class SwitchStartEventError extends Error { - constructor(startEvent) { - super(); - this.startEvent = startEvent; - } -} -exports.SwitchStartEventError = SwitchStartEventError; -var VerificationEvent; -(function (VerificationEvent) { - VerificationEvent["Cancel"] = "cancel"; -})(VerificationEvent = exports.VerificationEvent || (exports.VerificationEvent = {})); -class VerificationBase extends typed_event_emitter_1.TypedEventEmitter { - /** - * Base class for verification methods. - * - *

Once a verifier object is created, the verification can be started by - * calling the verify() method, which will return a promise that will - * resolve when the verification is completed, or reject if it could not - * complete.

- * - *

Subclasses must have a NAME class property.

- * - * @param channel - the verification channel to send verification messages over. - * TODO: Channel types - * - * @param baseApis - base matrix api interface - * - * @param userId - the user ID that is being verified - * - * @param deviceId - the device ID that is being verified - * - * @param startEvent - the m.key.verification.start event that - * initiated this verification, if any - * - * @param request - the key verification request object related to - * this verification, if any - */ - constructor(channel, baseApis, userId, deviceId, startEvent, request) { - super(); - this.channel = channel; - this.baseApis = baseApis; - this.userId = userId; - this.deviceId = deviceId; - this.startEvent = startEvent; - this.request = request; - this.cancelled = false; - this._done = false; - this.promise = null; - this.transactionTimeoutTimer = null; - } - get initiatedByMe() { - // if there is no start event yet, - // we probably want to send it, - // which happens if we initiate - if (!this.startEvent) { - return true; - } - const sender = this.startEvent.getSender(); - const content = this.startEvent.getContent(); - return sender === this.baseApis.getUserId() && content.from_device === this.baseApis.getDeviceId(); - } - get hasBeenCancelled() { - return this.cancelled; - } - resetTimer() { - logger_1.logger.info("Refreshing/starting the verification transaction timeout timer"); - if (this.transactionTimeoutTimer !== null) { - clearTimeout(this.transactionTimeoutTimer); - } - this.transactionTimeoutTimer = setTimeout(() => { - if (!this._done && !this.cancelled) { - logger_1.logger.info("Triggering verification timeout"); - this.cancel(timeoutException); - } - }, 10 * 60 * 1000); // 10 minutes - } - endTimer() { - if (this.transactionTimeoutTimer !== null) { - clearTimeout(this.transactionTimeoutTimer); - this.transactionTimeoutTimer = null; - } - } - send(type, uncompletedContent) { - return this.channel.send(type, uncompletedContent); - } - waitForEvent(type) { - if (this._done) { - return Promise.reject(new Error("Verification is already done")); - } - const existingEvent = this.request.getEventFromOtherParty(type); - if (existingEvent) { - return Promise.resolve(existingEvent); - } - this.expectedEvent = type; - return new Promise((resolve, reject) => { - this.resolveEvent = resolve; - this.rejectEvent = reject; - }); - } - canSwitchStartEvent(event) { - return false; - } - switchStartEvent(event) { - if (this.canSwitchStartEvent(event)) { - logger_1.logger.log("Verification Base: switching verification start event", { restartingFlow: !!this.rejectEvent }); - if (this.rejectEvent) { - const reject = this.rejectEvent; - this.rejectEvent = undefined; - reject(new SwitchStartEventError(event)); - } - else { - this.startEvent = event; - } - } - } - handleEvent(e) { - var _a; - if (this._done) { - return; - } - else if (e.getType() === this.expectedEvent) { - // if we receive an expected m.key.verification.done, then just - // ignore it, since we don't need to do anything about it - if (this.expectedEvent !== event_2.EventType.KeyVerificationDone) { - this.expectedEvent = undefined; - this.rejectEvent = undefined; - this.resetTimer(); - (_a = this.resolveEvent) === null || _a === void 0 ? void 0 : _a.call(this, e); - } - } - else if (e.getType() === event_2.EventType.KeyVerificationCancel) { - const reject = this.reject; - this.reject = undefined; - // there is only promise to reject if verify has been called - if (reject) { - const content = e.getContent(); - const { reason, code } = content; - reject(new Error(`Other side cancelled verification ` + `because ${reason} (${code})`)); - } - } - else if (this.expectedEvent) { - // only cancel if there is an event expected. - // if there is no event expected, it means verify() wasn't called - // and we're just replaying the timeline events when syncing - // after a refresh when the events haven't been stored in the cache yet. - const exception = new Error("Unexpected message: expecting " + this.expectedEvent + " but got " + e.getType()); - this.expectedEvent = undefined; - if (this.rejectEvent) { - const reject = this.rejectEvent; - this.rejectEvent = undefined; - reject(exception); - } - this.cancel(exception); - } - } - done() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - this.endTimer(); // always kill the activity timer - if (!this._done) { - this.request.onVerifierFinished(); - (_a = this.resolve) === null || _a === void 0 ? void 0 : _a.call(this); - return (0, CrossSigning_1.requestKeysDuringVerification)(this.baseApis, this.userId, this.deviceId); - } - }); - } - cancel(e) { - this.endTimer(); // always kill the activity timer - if (!this._done) { - this.cancelled = true; - this.request.onVerifierCancelled(); - if (this.userId && this.deviceId) { - // send a cancellation to the other user (if it wasn't - // cancelled by the other user) - if (e === timeoutException) { - const timeoutEvent = (0, Error_1.newTimeoutError)(); - this.send(timeoutEvent.getType(), timeoutEvent.getContent()); - } - else if (e instanceof event_1.MatrixEvent) { - const sender = e.getSender(); - if (sender !== this.userId) { - const content = e.getContent(); - if (e.getType() === event_2.EventType.KeyVerificationCancel) { - content.code = content.code || "m.unknown"; - content.reason = content.reason || content.body || "Unknown reason"; - this.send(event_2.EventType.KeyVerificationCancel, content); - } - else { - this.send(event_2.EventType.KeyVerificationCancel, { - code: "m.unknown", - reason: content.body || "Unknown reason", - }); - } - } - } - else { - this.send(event_2.EventType.KeyVerificationCancel, { - code: "m.unknown", - reason: e.toString(), - }); - } - } - if (this.promise !== null) { - // when we cancel without a promise, we end up with a promise - // but no reject function. If cancel is called again, we'd error. - if (this.reject) - this.reject(e); - } - else { - // FIXME: this causes an "Uncaught promise" console message - // if nothing ends up chaining this promise. - this.promise = Promise.reject(e); - } - // Also emit a 'cancel' event that the app can listen for to detect cancellation - // before calling verify() - this.emit(VerificationEvent.Cancel, e); - } - } - /** - * Begin the key verification - * - * @returns Promise which resolves when the verification has - * completed. - */ - verify() { - if (this.promise) - return this.promise; - this.promise = new Promise((resolve, reject) => { - this.resolve = (...args) => { - this._done = true; - this.endTimer(); - resolve(...args); - }; - this.reject = (e) => { - this._done = true; - this.endTimer(); - reject(e); - }; - }); - if (this.doVerification && !this.started) { - this.started = true; - this.resetTimer(); // restart the timeout - new Promise((resolve, reject) => { - var _a; - const crossSignId = (_a = this.baseApis.crypto.deviceList.getStoredCrossSigningForUser(this.userId)) === null || _a === void 0 ? void 0 : _a.getId(); - if (crossSignId === this.deviceId) { - reject(new Error("Device ID is the same as the cross-signing ID")); - } - resolve(); - }) - .then(() => this.doVerification()) - .then(this.done.bind(this), this.cancel.bind(this)); - } - return this.promise; - } - verifyKeys(userId, keys, verifier) { - return __awaiter(this, void 0, void 0, function* () { - // we try to verify all the keys that we're told about, but we might - // not know about all of them, so keep track of the keys that we know - // about, and ignore the rest - const verifiedDevices = []; - for (const [keyId, keyInfo] of Object.entries(keys)) { - const deviceId = keyId.split(":", 2)[1]; - const device = this.baseApis.getStoredDevice(userId, deviceId); - if (device) { - verifier(keyId, device, keyInfo); - verifiedDevices.push([deviceId, keyId, device.keys[keyId]]); - } - else { - const crossSigningInfo = this.baseApis.crypto.deviceList.getStoredCrossSigningForUser(userId); - if (crossSigningInfo && crossSigningInfo.getId() === deviceId) { - verifier(keyId, deviceinfo_1.DeviceInfo.fromStorage({ - keys: { - [keyId]: deviceId, - }, - }, deviceId), keyInfo); - verifiedDevices.push([deviceId, keyId, deviceId]); - } - else { - logger_1.logger.warn(`verification: Could not find device ${deviceId} to verify`); - } - } - } - // if none of the keys could be verified, then error because the app - // should be informed about that - if (!verifiedDevices.length) { - throw new Error("No devices could be verified"); - } - logger_1.logger.info("Verification completed! Marking devices verified: ", verifiedDevices); - // TODO: There should probably be a batch version of this, otherwise it's going - // to upload each signature in a separate API call which is silly because the - // API supports as many signatures as you like. - for (const [deviceId, keyId, key] of verifiedDevices) { - yield this.baseApis.crypto.setDeviceVerification(userId, deviceId, true, null, null, { [keyId]: key }); - } - // if one of the user's own devices is being marked as verified / unverified, - // check the key backup status, since whether or not we use this depends on - // whether it has a signature from a verified device - if (userId == this.baseApis.credentials.userId) { - yield this.baseApis.checkKeyBackup(); - } - }); - } - get events() { - return undefined; - } -} -exports.VerificationBase = VerificationBase; - -},{"../../@types/event":306,"../../logger":374,"../../models/event":383,"../../models/typed-event-emitter":395,"../CrossSigning":324,"../deviceinfo":340,"./Error":350}],350:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.errorFromEvent = exports.newInvalidMessageError = exports.newKeyMismatchError = exports.newUnexpectedMessageError = exports.newUnknownMethodError = exports.newTimeoutError = exports.newUserCancelledError = exports.errorFactory = exports.newVerificationError = void 0; -/** - * Error messages. - */ -const event_1 = require("../../models/event"); -const event_2 = require("../../@types/event"); -function newVerificationError(code, reason, extraData) { - const content = Object.assign({}, { code, reason }, extraData); - return new event_1.MatrixEvent({ - type: event_2.EventType.KeyVerificationCancel, - content, - }); -} -exports.newVerificationError = newVerificationError; -function errorFactory(code, reason) { - return function (extraData) { - return newVerificationError(code, reason, extraData); - }; -} -exports.errorFactory = errorFactory; -/** - * The verification was cancelled by the user. - */ -exports.newUserCancelledError = errorFactory("m.user", "Cancelled by user"); -/** - * The verification timed out. - */ -exports.newTimeoutError = errorFactory("m.timeout", "Timed out"); -/** - * An unknown method was selected. - */ -exports.newUnknownMethodError = errorFactory("m.unknown_method", "Unknown method"); -/** - * An unexpected message was sent. - */ -exports.newUnexpectedMessageError = errorFactory("m.unexpected_message", "Unexpected message"); -/** - * The key does not match. - */ -exports.newKeyMismatchError = errorFactory("m.key_mismatch", "Key mismatch"); -/** - * An invalid message was sent. - */ -exports.newInvalidMessageError = errorFactory("m.invalid_message", "Invalid message"); -function errorFromEvent(event) { - const content = event.getContent(); - if (content) { - const { code, reason } = content; - return { code, reason }; - } - else { - return { code: "Unknown error", reason: "m.unknown" }; - } -} -exports.errorFromEvent = errorFromEvent; - -},{"../../@types/event":306,"../../models/event":383}],351:[function(require,module,exports){ -"use strict"; -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IllegalMethod = void 0; -/** - * Verification method that is illegal to have (cannot possibly - * do verification with this method). - */ -const Base_1 = require("./Base"); -class IllegalMethod extends Base_1.VerificationBase { - constructor() { - super(...arguments); - this.doVerification = () => __awaiter(this, void 0, void 0, function* () { - throw new Error("Verification is not possible with this method"); - }); - } - static factory(channel, baseApis, userId, deviceId, startEvent, request) { - return new IllegalMethod(channel, baseApis, userId, deviceId, startEvent, request); - } - // eslint-disable-next-line @typescript-eslint/naming-convention - static get NAME() { - // Typically the name will be something else, but to complete - // the contract we offer a default one here. - return "org.matrix.illegal_method"; - } -} -exports.IllegalMethod = IllegalMethod; - -},{"./Base":349}],352:[function(require,module,exports){ -(function (global,Buffer){(function (){ -"use strict"; -/* -Copyright 2018 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.QRCodeData = exports.ReciprocateQRCode = exports.QrCodeEvent = exports.SCAN_QR_CODE_METHOD = exports.SHOW_QR_CODE_METHOD = void 0; -/** - * QR code key verification. - */ -const Base_1 = require("./Base"); -const Error_1 = require("./Error"); -const olmlib_1 = require("../olmlib"); -const logger_1 = require("../../logger"); -exports.SHOW_QR_CODE_METHOD = "m.qr_code.show.v1"; -exports.SCAN_QR_CODE_METHOD = "m.qr_code.scan.v1"; -var QrCodeEvent; -(function (QrCodeEvent) { - QrCodeEvent["ShowReciprocateQr"] = "show_reciprocate_qr"; -})(QrCodeEvent = exports.QrCodeEvent || (exports.QrCodeEvent = {})); -class ReciprocateQRCode extends Base_1.VerificationBase { - constructor() { - super(...arguments); - this.doVerification = () => __awaiter(this, void 0, void 0, function* () { - if (!this.startEvent) { - // TODO: Support scanning QR codes - throw new Error("It is not currently possible to start verification" + "with this method yet."); - } - const { qrCodeData } = this.request; - // 1. check the secret - if (this.startEvent.getContent()["secret"] !== (qrCodeData === null || qrCodeData === void 0 ? void 0 : qrCodeData.encodedSharedSecret)) { - throw (0, Error_1.newKeyMismatchError)(); - } - // 2. ask if other user shows shield as well - yield new Promise((resolve, reject) => { - this.reciprocateQREvent = { - confirm: resolve, - cancel: () => reject((0, Error_1.newUserCancelledError)()), - }; - this.emit(QrCodeEvent.ShowReciprocateQr, this.reciprocateQREvent); - }); - // 3. determine key to sign / mark as trusted - const keys = {}; - switch (qrCodeData === null || qrCodeData === void 0 ? void 0 : qrCodeData.mode) { - case Mode.VerifyOtherUser: { - // add master key to keys to be signed, only if we're not doing self-verification - const masterKey = qrCodeData.otherUserMasterKey; - keys[`ed25519:${masterKey}`] = masterKey; - break; - } - case Mode.VerifySelfTrusted: { - const deviceId = this.request.targetDevice.deviceId; - keys[`ed25519:${deviceId}`] = qrCodeData.otherDeviceKey; - break; - } - case Mode.VerifySelfUntrusted: { - const masterKey = qrCodeData.myMasterKey; - keys[`ed25519:${masterKey}`] = masterKey; - break; - } - } - // 4. sign the key (or mark own MSK as verified in case of MODE_VERIFY_SELF_TRUSTED) - yield this.verifyKeys(this.userId, keys, (keyId, device, keyInfo) => { - // make sure the device has the expected keys - const targetKey = keys[keyId]; - if (!targetKey) - throw (0, Error_1.newKeyMismatchError)(); - if (keyInfo !== targetKey) { - logger_1.logger.error("key ID from key info does not match"); - throw (0, Error_1.newKeyMismatchError)(); - } - for (const deviceKeyId in device.keys) { - if (!deviceKeyId.startsWith("ed25519")) - continue; - const deviceTargetKey = keys[deviceKeyId]; - if (!deviceTargetKey) - throw (0, Error_1.newKeyMismatchError)(); - if (device.keys[deviceKeyId] !== deviceTargetKey) { - logger_1.logger.error("master key does not match"); - throw (0, Error_1.newKeyMismatchError)(); - } - } - }); - }); - } - static factory(channel, baseApis, userId, deviceId, startEvent, request) { - return new ReciprocateQRCode(channel, baseApis, userId, deviceId, startEvent, request); - } - // eslint-disable-next-line @typescript-eslint/naming-convention - static get NAME() { - return "m.reciprocate.v1"; - } -} -exports.ReciprocateQRCode = ReciprocateQRCode; -const CODE_VERSION = 0x02; // the version of binary QR codes we support -const BINARY_PREFIX = "MATRIX"; // ASCII, used to prefix the binary format -var Mode; -(function (Mode) { - Mode[Mode["VerifyOtherUser"] = 0] = "VerifyOtherUser"; - Mode[Mode["VerifySelfTrusted"] = 1] = "VerifySelfTrusted"; - Mode[Mode["VerifySelfUntrusted"] = 2] = "VerifySelfUntrusted"; -})(Mode || (Mode = {})); -class QRCodeData { - constructor(mode, sharedSecret, - // only set when mode is MODE_VERIFY_OTHER_USER, master key of other party at time of generating QR code - otherUserMasterKey, - // only set when mode is MODE_VERIFY_SELF_TRUSTED, device key of other party at time of generating QR code - otherDeviceKey, - // only set when mode is MODE_VERIFY_SELF_UNTRUSTED, own master key at time of generating QR code - myMasterKey, buffer) { - this.mode = mode; - this.sharedSecret = sharedSecret; - this.otherUserMasterKey = otherUserMasterKey; - this.otherDeviceKey = otherDeviceKey; - this.myMasterKey = myMasterKey; - this.buffer = buffer; - } - static create(request, client) { - return __awaiter(this, void 0, void 0, function* () { - const sharedSecret = QRCodeData.generateSharedSecret(); - const mode = QRCodeData.determineMode(request, client); - let otherUserMasterKey = null; - let otherDeviceKey = null; - let myMasterKey = null; - if (mode === Mode.VerifyOtherUser) { - const otherUserCrossSigningInfo = client.getStoredCrossSigningForUser(request.otherUserId); - otherUserMasterKey = otherUserCrossSigningInfo.getId("master"); - } - else if (mode === Mode.VerifySelfTrusted) { - otherDeviceKey = yield QRCodeData.getOtherDeviceKey(request, client); - } - else if (mode === Mode.VerifySelfUntrusted) { - const myUserId = client.getUserId(); - const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId); - myMasterKey = myCrossSigningInfo.getId("master"); - } - const qrData = QRCodeData.generateQrData(request, client, mode, sharedSecret, otherUserMasterKey, otherDeviceKey, myMasterKey); - const buffer = QRCodeData.generateBuffer(qrData); - return new QRCodeData(mode, sharedSecret, otherUserMasterKey, otherDeviceKey, myMasterKey, buffer); - }); - } - /** - * The unpadded base64 encoded shared secret. - */ - get encodedSharedSecret() { - return this.sharedSecret; - } - getBuffer() { - return this.buffer; - } - static generateSharedSecret() { - const secretBytes = new Uint8Array(11); - global.crypto.getRandomValues(secretBytes); - return (0, olmlib_1.encodeUnpaddedBase64)(secretBytes); - } - static getOtherDeviceKey(request, client) { - return __awaiter(this, void 0, void 0, function* () { - const myUserId = client.getUserId(); - const otherDevice = request.targetDevice; - const device = otherDevice.deviceId ? client.getStoredDevice(myUserId, otherDevice.deviceId) : undefined; - if (!device) { - throw new Error("could not find device " + (otherDevice === null || otherDevice === void 0 ? void 0 : otherDevice.deviceId)); - } - return device.getFingerprint(); - }); - } - static determineMode(request, client) { - const myUserId = client.getUserId(); - const otherUserId = request.otherUserId; - let mode = Mode.VerifyOtherUser; - if (myUserId === otherUserId) { - // Mode changes depending on whether or not we trust the master cross signing key - const myTrust = client.checkUserTrust(myUserId); - if (myTrust.isCrossSigningVerified()) { - mode = Mode.VerifySelfTrusted; - } - else { - mode = Mode.VerifySelfUntrusted; - } - } - return mode; - } - static generateQrData(request, client, mode, encodedSharedSecret, otherUserMasterKey, otherDeviceKey, myMasterKey) { - const myUserId = client.getUserId(); - const transactionId = request.channel.transactionId; - const qrData = { - prefix: BINARY_PREFIX, - version: CODE_VERSION, - mode, - transactionId, - firstKeyB64: "", - secondKeyB64: "", - secretB64: encodedSharedSecret, - }; - const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId); - if (mode === Mode.VerifyOtherUser) { - // First key is our master cross signing key - qrData.firstKeyB64 = myCrossSigningInfo.getId("master"); - // Second key is the other user's master cross signing key - qrData.secondKeyB64 = otherUserMasterKey; - } - else if (mode === Mode.VerifySelfTrusted) { - // First key is our master cross signing key - qrData.firstKeyB64 = myCrossSigningInfo.getId("master"); - qrData.secondKeyB64 = otherDeviceKey; - } - else if (mode === Mode.VerifySelfUntrusted) { - // First key is our device's key - qrData.firstKeyB64 = client.getDeviceEd25519Key(); - // Second key is what we think our master cross signing key is - qrData.secondKeyB64 = myMasterKey; - } - return qrData; - } - static generateBuffer(qrData) { - let buf = Buffer.alloc(0); // we'll concat our way through life - const appendByte = (b) => { - const tmpBuf = Buffer.from([b]); - buf = Buffer.concat([buf, tmpBuf]); - }; - const appendInt = (i) => { - const tmpBuf = Buffer.alloc(2); - tmpBuf.writeInt16BE(i, 0); - buf = Buffer.concat([buf, tmpBuf]); - }; - const appendStr = (s, enc, withLengthPrefix = true) => { - const tmpBuf = Buffer.from(s, enc); - if (withLengthPrefix) - appendInt(tmpBuf.byteLength); - buf = Buffer.concat([buf, tmpBuf]); - }; - const appendEncBase64 = (b64) => { - const b = (0, olmlib_1.decodeBase64)(b64); - const tmpBuf = Buffer.from(b); - buf = Buffer.concat([buf, tmpBuf]); - }; - // Actually build the buffer for the QR code - appendStr(qrData.prefix, "ascii", false); - appendByte(qrData.version); - appendByte(qrData.mode); - appendStr(qrData.transactionId, "utf-8"); - appendEncBase64(qrData.firstKeyB64); - appendEncBase64(qrData.secondKeyB64); - appendEncBase64(qrData.secretB64); - return buf; - } -} -exports.QRCodeData = QRCodeData; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) - -},{"../../logger":374,"../olmlib":343,"./Base":349,"./Error":350,"buffer":68}],353:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2018 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SAS = exports.SasEvent = void 0; -/** - * Short Authentication String (SAS) verification. - */ -const another_json_1 = __importDefault(require("another-json")); -const Base_1 = require("./Base"); -const Error_1 = require("./Error"); -const logger_1 = require("../../logger"); -const SASDecimal_1 = require("./SASDecimal"); -const event_1 = require("../../@types/event"); -const START_TYPE = event_1.EventType.KeyVerificationStart; -const EVENTS = [event_1.EventType.KeyVerificationAccept, event_1.EventType.KeyVerificationKey, event_1.EventType.KeyVerificationMac]; -let olmutil; -const newMismatchedSASError = (0, Error_1.errorFactory)("m.mismatched_sas", "Mismatched short authentication string"); -const newMismatchedCommitmentError = (0, Error_1.errorFactory)("m.mismatched_commitment", "Mismatched commitment"); -const emojiMapping = [ - ["🐶", "dog"], - ["🐱", "cat"], - ["🦁", "lion"], - ["🐎", "horse"], - ["🦄", "unicorn"], - ["🐷", "pig"], - ["🐘", "elephant"], - ["🐰", "rabbit"], - ["🐼", "panda"], - ["🐓", "rooster"], - ["🐧", "penguin"], - ["🐢", "turtle"], - ["🐟", "fish"], - ["🐙", "octopus"], - ["🦋", "butterfly"], - ["🌷", "flower"], - ["🌳", "tree"], - ["🌵", "cactus"], - ["🍄", "mushroom"], - ["🌏", "globe"], - ["🌙", "moon"], - ["☁️", "cloud"], - ["🔥", "fire"], - ["🍌", "banana"], - ["🍎", "apple"], - ["🍓", "strawberry"], - ["🌽", "corn"], - ["🍕", "pizza"], - ["🎂", "cake"], - ["❤️", "heart"], - ["🙂", "smiley"], - ["🤖", "robot"], - ["🎩", "hat"], - ["👓", "glasses"], - ["🔧", "spanner"], - ["🎅", "santa"], - ["👍", "thumbs up"], - ["☂️", "umbrella"], - ["⌛", "hourglass"], - ["⏰", "clock"], - ["🎁", "gift"], - ["💡", "light bulb"], - ["📕", "book"], - ["✏️", "pencil"], - ["📎", "paperclip"], - ["✂️", "scissors"], - ["🔒", "lock"], - ["🔑", "key"], - ["🔨", "hammer"], - ["☎️", "telephone"], - ["🏁", "flag"], - ["🚂", "train"], - ["🚲", "bicycle"], - ["✈️", "aeroplane"], - ["🚀", "rocket"], - ["🏆", "trophy"], - ["⚽", "ball"], - ["🎸", "guitar"], - ["🎺", "trumpet"], - ["🔔", "bell"], - ["⚓️", "anchor"], - ["🎧", "headphones"], - ["📁", "folder"], - ["📌", "pin"], // 63 -]; -function generateEmojiSas(sasBytes) { - const emojis = [ - // just like base64 encoding - sasBytes[0] >> 2, - ((sasBytes[0] & 0x3) << 4) | (sasBytes[1] >> 4), - ((sasBytes[1] & 0xf) << 2) | (sasBytes[2] >> 6), - sasBytes[2] & 0x3f, - sasBytes[3] >> 2, - ((sasBytes[3] & 0x3) << 4) | (sasBytes[4] >> 4), - ((sasBytes[4] & 0xf) << 2) | (sasBytes[5] >> 6), - ]; - return emojis.map((num) => emojiMapping[num]); -} -const sasGenerators = { - decimal: SASDecimal_1.generateDecimalSas, - emoji: generateEmojiSas, -}; -function generateSas(sasBytes, methods) { - const sas = {}; - for (const method of methods) { - if (method in sasGenerators) { - // @ts-ignore - ts doesn't like us mixing types like this - sas[method] = sasGenerators[method](Array.from(sasBytes)); - } - } - return sas; -} -const macMethods = { - "hkdf-hmac-sha256": "calculate_mac", - "org.matrix.msc3783.hkdf-hmac-sha256": "calculate_mac_fixed_base64", - "hkdf-hmac-sha256.v2": "calculate_mac_fixed_base64", - "hmac-sha256": "calculate_mac_long_kdf", -}; -function calculateMAC(olmSAS, method) { - return function (input, info) { - const mac = olmSAS[macMethods[method]](input, info); - logger_1.logger.log("SAS calculateMAC:", method, [input, info], mac); - return mac; - }; -} -const calculateKeyAgreement = { - // eslint-disable-next-line @typescript-eslint/naming-convention - "curve25519-hkdf-sha256": function (sas, olmSAS, bytes) { - const ourInfo = `${sas.baseApis.getUserId()}|${sas.baseApis.deviceId}|` + `${sas.ourSASPubKey}|`; - const theirInfo = `${sas.userId}|${sas.deviceId}|${sas.theirSASPubKey}|`; - const sasInfo = "MATRIX_KEY_VERIFICATION_SAS|" + - (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo) + - sas.channel.transactionId; - return olmSAS.generate_bytes(sasInfo, bytes); - }, - "curve25519": function (sas, olmSAS, bytes) { - const ourInfo = `${sas.baseApis.getUserId()}${sas.baseApis.deviceId}`; - const theirInfo = `${sas.userId}${sas.deviceId}`; - const sasInfo = "MATRIX_KEY_VERIFICATION_SAS" + - (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo) + - sas.channel.transactionId; - return olmSAS.generate_bytes(sasInfo, bytes); - }, -}; -/* lists of algorithms/methods that are supported. The key agreement, hashes, - * and MAC lists should be sorted in order of preference (most preferred - * first). - */ -const KEY_AGREEMENT_LIST = ["curve25519-hkdf-sha256", "curve25519"]; -const HASHES_LIST = ["sha256"]; -const MAC_LIST = [ - "hkdf-hmac-sha256.v2", - "org.matrix.msc3783.hkdf-hmac-sha256", - "hkdf-hmac-sha256", - "hmac-sha256", -]; -const SAS_LIST = Object.keys(sasGenerators); -const KEY_AGREEMENT_SET = new Set(KEY_AGREEMENT_LIST); -const HASHES_SET = new Set(HASHES_LIST); -const MAC_SET = new Set(MAC_LIST); -const SAS_SET = new Set(SAS_LIST); -function intersection(anArray, aSet) { - return Array.isArray(anArray) ? anArray.filter((x) => aSet.has(x)) : []; -} -var SasEvent; -(function (SasEvent) { - SasEvent["ShowSas"] = "show_sas"; -})(SasEvent = exports.SasEvent || (exports.SasEvent = {})); -class SAS extends Base_1.VerificationBase { - constructor() { - super(...arguments); - this.doVerification = () => __awaiter(this, void 0, void 0, function* () { - yield global.Olm.init(); - olmutil = olmutil || new global.Olm.Utility(); - // make sure user's keys are downloaded - yield this.baseApis.downloadKeys([this.userId]); - let retry = false; - do { - try { - if (this.initiatedByMe) { - return yield this.doSendVerification(); - } - else { - return yield this.doRespondVerification(); - } - } - catch (err) { - if (err instanceof Base_1.SwitchStartEventError) { - // this changes what initiatedByMe returns - this.startEvent = err.startEvent; - retry = true; - } - else { - throw err; - } - } - } while (retry); - }); - } - // eslint-disable-next-line @typescript-eslint/naming-convention - static get NAME() { - return "m.sas.v1"; - } - get events() { - return EVENTS; - } - canSwitchStartEvent(event) { - if (event.getType() !== START_TYPE) { - return false; - } - const content = event.getContent(); - return (content === null || content === void 0 ? void 0 : content.method) === SAS.NAME && !!this.waitingForAccept; - } - sendStart() { - return __awaiter(this, void 0, void 0, function* () { - const startContent = this.channel.completeContent(START_TYPE, { - method: SAS.NAME, - from_device: this.baseApis.deviceId, - key_agreement_protocols: KEY_AGREEMENT_LIST, - hashes: HASHES_LIST, - message_authentication_codes: MAC_LIST, - // FIXME: allow app to specify what SAS methods can be used - short_authentication_string: SAS_LIST, - }); - yield this.channel.sendCompleted(START_TYPE, startContent); - return startContent; - }); - } - verifyAndCheckMAC(keyAgreement, sasMethods, olmSAS, macMethod) { - return __awaiter(this, void 0, void 0, function* () { - const sasBytes = calculateKeyAgreement[keyAgreement](this, olmSAS, 6); - const verifySAS = new Promise((resolve, reject) => { - this.sasEvent = { - sas: generateSas(sasBytes, sasMethods), - confirm: () => __awaiter(this, void 0, void 0, function* () { - try { - yield this.sendMAC(olmSAS, macMethod); - resolve(); - } - catch (err) { - reject(err); - } - }), - cancel: () => reject((0, Error_1.newUserCancelledError)()), - mismatch: () => reject(newMismatchedSASError()), - }; - this.emit(SasEvent.ShowSas, this.sasEvent); - }); - const [e] = yield Promise.all([ - this.waitForEvent(event_1.EventType.KeyVerificationMac).then((e) => { - // we don't expect any more messages from the other - // party, and they may send a m.key.verification.done - // when they're done on their end - this.expectedEvent = event_1.EventType.KeyVerificationDone; - return e; - }), - verifySAS, - ]); - const content = e.getContent(); - yield this.checkMAC(olmSAS, content, macMethod); - }); - } - doSendVerification() { - return __awaiter(this, void 0, void 0, function* () { - this.waitingForAccept = true; - let startContent; - if (this.startEvent) { - startContent = this.channel.completedContentFromEvent(this.startEvent); - } - else { - startContent = yield this.sendStart(); - } - // we might have switched to a different start event, - // but was we didn't call _waitForEvent there was no - // call that could throw yet. So check manually that - // we're still on the initiator side - if (!this.initiatedByMe) { - throw new Base_1.SwitchStartEventError(this.startEvent); - } - let e; - try { - e = yield this.waitForEvent(event_1.EventType.KeyVerificationAccept); - } - finally { - this.waitingForAccept = false; - } - let content = e.getContent(); - const sasMethods = intersection(content.short_authentication_string, SAS_SET); - if (!(KEY_AGREEMENT_SET.has(content.key_agreement_protocol) && - HASHES_SET.has(content.hash) && - MAC_SET.has(content.message_authentication_code) && - sasMethods.length)) { - throw (0, Error_1.newUnknownMethodError)(); - } - if (typeof content.commitment !== "string") { - throw (0, Error_1.newInvalidMessageError)(); - } - const keyAgreement = content.key_agreement_protocol; - const macMethod = content.message_authentication_code; - const hashCommitment = content.commitment; - const olmSAS = new global.Olm.SAS(); - try { - this.ourSASPubKey = olmSAS.get_pubkey(); - yield this.send(event_1.EventType.KeyVerificationKey, { - key: this.ourSASPubKey, - }); - e = yield this.waitForEvent(event_1.EventType.KeyVerificationKey); - // FIXME: make sure event is properly formed - content = e.getContent(); - const commitmentStr = content.key + another_json_1.default.stringify(startContent); - // TODO: use selected hash function (when we support multiple) - if (olmutil.sha256(commitmentStr) !== hashCommitment) { - throw newMismatchedCommitmentError(); - } - this.theirSASPubKey = content.key; - olmSAS.set_their_key(content.key); - yield this.verifyAndCheckMAC(keyAgreement, sasMethods, olmSAS, macMethod); - } - finally { - olmSAS.free(); - } - }); - } - doRespondVerification() { - return __awaiter(this, void 0, void 0, function* () { - // as m.related_to is not included in the encrypted content in e2e rooms, - // we need to make sure it is added - let content = this.channel.completedContentFromEvent(this.startEvent); - // Note: we intersect using our pre-made lists, rather than the sets, - // so that the result will be in our order of preference. Then - // fetching the first element from the array will give our preferred - // method out of the ones offered by the other party. - const keyAgreement = intersection(KEY_AGREEMENT_LIST, new Set(content.key_agreement_protocols))[0]; - const hashMethod = intersection(HASHES_LIST, new Set(content.hashes))[0]; - const macMethod = intersection(MAC_LIST, new Set(content.message_authentication_codes))[0]; - // FIXME: allow app to specify what SAS methods can be used - const sasMethods = intersection(content.short_authentication_string, SAS_SET); - if (!(keyAgreement !== undefined && hashMethod !== undefined && macMethod !== undefined && sasMethods.length)) { - throw (0, Error_1.newUnknownMethodError)(); - } - const olmSAS = new global.Olm.SAS(); - try { - const commitmentStr = olmSAS.get_pubkey() + another_json_1.default.stringify(content); - yield this.send(event_1.EventType.KeyVerificationAccept, { - key_agreement_protocol: keyAgreement, - hash: hashMethod, - message_authentication_code: macMethod, - short_authentication_string: sasMethods, - // TODO: use selected hash function (when we support multiple) - commitment: olmutil.sha256(commitmentStr), - }); - const e = yield this.waitForEvent(event_1.EventType.KeyVerificationKey); - // FIXME: make sure event is properly formed - content = e.getContent(); - this.theirSASPubKey = content.key; - olmSAS.set_their_key(content.key); - this.ourSASPubKey = olmSAS.get_pubkey(); - yield this.send(event_1.EventType.KeyVerificationKey, { - key: this.ourSASPubKey, - }); - yield this.verifyAndCheckMAC(keyAgreement, sasMethods, olmSAS, macMethod); - } - finally { - olmSAS.free(); - } - }); - } - sendMAC(olmSAS, method) { - const mac = {}; - const keyList = []; - const baseInfo = "MATRIX_KEY_VERIFICATION_MAC" + - this.baseApis.getUserId() + - this.baseApis.deviceId + - this.userId + - this.deviceId + - this.channel.transactionId; - const deviceKeyId = `ed25519:${this.baseApis.deviceId}`; - mac[deviceKeyId] = calculateMAC(olmSAS, method)(this.baseApis.getDeviceEd25519Key(), baseInfo + deviceKeyId); - keyList.push(deviceKeyId); - const crossSigningId = this.baseApis.getCrossSigningId(); - if (crossSigningId) { - const crossSigningKeyId = `ed25519:${crossSigningId}`; - mac[crossSigningKeyId] = calculateMAC(olmSAS, method)(crossSigningId, baseInfo + crossSigningKeyId); - keyList.push(crossSigningKeyId); - } - const keys = calculateMAC(olmSAS, method)(keyList.sort().join(","), baseInfo + "KEY_IDS"); - return this.send(event_1.EventType.KeyVerificationMac, { mac, keys }); - } - checkMAC(olmSAS, content, method) { - return __awaiter(this, void 0, void 0, function* () { - const baseInfo = "MATRIX_KEY_VERIFICATION_MAC" + - this.userId + - this.deviceId + - this.baseApis.getUserId() + - this.baseApis.deviceId + - this.channel.transactionId; - if (content.keys !== - calculateMAC(olmSAS, method)(Object.keys(content.mac).sort().join(","), baseInfo + "KEY_IDS")) { - throw (0, Error_1.newKeyMismatchError)(); - } - yield this.verifyKeys(this.userId, content.mac, (keyId, device, keyInfo) => { - if (keyInfo !== calculateMAC(olmSAS, method)(device.keys[keyId], baseInfo + keyId)) { - throw (0, Error_1.newKeyMismatchError)(); - } - }); - }); - } -} -exports.SAS = SAS; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../../@types/event":306,"../../logger":374,"./Base":349,"./Error":350,"./SASDecimal":354,"another-json":1}],354:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.generateDecimalSas = void 0; -/** - * Implementation of decimal encoding of SAS as per: - * https://spec.matrix.org/v1.4/client-server-api/#sas-method-decimal - * @param sasBytes - the five bytes generated by HKDF - * @returns the derived three numbers between 1000 and 9191 inclusive - */ -function generateDecimalSas(sasBytes) { - /* - * +--------+--------+--------+--------+--------+ - * | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | - * +--------+--------+--------+--------+--------+ - * bits: 87654321 87654321 87654321 87654321 87654321 - * \____________/\_____________/\____________/ - * 1st number 2nd number 3rd number - */ - return [ - ((sasBytes[0] << 5) | (sasBytes[1] >> 3)) + 1000, - (((sasBytes[1] & 0x7) << 10) | (sasBytes[2] << 2) | (sasBytes[3] >> 6)) + 1000, - (((sasBytes[3] & 0x3f) << 7) | (sasBytes[4] >> 1)) + 1000, - ]; -} -exports.generateDecimalSas = generateDecimalSas; - -},{}],355:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InRoomRequests = exports.InRoomChannel = void 0; -const VerificationRequest_1 = require("./VerificationRequest"); -const logger_1 = require("../../../logger"); -const event_1 = require("../../../@types/event"); -const MESSAGE_TYPE = event_1.EventType.RoomMessage; -const M_REFERENCE = "m.reference"; -const M_RELATES_TO = "m.relates_to"; -/** - * A key verification channel that sends verification events in the timeline of a room. - * Uses the event id of the initial m.key.verification.request event as a transaction id. - */ -class InRoomChannel { - /** - * @param client - the matrix client, to send messages with and get current user & device from. - * @param roomId - id of the room where verification events should be posted in, should be a DM with the given user. - * @param userId - id of user that the verification request is directed at, should be present in the room. - */ - constructor(client, roomId, userId) { - this.client = client; - this.roomId = roomId; - this.userId = userId; - } - get receiveStartFromOtherDevices() { - return true; - } - /** The transaction id generated/used by this verification channel */ - get transactionId() { - return this.requestEventId; - } - static getOtherPartyUserId(event, client) { - const type = InRoomChannel.getEventType(event); - if (type !== VerificationRequest_1.REQUEST_TYPE) { - return; - } - const ownUserId = client.getUserId(); - const sender = event.getSender(); - const content = event.getContent(); - const receiver = content.to; - if (sender === ownUserId) { - return receiver; - } - else if (receiver === ownUserId) { - return sender; - } - } - /** - * @param event - the event to get the timestamp of - * @returns the timestamp when the event was sent - */ - getTimestamp(event) { - return event.getTs(); - } - /** - * Checks whether the given event type should be allowed to initiate a new VerificationRequest over this channel - * @param type - the event type to check - * @returns boolean flag - */ - static canCreateRequest(type) { - return type === VerificationRequest_1.REQUEST_TYPE; - } - canCreateRequest(type) { - return InRoomChannel.canCreateRequest(type); - } - /** - * Extract the transaction id used by a given key verification event, if any - * @param event - the event - * @returns the transaction id - */ - static getTransactionId(event) { - if (InRoomChannel.getEventType(event) === VerificationRequest_1.REQUEST_TYPE) { - return event.getId(); - } - else { - const relation = event.getRelation(); - if ((relation === null || relation === void 0 ? void 0 : relation.rel_type) === M_REFERENCE) { - return relation.event_id; - } - } - } - /** - * Checks whether this event is a well-formed key verification event. - * This only does checks that don't rely on the current state of a potentially already channel - * so we can prevent channels being created by invalid events. - * `handleEvent` can do more checks and choose to ignore invalid events. - * @param event - the event to validate - * @param client - the client to get the current user and device id from - * @returns whether the event is valid and should be passed to handleEvent - */ - static validateEvent(event, client) { - const txnId = InRoomChannel.getTransactionId(event); - if (typeof txnId !== "string" || txnId.length === 0) { - return false; - } - const type = InRoomChannel.getEventType(event); - const content = event.getContent(); - // from here on we're fairly sure that this is supposed to be - // part of a verification request, so be noisy when rejecting something - if (type === VerificationRequest_1.REQUEST_TYPE) { - if (!content || typeof content.to !== "string" || !content.to.length) { - logger_1.logger.log("InRoomChannel: validateEvent: " + "no valid to " + (content && content.to)); - return false; - } - // ignore requests that are not direct to or sent by the syncing user - if (!InRoomChannel.getOtherPartyUserId(event, client)) { - logger_1.logger.log("InRoomChannel: validateEvent: " + - `not directed to or sent by me: ${event.getSender()}` + - `, ${content && content.to}`); - return false; - } - } - return VerificationRequest_1.VerificationRequest.validateEvent(type, event, client); - } - /** - * As m.key.verification.request events are as m.room.message events with the InRoomChannel - * to have a fallback message in non-supporting clients, we map the real event type - * to the symbolic one to keep things in unison with ToDeviceChannel - * @param event - the event to get the type of - * @returns the "symbolic" event type - */ - static getEventType(event) { - const type = event.getType(); - if (type === MESSAGE_TYPE) { - const content = event.getContent(); - if (content) { - const { msgtype } = content; - if (msgtype === VerificationRequest_1.REQUEST_TYPE) { - return VerificationRequest_1.REQUEST_TYPE; - } - } - } - if (type && type !== VerificationRequest_1.REQUEST_TYPE) { - return type; - } - else { - return ""; - } - } - /** - * Changes the state of the channel, request, and verifier in response to a key verification event. - * @param event - to handle - * @param request - the request to forward handling to - * @param isLiveEvent - whether this is an even received through sync or not - * @returns a promise that resolves when any requests as an answer to the passed-in event are sent. - */ - handleEvent(event, request, isLiveEvent = false) { - return __awaiter(this, void 0, void 0, function* () { - // prevent processing the same event multiple times, as under - // some circumstances Room.timeline can get emitted twice for the same event - if (request.hasEventId(event.getId())) { - return; - } - const type = InRoomChannel.getEventType(event); - // do validations that need state (roomId, userId), - // ignore if invalid - if (event.getRoomId() !== this.roomId) { - return; - } - // set userId if not set already - if (!this.userId) { - const userId = InRoomChannel.getOtherPartyUserId(event, this.client); - if (userId) { - this.userId = userId; - } - } - // ignore events not sent by us or the other party - const ownUserId = this.client.getUserId(); - const sender = event.getSender(); - if (this.userId) { - if (sender !== ownUserId && sender !== this.userId) { - logger_1.logger.log(`InRoomChannel: ignoring verification event from non-participating sender ${sender}`); - return; - } - } - if (!this.requestEventId) { - this.requestEventId = InRoomChannel.getTransactionId(event); - } - const isRemoteEcho = !!event.getUnsigned().transaction_id; - const isSentByUs = event.getSender() === this.client.getUserId(); - return request.handleEvent(type, event, isLiveEvent, isRemoteEcho, isSentByUs); - }); - } - /** - * Adds the transaction id (relation) back to a received event - * so it has the same format as returned by `completeContent` before sending. - * The relation can not appear on the event content because of encryption, - * relations are excluded from encryption. - * @param event - the received event - * @returns the content object with the relation added again - */ - completedContentFromEvent(event) { - // ensure m.related_to is included in e2ee rooms - // as the field is excluded from encryption - const content = Object.assign({}, event.getContent()); - content[M_RELATES_TO] = event.getRelation(); - return content; - } - /** - * Add all the fields to content needed for sending it over this channel. - * This is public so verification methods (SAS uses this) can get the exact - * content that will be sent independent of the used channel, - * as they need to calculate the hash of it. - * @param type - the event type - * @param content - the (incomplete) content - * @returns the complete content, as it will be sent. - */ - completeContent(type, content) { - content = Object.assign({}, content); - if (type === VerificationRequest_1.REQUEST_TYPE || type === VerificationRequest_1.READY_TYPE || type === VerificationRequest_1.START_TYPE) { - content.from_device = this.client.getDeviceId(); - } - if (type === VerificationRequest_1.REQUEST_TYPE) { - // type is mapped to m.room.message in the send method - content = { - body: this.client.getUserId() + - " is requesting to verify " + - "your key, but your client does not support in-chat key " + - "verification. You will need to use legacy key " + - "verification to verify keys.", - msgtype: VerificationRequest_1.REQUEST_TYPE, - to: this.userId, - from_device: content.from_device, - methods: content.methods, - }; - } - else { - content[M_RELATES_TO] = { - rel_type: M_REFERENCE, - event_id: this.transactionId, - }; - } - return content; - } - /** - * Send an event over the channel with the content not having gone through `completeContent`. - * @param type - the event type - * @param uncompletedContent - the (incomplete) content - * @returns the promise of the request - */ - send(type, uncompletedContent) { - const content = this.completeContent(type, uncompletedContent); - return this.sendCompleted(type, content); - } - /** - * Send an event over the channel with the content having gone through `completeContent` already. - * @param type - the event type - * @returns the promise of the request - */ - sendCompleted(type, content) { - return __awaiter(this, void 0, void 0, function* () { - let sendType = type; - if (type === VerificationRequest_1.REQUEST_TYPE) { - sendType = MESSAGE_TYPE; - } - const response = yield this.client.sendEvent(this.roomId, sendType, content); - if (type === VerificationRequest_1.REQUEST_TYPE) { - this.requestEventId = response.event_id; - } - }); - } -} -exports.InRoomChannel = InRoomChannel; -class InRoomRequests { - constructor() { - this.requestsByRoomId = new Map(); - } - getRequest(event) { - const roomId = event.getRoomId(); - const txnId = InRoomChannel.getTransactionId(event); - return this.getRequestByTxnId(roomId, txnId); - } - getRequestByChannel(channel) { - return this.getRequestByTxnId(channel.roomId, channel.transactionId); - } - getRequestByTxnId(roomId, txnId) { - const requestsByTxnId = this.requestsByRoomId.get(roomId); - if (requestsByTxnId) { - return requestsByTxnId.get(txnId); - } - } - setRequest(event, request) { - this.doSetRequest(event.getRoomId(), InRoomChannel.getTransactionId(event), request); - } - setRequestByChannel(channel, request) { - this.doSetRequest(channel.roomId, channel.transactionId, request); - } - doSetRequest(roomId, txnId, request) { - let requestsByTxnId = this.requestsByRoomId.get(roomId); - if (!requestsByTxnId) { - requestsByTxnId = new Map(); - this.requestsByRoomId.set(roomId, requestsByTxnId); - } - requestsByTxnId.set(txnId, request); - } - removeRequest(event) { - const roomId = event.getRoomId(); - const requestsByTxnId = this.requestsByRoomId.get(roomId); - if (requestsByTxnId) { - requestsByTxnId.delete(InRoomChannel.getTransactionId(event)); - if (requestsByTxnId.size === 0) { - this.requestsByRoomId.delete(roomId); - } - } - } - findRequestInProgress(roomId) { - const requestsByTxnId = this.requestsByRoomId.get(roomId); - if (requestsByTxnId) { - for (const request of requestsByTxnId.values()) { - if (request.pending) { - return request; - } - } - } - } -} -exports.InRoomRequests = InRoomRequests; - -},{"../../../@types/event":306,"../../../logger":374,"./VerificationRequest":357}],356:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ToDeviceRequests = exports.ToDeviceChannel = void 0; -const randomstring_1 = require("../../../randomstring"); -const logger_1 = require("../../../logger"); -const VerificationRequest_1 = require("./VerificationRequest"); -const Error_1 = require("../Error"); -const event_1 = require("../../../models/event"); -/** - * A key verification channel that sends verification events over to_device messages. - * Generates its own transaction ids. - */ -class ToDeviceChannel { - // userId and devices of user we're about to verify - constructor(client, userId, devices, transactionId, deviceId) { - this.client = client; - this.userId = userId; - this.devices = devices; - this.transactionId = transactionId; - this.deviceId = deviceId; - } - isToDevices(devices) { - if (devices.length === this.devices.length) { - for (const device of devices) { - if (!this.devices.includes(device)) { - return false; - } - } - return true; - } - else { - return false; - } - } - static getEventType(event) { - return event.getType(); - } - /** - * Extract the transaction id used by a given key verification event, if any - * @param event - the event - * @returns the transaction id - */ - static getTransactionId(event) { - const content = event.getContent(); - return content && content.transaction_id; - } - /** - * Checks whether the given event type should be allowed to initiate a new VerificationRequest over this channel - * @param type - the event type to check - * @returns boolean flag - */ - static canCreateRequest(type) { - return type === VerificationRequest_1.REQUEST_TYPE || type === VerificationRequest_1.START_TYPE; - } - canCreateRequest(type) { - return ToDeviceChannel.canCreateRequest(type); - } - /** - * Checks whether this event is a well-formed key verification event. - * This only does checks that don't rely on the current state of a potentially already channel - * so we can prevent channels being created by invalid events. - * `handleEvent` can do more checks and choose to ignore invalid events. - * @param event - the event to validate - * @param client - the client to get the current user and device id from - * @returns whether the event is valid and should be passed to handleEvent - */ - static validateEvent(event, client) { - if (event.isCancelled()) { - logger_1.logger.warn("Ignoring flagged verification request from " + event.getSender()); - return false; - } - const content = event.getContent(); - if (!content) { - logger_1.logger.warn("ToDeviceChannel.validateEvent: invalid: no content"); - return false; - } - if (!content.transaction_id) { - logger_1.logger.warn("ToDeviceChannel.validateEvent: invalid: no transaction_id"); - return false; - } - const type = event.getType(); - if (type === VerificationRequest_1.REQUEST_TYPE) { - if (!Number.isFinite(content.timestamp)) { - logger_1.logger.warn("ToDeviceChannel.validateEvent: invalid: no timestamp"); - return false; - } - if (event.getSender() === client.getUserId() && content.from_device == client.getDeviceId()) { - // ignore requests from ourselves, because it doesn't make sense for a - // device to verify itself - logger_1.logger.warn("ToDeviceChannel.validateEvent: invalid: from own device"); - return false; - } - } - return VerificationRequest_1.VerificationRequest.validateEvent(type, event, client); - } - /** - * @param event - the event to get the timestamp of - * @returns the timestamp when the event was sent - */ - getTimestamp(event) { - const content = event.getContent(); - return content && content.timestamp; - } - /** - * Changes the state of the channel, request, and verifier in response to a key verification event. - * @param event - to handle - * @param request - the request to forward handling to - * @param isLiveEvent - whether this is an even received through sync or not - * @returns a promise that resolves when any requests as an answer to the passed-in event are sent. - */ - handleEvent(event, request, isLiveEvent = false) { - return __awaiter(this, void 0, void 0, function* () { - const type = event.getType(); - const content = event.getContent(); - if (type === VerificationRequest_1.REQUEST_TYPE || type === VerificationRequest_1.READY_TYPE || type === VerificationRequest_1.START_TYPE) { - if (!this.transactionId) { - this.transactionId = content.transaction_id; - } - const deviceId = content.from_device; - // adopt deviceId if not set before and valid - if (!this.deviceId && this.devices.includes(deviceId)) { - this.deviceId = deviceId; - } - // if no device id or different from adopted one, cancel with sender - if (!this.deviceId || this.deviceId !== deviceId) { - // also check that message came from the device we sent the request to earlier on - // and do send a cancel message to that device - // (but don't cancel the request for the device we should be talking to) - const cancelContent = this.completeContent(VerificationRequest_1.CANCEL_TYPE, (0, Error_1.errorFromEvent)((0, Error_1.newUnexpectedMessageError)())); - return this.sendToDevices(VerificationRequest_1.CANCEL_TYPE, cancelContent, [deviceId]); - } - } - const wasStarted = request.phase === VerificationRequest_1.PHASE_STARTED || request.phase === VerificationRequest_1.PHASE_READY; - yield request.handleEvent(event.getType(), event, isLiveEvent, false, false); - const isStarted = request.phase === VerificationRequest_1.PHASE_STARTED || request.phase === VerificationRequest_1.PHASE_READY; - const isAcceptingEvent = type === VerificationRequest_1.START_TYPE || type === VerificationRequest_1.READY_TYPE; - // the request has picked a ready or start event, tell the other devices about it - if (isAcceptingEvent && !wasStarted && isStarted && this.deviceId) { - const nonChosenDevices = this.devices.filter((d) => d !== this.deviceId && d !== this.client.getDeviceId()); - if (nonChosenDevices.length) { - const message = this.completeContent(VerificationRequest_1.CANCEL_TYPE, { - code: "m.accepted", - reason: "Verification request accepted by another device", - }); - yield this.sendToDevices(VerificationRequest_1.CANCEL_TYPE, message, nonChosenDevices); - } - } - }); - } - /** - * See {@link InRoomChannel#completedContentFromEvent} for why this is needed. - * @param event - the received event - * @returns the content object - */ - completedContentFromEvent(event) { - return event.getContent(); - } - /** - * Add all the fields to content needed for sending it over this channel. - * This is public so verification methods (SAS uses this) can get the exact - * content that will be sent independent of the used channel, - * as they need to calculate the hash of it. - * @param type - the event type - * @param content - the (incomplete) content - * @returns the complete content, as it will be sent. - */ - completeContent(type, content) { - // make a copy - content = Object.assign({}, content); - if (this.transactionId) { - content.transaction_id = this.transactionId; - } - if (type === VerificationRequest_1.REQUEST_TYPE || type === VerificationRequest_1.READY_TYPE || type === VerificationRequest_1.START_TYPE) { - content.from_device = this.client.getDeviceId(); - } - if (type === VerificationRequest_1.REQUEST_TYPE) { - content.timestamp = Date.now(); - } - return content; - } - /** - * Send an event over the channel with the content not having gone through `completeContent`. - * @param type - the event type - * @param uncompletedContent - the (incomplete) content - * @returns the promise of the request - */ - send(type, uncompletedContent = {}) { - // create transaction id when sending request - if ((type === VerificationRequest_1.REQUEST_TYPE || type === VerificationRequest_1.START_TYPE) && !this.transactionId) { - this.transactionId = ToDeviceChannel.makeTransactionId(); - } - const content = this.completeContent(type, uncompletedContent); - return this.sendCompleted(type, content); - } - /** - * Send an event over the channel with the content having gone through `completeContent` already. - * @param type - the event type - * @returns the promise of the request - */ - sendCompleted(type, content) { - return __awaiter(this, void 0, void 0, function* () { - let result; - if (type === VerificationRequest_1.REQUEST_TYPE || (type === VerificationRequest_1.CANCEL_TYPE && !this.deviceId)) { - result = yield this.sendToDevices(type, content, this.devices); - } - else { - result = yield this.sendToDevices(type, content, [this.deviceId]); - } - // the VerificationRequest state machine requires remote echos of the event - // the client sends itself, so we fake this for to_device messages - const remoteEchoEvent = new event_1.MatrixEvent({ - sender: this.client.getUserId(), - content, - type, - }); - yield this.request.handleEvent(type, remoteEchoEvent, - /*isLiveEvent=*/ true, - /*isRemoteEcho=*/ true, - /*isSentByUs=*/ true); - return result; - }); - } - sendToDevices(type, content, devices) { - return __awaiter(this, void 0, void 0, function* () { - if (devices.length) { - const deviceMessages = new Map(); - for (const deviceId of devices) { - deviceMessages.set(deviceId, content); - } - yield this.client.sendToDevice(type, new Map([[this.userId, deviceMessages]])); - } - }); - } - /** - * Allow Crypto module to create and know the transaction id before the .start event gets sent. - * @returns the transaction id - */ - static makeTransactionId() { - return (0, randomstring_1.randomString)(32); - } -} -exports.ToDeviceChannel = ToDeviceChannel; -class ToDeviceRequests { - constructor() { - this.requestsByUserId = new Map(); - } - getRequest(event) { - return this.getRequestBySenderAndTxnId(event.getSender(), ToDeviceChannel.getTransactionId(event)); - } - getRequestByChannel(channel) { - return this.getRequestBySenderAndTxnId(channel.userId, channel.transactionId); - } - getRequestBySenderAndTxnId(sender, txnId) { - const requestsByTxnId = this.requestsByUserId.get(sender); - if (requestsByTxnId) { - return requestsByTxnId.get(txnId); - } - } - setRequest(event, request) { - this.setRequestBySenderAndTxnId(event.getSender(), ToDeviceChannel.getTransactionId(event), request); - } - setRequestByChannel(channel, request) { - this.setRequestBySenderAndTxnId(channel.userId, channel.transactionId, request); - } - setRequestBySenderAndTxnId(sender, txnId, request) { - let requestsByTxnId = this.requestsByUserId.get(sender); - if (!requestsByTxnId) { - requestsByTxnId = new Map(); - this.requestsByUserId.set(sender, requestsByTxnId); - } - requestsByTxnId.set(txnId, request); - } - removeRequest(event) { - const userId = event.getSender(); - const requestsByTxnId = this.requestsByUserId.get(userId); - if (requestsByTxnId) { - requestsByTxnId.delete(ToDeviceChannel.getTransactionId(event)); - if (requestsByTxnId.size === 0) { - this.requestsByUserId.delete(userId); - } - } - } - findRequestInProgress(userId, devices) { - const requestsByTxnId = this.requestsByUserId.get(userId); - if (requestsByTxnId) { - for (const request of requestsByTxnId.values()) { - if (request.pending && request.channel.isToDevices(devices)) { - return request; - } - } - } - } - getRequestsInProgress(userId) { - const requestsByTxnId = this.requestsByUserId.get(userId); - if (requestsByTxnId) { - return Array.from(requestsByTxnId.values()).filter((r) => r.pending); - } - return []; - } -} -exports.ToDeviceRequests = ToDeviceRequests; - -},{"../../../logger":374,"../../../models/event":383,"../../../randomstring":398,"../Error":350,"./VerificationRequest":357}],357:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VerificationRequest = exports.VerificationRequestEvent = exports.PHASE_DONE = exports.PHASE_CANCELLED = exports.PHASE_STARTED = exports.PHASE_READY = exports.PHASE_REQUESTED = exports.PHASE_UNSENT = exports.Phase = exports.READY_TYPE = exports.DONE_TYPE = exports.CANCEL_TYPE = exports.START_TYPE = exports.REQUEST_TYPE = exports.EVENT_PREFIX = void 0; -const logger_1 = require("../../../logger"); -const Error_1 = require("../Error"); -const QRCode_1 = require("../QRCode"); -const event_1 = require("../../../@types/event"); -const typed_event_emitter_1 = require("../../../models/typed-event-emitter"); -// How long after the event's timestamp that the request times out -const TIMEOUT_FROM_EVENT_TS = 10 * 60 * 1000; // 10 minutes -// How long after we receive the event that the request times out -const TIMEOUT_FROM_EVENT_RECEIPT = 2 * 60 * 1000; // 2 minutes -// to avoid almost expired verification notifications -// from showing a notification and almost immediately -// disappearing, also ignore verification requests that -// are this amount of time away from expiring. -const VERIFICATION_REQUEST_MARGIN = 3 * 1000; // 3 seconds -exports.EVENT_PREFIX = "m.key.verification."; -exports.REQUEST_TYPE = exports.EVENT_PREFIX + "request"; -exports.START_TYPE = exports.EVENT_PREFIX + "start"; -exports.CANCEL_TYPE = exports.EVENT_PREFIX + "cancel"; -exports.DONE_TYPE = exports.EVENT_PREFIX + "done"; -exports.READY_TYPE = exports.EVENT_PREFIX + "ready"; -var Phase; -(function (Phase) { - Phase[Phase["Unsent"] = 1] = "Unsent"; - Phase[Phase["Requested"] = 2] = "Requested"; - Phase[Phase["Ready"] = 3] = "Ready"; - Phase[Phase["Started"] = 4] = "Started"; - Phase[Phase["Cancelled"] = 5] = "Cancelled"; - Phase[Phase["Done"] = 6] = "Done"; -})(Phase = exports.Phase || (exports.Phase = {})); -// Legacy export fields -exports.PHASE_UNSENT = Phase.Unsent; -exports.PHASE_REQUESTED = Phase.Requested; -exports.PHASE_READY = Phase.Ready; -exports.PHASE_STARTED = Phase.Started; -exports.PHASE_CANCELLED = Phase.Cancelled; -exports.PHASE_DONE = Phase.Done; -var VerificationRequestEvent; -(function (VerificationRequestEvent) { - VerificationRequestEvent["Change"] = "change"; -})(VerificationRequestEvent = exports.VerificationRequestEvent || (exports.VerificationRequestEvent = {})); -/** - * State machine for verification requests. - * Things that differ based on what channel is used to - * send and receive verification events are put in `InRoomChannel` or `ToDeviceChannel`. - */ -class VerificationRequest extends typed_event_emitter_1.TypedEventEmitter { - constructor(channel, verificationMethods, client) { - super(); - this.channel = channel; - this.verificationMethods = verificationMethods; - this.client = client; - this.eventsByUs = new Map(); - this.eventsByThem = new Map(); - this._observeOnly = false; - this.timeoutTimer = null; - this._accepting = false; - this._declining = false; - this.verifierHasFinished = false; - this._cancelled = false; - this._chosenMethod = null; - // we keep a copy of the QR Code data (including other user master key) around - // for QR reciprocate verification, to protect against - // cross-signing identity reset between the .ready and .start event - // and signing the wrong key after .start - this._qrCodeData = null; - // The timestamp when we received the request event from the other side - this.requestReceivedAt = null; - this.commonMethods = []; - this.cancelOnTimeout = () => __awaiter(this, void 0, void 0, function* () { - try { - if (this.initiatedByMe) { - yield this.cancel({ - reason: "Other party didn't accept in time", - code: "m.timeout", - }); - } - else { - yield this.cancel({ - reason: "User didn't accept in time", - code: "m.timeout", - }); - } - } - catch (err) { - logger_1.logger.error("Error while cancelling verification request", err); - } - }); - this.channel.request = this; - this.setPhase(exports.PHASE_UNSENT, false); - } - /** - * Stateless validation logic not specific to the channel. - * Invoked by the same static method in either channel. - * @param type - the "symbolic" event type, as returned by the `getEventType` function on the channel. - * @param event - the event to validate. Don't call getType() on it but use the `type` parameter instead. - * @param client - the client to get the current user and device id from - * @returns whether the event is valid and should be passed to handleEvent - */ - static validateEvent(type, event, client) { - const content = event.getContent(); - if (!type || !type.startsWith(exports.EVENT_PREFIX)) { - return false; - } - // from here on we're fairly sure that this is supposed to be - // part of a verification request, so be noisy when rejecting something - if (!content) { - logger_1.logger.log("VerificationRequest: validateEvent: no content"); - return false; - } - if (type === exports.REQUEST_TYPE || type === exports.READY_TYPE) { - if (!Array.isArray(content.methods)) { - logger_1.logger.log("VerificationRequest: validateEvent: " + "fail because methods"); - return false; - } - } - if (type === exports.REQUEST_TYPE || type === exports.READY_TYPE || type === exports.START_TYPE) { - if (typeof content.from_device !== "string" || content.from_device.length === 0) { - logger_1.logger.log("VerificationRequest: validateEvent: " + "fail because from_device"); - return false; - } - } - return true; - } - get invalid() { - return this.phase === exports.PHASE_UNSENT; - } - /** returns whether the phase is PHASE_REQUESTED */ - get requested() { - return this.phase === exports.PHASE_REQUESTED; - } - /** returns whether the phase is PHASE_CANCELLED */ - get cancelled() { - return this.phase === exports.PHASE_CANCELLED; - } - /** returns whether the phase is PHASE_READY */ - get ready() { - return this.phase === exports.PHASE_READY; - } - /** returns whether the phase is PHASE_STARTED */ - get started() { - return this.phase === exports.PHASE_STARTED; - } - /** returns whether the phase is PHASE_DONE */ - get done() { - return this.phase === exports.PHASE_DONE; - } - /** once the phase is PHASE_STARTED (and !initiatedByMe) or PHASE_READY: common methods supported by both sides */ - get methods() { - return this.commonMethods; - } - /** the method picked in the .start event */ - get chosenMethod() { - return this._chosenMethod; - } - calculateEventTimeout(event) { - let effectiveExpiresAt = this.channel.getTimestamp(event) + TIMEOUT_FROM_EVENT_TS; - if (this.requestReceivedAt && !this.initiatedByMe && this.phase <= exports.PHASE_REQUESTED) { - const expiresAtByReceipt = this.requestReceivedAt + TIMEOUT_FROM_EVENT_RECEIPT; - effectiveExpiresAt = Math.min(effectiveExpiresAt, expiresAtByReceipt); - } - return Math.max(0, effectiveExpiresAt - Date.now()); - } - /** The current remaining amount of ms before the request should be automatically cancelled */ - get timeout() { - const requestEvent = this.getEventByEither(exports.REQUEST_TYPE); - if (requestEvent) { - return this.calculateEventTimeout(requestEvent); - } - return 0; - } - /** - * The key verification request event. - * @returns The request event, or falsey if not found. - */ - get requestEvent() { - return this.getEventByEither(exports.REQUEST_TYPE); - } - /** current phase of the request. Some properties might only be defined in a current phase. */ - get phase() { - return this._phase; - } - /** The verifier to do the actual verification, once the method has been established. Only defined when the `phase` is PHASE_STARTED. */ - get verifier() { - return this._verifier; - } - get canAccept() { - return this.phase < exports.PHASE_READY && !this._accepting && !this._declining; - } - get accepting() { - return this._accepting; - } - get declining() { - return this._declining; - } - /** whether this request has sent it's initial event and needs more events to complete */ - get pending() { - return !this.observeOnly && this._phase !== exports.PHASE_DONE && this._phase !== exports.PHASE_CANCELLED; - } - /** Only set after a .ready if the other party can scan a QR code */ - get qrCodeData() { - return this._qrCodeData; - } - /** Checks whether the other party supports a given verification method. - * This is useful when setting up the QR code UI, as it is somewhat asymmetrical: - * if the other party supports SCAN_QR, we should show a QR code in the UI, and vice versa. - * For methods that need to be supported by both ends, use the `methods` property. - * @param method - the method to check - * @param force - to check even if the phase is not ready or started yet, internal usage - * @returns whether or not the other party said the supported the method */ - otherPartySupportsMethod(method, force = false) { - if (!force && !this.ready && !this.started) { - return false; - } - const theirMethodEvent = this.eventsByThem.get(exports.REQUEST_TYPE) || this.eventsByThem.get(exports.READY_TYPE); - if (!theirMethodEvent) { - // if we started straight away with .start event, - // we are assuming that the other side will support the - // chosen method, so return true for that. - if (this.started && this.initiatedByMe) { - const myStartEvent = this.eventsByUs.get(exports.START_TYPE); - const content = myStartEvent && myStartEvent.getContent(); - const myStartMethod = content && content.method; - return method == myStartMethod; - } - return false; - } - const content = theirMethodEvent.getContent(); - if (!content) { - return false; - } - const { methods } = content; - if (!Array.isArray(methods)) { - return false; - } - return methods.includes(method); - } - /** Whether this request was initiated by the syncing user. - * For InRoomChannel, this is who sent the .request event. - * For ToDeviceChannel, this is who sent the .start event - */ - get initiatedByMe() { - // event created by us but no remote echo has been received yet - const noEventsYet = this.eventsByUs.size + this.eventsByThem.size === 0; - if (this._phase === exports.PHASE_UNSENT && noEventsYet) { - return true; - } - const hasMyRequest = this.eventsByUs.has(exports.REQUEST_TYPE); - const hasTheirRequest = this.eventsByThem.has(exports.REQUEST_TYPE); - if (hasMyRequest && !hasTheirRequest) { - return true; - } - if (!hasMyRequest && hasTheirRequest) { - return false; - } - const hasMyStart = this.eventsByUs.has(exports.START_TYPE); - const hasTheirStart = this.eventsByThem.has(exports.START_TYPE); - if (hasMyStart && !hasTheirStart) { - return true; - } - return false; - } - /** The id of the user that initiated the request */ - get requestingUserId() { - if (this.initiatedByMe) { - return this.client.getUserId(); - } - else { - return this.otherUserId; - } - } - /** The id of the user that (will) receive(d) the request */ - get receivingUserId() { - if (this.initiatedByMe) { - return this.otherUserId; - } - else { - return this.client.getUserId(); - } - } - /** The user id of the other party in this request */ - get otherUserId() { - return this.channel.userId; - } - get isSelfVerification() { - return this.client.getUserId() === this.otherUserId; - } - /** - * The id of the user that cancelled the request, - * only defined when phase is PHASE_CANCELLED - */ - get cancellingUserId() { - const myCancel = this.eventsByUs.get(exports.CANCEL_TYPE); - const theirCancel = this.eventsByThem.get(exports.CANCEL_TYPE); - if (myCancel && (!theirCancel || myCancel.getId() < theirCancel.getId())) { - return myCancel.getSender(); - } - if (theirCancel) { - return theirCancel.getSender(); - } - return undefined; - } - /** - * The cancellation code e.g m.user which is responsible for cancelling this verification - */ - get cancellationCode() { - const ev = this.getEventByEither(exports.CANCEL_TYPE); - return ev ? ev.getContent().code : null; - } - get observeOnly() { - return this._observeOnly; - } - /** - * Gets which device the verification should be started with - * given the events sent so far in the verification. This is the - * same algorithm used to determine which device to send the - * verification to when no specific device is specified. - * @returns The device information - */ - get targetDevice() { - const theirFirstEvent = this.eventsByThem.get(exports.REQUEST_TYPE) || - this.eventsByThem.get(exports.READY_TYPE) || - this.eventsByThem.get(exports.START_TYPE); - const theirFirstContent = theirFirstEvent === null || theirFirstEvent === void 0 ? void 0 : theirFirstEvent.getContent(); - const fromDevice = theirFirstContent === null || theirFirstContent === void 0 ? void 0 : theirFirstContent.from_device; - return { - userId: this.otherUserId, - deviceId: fromDevice, - }; - } - /* Start the key verification, creating a verifier and sending a .start event. - * If no previous events have been sent, pass in `targetDevice` to set who to direct this request to. - * @param method - the name of the verification method to use. - * @param targetDevice.userId the id of the user to direct this request to - * @param targetDevice.deviceId the id of the device to direct this request to - * @returns the verifier of the given method - */ - beginKeyVerification(method, targetDevice = null) { - // need to allow also when unsent in case of to_device - if (!this.observeOnly && !this._verifier) { - const validStartPhase = this.phase === exports.PHASE_REQUESTED || - this.phase === exports.PHASE_READY || - (this.phase === exports.PHASE_UNSENT && this.channel.canCreateRequest(exports.START_TYPE)); - if (validStartPhase) { - // when called on a request that was initiated with .request event - // check the method is supported by both sides - if (this.commonMethods.length && !this.commonMethods.includes(method)) { - throw (0, Error_1.newUnknownMethodError)(); - } - this._verifier = this.createVerifier(method, null, targetDevice); - if (!this._verifier) { - throw (0, Error_1.newUnknownMethodError)(); - } - this._chosenMethod = method; - } - } - return this._verifier; - } - /** - * sends the initial .request event. - * @returns resolves when the event has been sent. - */ - sendRequest() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.observeOnly && this._phase === exports.PHASE_UNSENT) { - const methods = [...this.verificationMethods.keys()]; - yield this.channel.send(exports.REQUEST_TYPE, { methods }); - } - }); - } - /** - * Cancels the request, sending a cancellation to the other party - * @param reason - the error reason to send the cancellation with - * @param code - the error code to send the cancellation with - * @returns resolves when the event has been sent. - */ - cancel({ reason = "User declined", code = "m.user" } = {}) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.observeOnly && this._phase !== exports.PHASE_CANCELLED) { - this._declining = true; - this.emit(VerificationRequestEvent.Change); - if (this._verifier) { - return this._verifier.cancel((0, Error_1.errorFactory)(code, reason)()); - } - else { - this._cancellingUserId = this.client.getUserId(); - yield this.channel.send(exports.CANCEL_TYPE, { code, reason }); - } - } - }); - } - /** - * Accepts the request, sending a .ready event to the other party - * @returns resolves when the event has been sent. - */ - accept() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.observeOnly && this.phase === exports.PHASE_REQUESTED && !this.initiatedByMe) { - const methods = [...this.verificationMethods.keys()]; - this._accepting = true; - this.emit(VerificationRequestEvent.Change); - yield this.channel.send(exports.READY_TYPE, { methods }); - } - }); - } - /** - * Can be used to listen for state changes until the callback returns true. - * @param fn - callback to evaluate whether the request is in the desired state. - * Takes the request as an argument. - * @returns that resolves once the callback returns true - * @throws Error when the request is cancelled - */ - waitFor(fn) { - return new Promise((resolve, reject) => { - const check = () => { - let handled = false; - if (fn(this)) { - resolve(this); - handled = true; - } - else if (this.cancelled) { - reject(new Error("cancelled")); - handled = true; - } - if (handled) { - this.off(VerificationRequestEvent.Change, check); - } - return handled; - }; - if (!check()) { - this.on(VerificationRequestEvent.Change, check); - } - }); - } - setPhase(phase, notify = true) { - this._phase = phase; - if (notify) { - this.emit(VerificationRequestEvent.Change); - } - } - getEventByEither(type) { - return this.eventsByThem.get(type) || this.eventsByUs.get(type); - } - getEventBy(type, byThem = false) { - if (byThem) { - return this.eventsByThem.get(type); - } - else { - return this.eventsByUs.get(type); - } - } - calculatePhaseTransitions() { - const transitions = [{ phase: exports.PHASE_UNSENT }]; - const phase = () => transitions[transitions.length - 1].phase; - // always pass by .request first to be sure channel.userId has been set - const hasRequestByThem = this.eventsByThem.has(exports.REQUEST_TYPE); - const requestEvent = this.getEventBy(exports.REQUEST_TYPE, hasRequestByThem); - if (requestEvent) { - transitions.push({ phase: exports.PHASE_REQUESTED, event: requestEvent }); - } - const readyEvent = requestEvent && this.getEventBy(exports.READY_TYPE, !hasRequestByThem); - if (readyEvent && phase() === exports.PHASE_REQUESTED) { - transitions.push({ phase: exports.PHASE_READY, event: readyEvent }); - } - let startEvent; - if (readyEvent || !requestEvent) { - const theirStartEvent = this.eventsByThem.get(exports.START_TYPE); - const ourStartEvent = this.eventsByUs.get(exports.START_TYPE); - // any party can send .start after a .ready or unsent - if (theirStartEvent && ourStartEvent) { - startEvent = - theirStartEvent.getSender() < ourStartEvent.getSender() ? theirStartEvent : ourStartEvent; - } - else { - startEvent = theirStartEvent ? theirStartEvent : ourStartEvent; - } - } - else { - startEvent = this.getEventBy(exports.START_TYPE, !hasRequestByThem); - } - if (startEvent) { - const fromRequestPhase = phase() === exports.PHASE_REQUESTED && (requestEvent === null || requestEvent === void 0 ? void 0 : requestEvent.getSender()) !== startEvent.getSender(); - const fromUnsentPhase = phase() === exports.PHASE_UNSENT && this.channel.canCreateRequest(exports.START_TYPE); - if (fromRequestPhase || phase() === exports.PHASE_READY || fromUnsentPhase) { - transitions.push({ phase: exports.PHASE_STARTED, event: startEvent }); - } - } - const ourDoneEvent = this.eventsByUs.get(exports.DONE_TYPE); - if (this.verifierHasFinished || (ourDoneEvent && phase() === exports.PHASE_STARTED)) { - transitions.push({ phase: exports.PHASE_DONE }); - } - const cancelEvent = this.getEventByEither(exports.CANCEL_TYPE); - if ((this._cancelled || cancelEvent) && phase() !== exports.PHASE_DONE) { - transitions.push({ phase: exports.PHASE_CANCELLED, event: cancelEvent }); - return transitions; - } - return transitions; - } - transitionToPhase(transition) { - const { phase, event } = transition; - // get common methods - if (phase === exports.PHASE_REQUESTED || phase === exports.PHASE_READY) { - if (!this.wasSentByOwnDevice(event)) { - const content = event.getContent(); - this.commonMethods = content.methods.filter((m) => this.verificationMethods.has(m)); - } - } - // detect if we're not a party in the request, and we should just observe - if (!this.observeOnly) { - // if requested or accepted by one of my other devices - if (phase === exports.PHASE_REQUESTED || phase === exports.PHASE_STARTED || phase === exports.PHASE_READY) { - if (this.channel.receiveStartFromOtherDevices && - this.wasSentByOwnUser(event) && - !this.wasSentByOwnDevice(event)) { - this._observeOnly = true; - } - } - } - // create verifier - if (phase === exports.PHASE_STARTED) { - const { method } = event.getContent(); - if (!this._verifier && !this.observeOnly) { - this._verifier = this.createVerifier(method, event); - if (!this._verifier) { - this.cancel({ - code: "m.unknown_method", - reason: `Unknown method: ${method}`, - }); - } - else { - this._chosenMethod = method; - } - } - } - } - applyPhaseTransitions() { - const transitions = this.calculatePhaseTransitions(); - const existingIdx = transitions.findIndex((t) => t.phase === this.phase); - // trim off phases we already went through, if any - const newTransitions = transitions.slice(existingIdx + 1); - // transition to all new phases - for (const transition of newTransitions) { - this.transitionToPhase(transition); - } - return newTransitions; - } - isWinningStartRace(newEvent) { - if (newEvent.getType() !== exports.START_TYPE) { - return false; - } - const oldEvent = this._verifier.startEvent; - let oldRaceIdentifier; - if (this.isSelfVerification) { - // if the verifier does not have a startEvent, - // it is because it's still sending and we are on the initator side - // we know we are sending a .start event because we already - // have a verifier (checked in calling method) - if (oldEvent) { - const oldContent = oldEvent.getContent(); - oldRaceIdentifier = oldContent && oldContent.from_device; - } - else { - oldRaceIdentifier = this.client.getDeviceId(); - } - } - else { - if (oldEvent) { - oldRaceIdentifier = oldEvent.getSender(); - } - else { - oldRaceIdentifier = this.client.getUserId(); - } - } - let newRaceIdentifier; - if (this.isSelfVerification) { - const newContent = newEvent.getContent(); - newRaceIdentifier = newContent && newContent.from_device; - } - else { - newRaceIdentifier = newEvent.getSender(); - } - return newRaceIdentifier < oldRaceIdentifier; - } - hasEventId(eventId) { - for (const event of this.eventsByUs.values()) { - if (event.getId() === eventId) { - return true; - } - } - for (const event of this.eventsByThem.values()) { - if (event.getId() === eventId) { - return true; - } - } - return false; - } - /** - * Changes the state of the request and verifier in response to a key verification event. - * @param type - the "symbolic" event type, as returned by the `getEventType` function on the channel. - * @param event - the event to handle. Don't call getType() on it but use the `type` parameter instead. - * @param isLiveEvent - whether this is an even received through sync or not - * @param isRemoteEcho - whether this is the remote echo of an event sent by the same device - * @param isSentByUs - whether this event is sent by a party that can accept and/or observe the request like one of our peers. - * For InRoomChannel this means any device for the syncing user. For ToDeviceChannel, just the syncing device. - * @returns a promise that resolves when any requests as an answer to the passed-in event are sent. - */ - handleEvent(type, event, isLiveEvent, isRemoteEcho, isSentByUs) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // if reached phase cancelled or done, ignore anything else that comes - if (this.done || this.cancelled) { - return; - } - const wasObserveOnly = this._observeOnly; - this.adjustObserveOnly(event, isLiveEvent); - if (!this.observeOnly && !isRemoteEcho) { - if (yield this.cancelOnError(type, event)) { - return; - } - } - // This assumes verification won't need to send an event with - // the same type for the same party twice. - // This is true for QR and SAS verification, and was - // added here to prevent verification getting cancelled - // when the server duplicates an event (https://github.com/matrix-org/synapse/issues/3365) - const isDuplicateEvent = isSentByUs ? this.eventsByUs.has(type) : this.eventsByThem.has(type); - if (isDuplicateEvent) { - return; - } - const oldPhase = this.phase; - this.addEvent(type, event, isSentByUs); - // this will create if needed the verifier so needs to happen before calling it - const newTransitions = this.applyPhaseTransitions(); - try { - // only pass events from the other side to the verifier, - // no remote echos of our own events - if (this._verifier && !this.observeOnly) { - const newEventWinsRace = this.isWinningStartRace(event); - if (this._verifier.canSwitchStartEvent(event) && newEventWinsRace) { - this._verifier.switchStartEvent(event); - } - else if (!isRemoteEcho) { - if (type === exports.CANCEL_TYPE || ((_a = this._verifier.events) === null || _a === void 0 ? void 0 : _a.includes(type))) { - this._verifier.handleEvent(event); - } - } - } - if (newTransitions.length) { - // create QRCodeData if the other side can scan - // important this happens before emitting a phase change, - // so listeners can rely on it being there already - // We only do this for live events because it is important that - // we sign the keys that were in the QR code, and not the keys - // we happen to have at some later point in time. - if (isLiveEvent && newTransitions.some((t) => t.phase === exports.PHASE_READY)) { - const shouldGenerateQrCode = this.otherPartySupportsMethod(QRCode_1.SCAN_QR_CODE_METHOD, true); - if (shouldGenerateQrCode) { - this._qrCodeData = yield QRCode_1.QRCodeData.create(this, this.client); - } - } - const lastTransition = newTransitions[newTransitions.length - 1]; - const { phase } = lastTransition; - this.setupTimeout(phase); - // set phase as last thing as this emits the "change" event - this.setPhase(phase); - } - else if (this._observeOnly !== wasObserveOnly) { - this.emit(VerificationRequestEvent.Change); - } - } - finally { - // log events we processed so we can see from rageshakes what events were added to a request - logger_1.logger.log(`Verification request ${this.channel.transactionId}: ` + - `${type} event with id:${event.getId()}, ` + - `content:${JSON.stringify(event.getContent())} ` + - `deviceId:${this.channel.deviceId}, ` + - `sender:${event.getSender()}, isSentByUs:${isSentByUs}, ` + - `isLiveEvent:${isLiveEvent}, isRemoteEcho:${isRemoteEcho}, ` + - `phase:${oldPhase}=>${this.phase}, ` + - `observeOnly:${wasObserveOnly}=>${this._observeOnly}`); - } - }); - } - setupTimeout(phase) { - const shouldTimeout = !this.timeoutTimer && !this.observeOnly && phase === exports.PHASE_REQUESTED; - if (shouldTimeout) { - this.timeoutTimer = setTimeout(this.cancelOnTimeout, this.timeout); - } - if (this.timeoutTimer) { - const shouldClear = phase === exports.PHASE_STARTED || phase === exports.PHASE_READY || phase === exports.PHASE_DONE || phase === exports.PHASE_CANCELLED; - if (shouldClear) { - clearTimeout(this.timeoutTimer); - this.timeoutTimer = null; - } - } - } - cancelOnError(type, event) { - return __awaiter(this, void 0, void 0, function* () { - if (type === exports.START_TYPE) { - const method = event.getContent().method; - if (!this.verificationMethods.has(method)) { - yield this.cancel((0, Error_1.errorFromEvent)((0, Error_1.newUnknownMethodError)())); - return true; - } - } - const isUnexpectedRequest = type === exports.REQUEST_TYPE && this.phase !== exports.PHASE_UNSENT; - const isUnexpectedReady = type === exports.READY_TYPE && this.phase !== exports.PHASE_REQUESTED && this.phase !== exports.PHASE_STARTED; - // only if phase has passed from PHASE_UNSENT should we cancel, because events - // are allowed to come in in any order (at least with InRoomChannel). So we only know - // we're dealing with a valid request we should participate in once we've moved to PHASE_REQUESTED. - // Before that, we could be looking at somebody else's verification request and we just - // happen to be in the room - if (this.phase !== exports.PHASE_UNSENT && (isUnexpectedRequest || isUnexpectedReady)) { - logger_1.logger.warn(`Cancelling, unexpected ${type} verification ` + `event from ${event.getSender()}`); - const reason = `Unexpected ${type} event in phase ${this.phase}`; - yield this.cancel((0, Error_1.errorFromEvent)((0, Error_1.newUnexpectedMessageError)({ reason }))); - return true; - } - return false; - }); - } - adjustObserveOnly(event, isLiveEvent = false) { - // don't send out events for historical requests - if (!isLiveEvent) { - this._observeOnly = true; - } - if (this.calculateEventTimeout(event) < VERIFICATION_REQUEST_MARGIN) { - this._observeOnly = true; - } - } - addEvent(type, event, isSentByUs = false) { - if (isSentByUs) { - this.eventsByUs.set(type, event); - } - else { - this.eventsByThem.set(type, event); - } - // once we know the userId of the other party (from the .request event) - // see if any event by anyone else crept into this.eventsByThem - if (type === exports.REQUEST_TYPE) { - for (const [type, event] of this.eventsByThem.entries()) { - if (event.getSender() !== this.otherUserId) { - this.eventsByThem.delete(type); - } - } - // also remember when we received the request event - this.requestReceivedAt = Date.now(); - } - } - createVerifier(method, startEvent = null, targetDevice = null) { - if (!targetDevice) { - targetDevice = this.targetDevice; - } - const { userId, deviceId } = targetDevice; - const VerifierCtor = this.verificationMethods.get(method); - if (!VerifierCtor) { - logger_1.logger.warn("could not find verifier constructor for method", method); - return; - } - return new VerifierCtor(this.channel, this.client, userId, deviceId, startEvent, this); - } - wasSentByOwnUser(event) { - return (event === null || event === void 0 ? void 0 : event.getSender()) === this.client.getUserId(); - } - // only for .request, .ready or .start - wasSentByOwnDevice(event) { - if (!this.wasSentByOwnUser(event)) { - return false; - } - const content = event.getContent(); - if (!content || content.from_device !== this.client.getDeviceId()) { - return false; - } - return true; - } - onVerifierCancelled() { - this._cancelled = true; - // move to cancelled phase - const newTransitions = this.applyPhaseTransitions(); - if (newTransitions.length) { - this.setPhase(newTransitions[newTransitions.length - 1].phase); - } - } - onVerifierFinished() { - this.channel.send(event_1.EventType.KeyVerificationDone, {}); - this.verifierHasFinished = true; - // move to .done phase - const newTransitions = this.applyPhaseTransitions(); - if (newTransitions.length) { - this.setPhase(newTransitions[newTransitions.length - 1].phase); - } - } - getEventFromOtherParty(type) { - return this.eventsByThem.get(type); - } -} -exports.VerificationRequest = VerificationRequest; - -},{"../../../@types/event":306,"../../../logger":374,"../../../models/typed-event-emitter":395,"../Error":350,"../QRCode":352}],358:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoomWidgetClient = void 0; -const matrix_widget_api_1 = require("matrix-widget-api"); -const event_1 = require("./models/event"); -const event_2 = require("./@types/event"); -const logger_1 = require("./logger"); -const client_1 = require("./client"); -const sync_1 = require("./sync"); -const sliding_sync_sdk_1 = require("./sliding-sync-sdk"); -const event_3 = require("./models/event"); -const user_1 = require("./models/user"); -const utils_1 = require("./utils"); -/** - * A MatrixClient that routes its requests through the widget API instead of the - * real CS API. - * @experimental This class is considered unstable! - */ -class RoomWidgetClient extends client_1.MatrixClient { - constructor(widgetApi, capabilities, roomId, opts) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; - super(opts); - this.widgetApi = widgetApi; - this.capabilities = capabilities; - this.roomId = roomId; - this.widgetApiReady = new Promise((resolve) => this.widgetApi.once("ready", resolve)); - this.syncState = null; - this.onEvent = (ev) => __awaiter(this, void 0, void 0, function* () { - ev.preventDefault(); - // Verify the room ID matches, since it's possible for the client to - // send us events from other rooms if this widget is always on screen - if (ev.detail.data.room_id === this.roomId) { - const event = new event_3.MatrixEvent(ev.detail.data); - yield this.syncApi.injectRoomEvents(this.room, [], [event]); - this.emit(client_1.ClientEvent.Event, event); - this.setSyncState(sync_1.SyncState.Syncing); - logger_1.logger.info(`Received event ${event.getId()} ${event.getType()} ${event.getStateKey()}`); - } - else { - const { event_id: eventId, room_id: roomId } = ev.detail.data; - logger_1.logger.info(`Received event ${eventId} for a different room ${roomId}; discarding`); - } - yield this.ack(ev); - }); - this.onToDevice = (ev) => __awaiter(this, void 0, void 0, function* () { - ev.preventDefault(); - const event = new event_3.MatrixEvent({ - type: ev.detail.data.type, - sender: ev.detail.data.sender, - content: ev.detail.data.content, - }); - // Mark the event as encrypted if it was, using fake contents and keys since those are unknown to us - if (ev.detail.data.encrypted) - event.makeEncrypted(event_2.EventType.RoomMessageEncrypted, {}, "", ""); - this.emit(client_1.ClientEvent.ToDeviceEvent, event); - this.setSyncState(sync_1.SyncState.Syncing); - yield this.ack(ev); - }); - // Request capabilities for the functionality this client needs to support - if (((_a = capabilities.sendEvent) === null || _a === void 0 ? void 0 : _a.length) || - ((_b = capabilities.receiveEvent) === null || _b === void 0 ? void 0 : _b.length) || - capabilities.sendMessage === true || - (Array.isArray(capabilities.sendMessage) && capabilities.sendMessage.length) || - capabilities.receiveMessage === true || - (Array.isArray(capabilities.receiveMessage) && capabilities.receiveMessage.length) || - ((_c = capabilities.sendState) === null || _c === void 0 ? void 0 : _c.length) || - ((_d = capabilities.receiveState) === null || _d === void 0 ? void 0 : _d.length)) { - widgetApi.requestCapabilityForRoomTimeline(roomId); - } - (_e = capabilities.sendEvent) === null || _e === void 0 ? void 0 : _e.forEach((eventType) => widgetApi.requestCapabilityToSendEvent(eventType)); - (_f = capabilities.receiveEvent) === null || _f === void 0 ? void 0 : _f.forEach((eventType) => widgetApi.requestCapabilityToReceiveEvent(eventType)); - if (capabilities.sendMessage === true) { - widgetApi.requestCapabilityToSendMessage(); - } - else if (Array.isArray(capabilities.sendMessage)) { - capabilities.sendMessage.forEach((msgType) => widgetApi.requestCapabilityToSendMessage(msgType)); - } - if (capabilities.receiveMessage === true) { - widgetApi.requestCapabilityToReceiveMessage(); - } - else if (Array.isArray(capabilities.receiveMessage)) { - capabilities.receiveMessage.forEach((msgType) => widgetApi.requestCapabilityToReceiveMessage(msgType)); - } - (_g = capabilities.sendState) === null || _g === void 0 ? void 0 : _g.forEach(({ eventType, stateKey }) => widgetApi.requestCapabilityToSendState(eventType, stateKey)); - (_h = capabilities.receiveState) === null || _h === void 0 ? void 0 : _h.forEach(({ eventType, stateKey }) => widgetApi.requestCapabilityToReceiveState(eventType, stateKey)); - (_j = capabilities.sendToDevice) === null || _j === void 0 ? void 0 : _j.forEach((eventType) => widgetApi.requestCapabilityToSendToDevice(eventType)); - (_k = capabilities.receiveToDevice) === null || _k === void 0 ? void 0 : _k.forEach((eventType) => widgetApi.requestCapabilityToReceiveToDevice(eventType)); - if (capabilities.turnServers) { - widgetApi.requestCapability(matrix_widget_api_1.MatrixCapabilities.MSC3846TurnServers); - } - widgetApi.on(`action:${matrix_widget_api_1.WidgetApiToWidgetAction.SendEvent}`, this.onEvent); - widgetApi.on(`action:${matrix_widget_api_1.WidgetApiToWidgetAction.SendToDevice}`, this.onToDevice); - // Open communication with the host - widgetApi.start(); - } - startClient(opts = {}) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - this.lifecycle = new AbortController(); - // Create our own user object artificially (instead of waiting for sync) - // so it's always available, even if the user is not in any rooms etc. - const userId = this.getUserId(); - if (userId) { - this.store.storeUser(new user_1.User(userId)); - } - // Even though we have no access token and cannot sync, the sync class - // still has some valuable helper methods that we make use of, so we - // instantiate it anyways - if (opts.slidingSync) { - this.syncApi = new sliding_sync_sdk_1.SlidingSyncSdk(opts.slidingSync, this, opts, this.buildSyncApiOptions()); - } - else { - this.syncApi = new sync_1.SyncApi(this, opts, this.buildSyncApiOptions()); - } - this.room = this.syncApi.createRoom(this.roomId); - this.store.storeRoom(this.room); - yield this.widgetApiReady; - // Backfill the requested events - // We only get the most recent event for every type + state key combo, - // so it doesn't really matter what order we inject them in - yield Promise.all((_b = (_a = this.capabilities.receiveState) === null || _a === void 0 ? void 0 : _a.map(({ eventType, stateKey }) => __awaiter(this, void 0, void 0, function* () { - const rawEvents = yield this.widgetApi.readStateEvents(eventType, undefined, stateKey, [this.roomId]); - const events = rawEvents.map((rawEvent) => new event_3.MatrixEvent(rawEvent)); - yield this.syncApi.injectRoomEvents(this.room, [], events); - events.forEach((event) => { - this.emit(client_1.ClientEvent.Event, event); - logger_1.logger.info(`Backfilled event ${event.getId()} ${event.getType()} ${event.getStateKey()}`); - }); - }))) !== null && _b !== void 0 ? _b : []); - this.setSyncState(sync_1.SyncState.Syncing); - logger_1.logger.info("Finished backfilling events"); - // Watch for TURN servers, if requested - if (this.capabilities.turnServers) - this.watchTurnServers(); - }); - } - stopClient() { - this.widgetApi.off(`action:${matrix_widget_api_1.WidgetApiToWidgetAction.SendEvent}`, this.onEvent); - this.widgetApi.off(`action:${matrix_widget_api_1.WidgetApiToWidgetAction.SendToDevice}`, this.onToDevice); - super.stopClient(); - this.lifecycle.abort(); // Signal to other async tasks that the client has stopped - } - joinRoom(roomIdOrAlias) { - return __awaiter(this, void 0, void 0, function* () { - if (roomIdOrAlias === this.roomId) - return this.room; - throw new Error(`Unknown room: ${roomIdOrAlias}`); - }); - } - encryptAndSendEvent(room, event) { - return __awaiter(this, void 0, void 0, function* () { - let response; - try { - response = yield this.widgetApi.sendRoomEvent(event.getType(), event.getContent(), room.roomId); - } - catch (e) { - this.updatePendingEventStatus(room, event, event_1.EventStatus.NOT_SENT); - throw e; - } - room.updatePendingEvent(event, event_1.EventStatus.SENT, response.event_id); - return { event_id: response.event_id }; - }); - } - sendStateEvent(roomId, eventType, content, stateKey = "") { - return __awaiter(this, void 0, void 0, function* () { - return yield this.widgetApi.sendStateEvent(eventType, stateKey, content, roomId); - }); - } - sendToDevice(eventType, contentMap) { - return __awaiter(this, void 0, void 0, function* () { - yield this.widgetApi.sendToDevice(eventType, false, (0, utils_1.recursiveMapToObject)(contentMap)); - return {}; - }); - } - queueToDevice({ eventType, batch }) { - return __awaiter(this, void 0, void 0, function* () { - // map: user Id → device Id → payload - const contentMap = new utils_1.MapWithDefault(() => new Map()); - for (const { userId, deviceId, payload } of batch) { - contentMap.getOrCreate(userId).set(deviceId, payload); - } - yield this.widgetApi.sendToDevice(eventType, false, (0, utils_1.recursiveMapToObject)(contentMap)); - }); - } - encryptAndSendToDevices(userDeviceInfoArr, payload) { - return __awaiter(this, void 0, void 0, function* () { - // map: user Id → device Id → payload - const contentMap = new utils_1.MapWithDefault(() => new Map()); - for (const { userId, deviceInfo: { deviceId }, } of userDeviceInfoArr) { - contentMap.getOrCreate(userId).set(deviceId, payload); - } - yield this.widgetApi.sendToDevice(payload.type, true, (0, utils_1.recursiveMapToObject)(contentMap)); - }); - } - // Overridden since we get TURN servers automatically over the widget API, - // and this method would otherwise complain about missing an access token - checkTurnServers() { - return __awaiter(this, void 0, void 0, function* () { - return this.turnServers.length > 0; - }); - } - // Overridden since we 'sync' manually without the sync API - getSyncState() { - return this.syncState; - } - setSyncState(state) { - const oldState = this.syncState; - this.syncState = state; - this.emit(client_1.ClientEvent.Sync, state, oldState); - } - ack(ev) { - return __awaiter(this, void 0, void 0, function* () { - yield this.widgetApi.transport.reply(ev.detail, {}); - }); - } - watchTurnServers() { - var _a, e_1, _b, _c; - return __awaiter(this, void 0, void 0, function* () { - const servers = this.widgetApi.getTurnServers(); - const onClientStopped = () => { - servers.return(undefined); - }; - this.lifecycle.signal.addEventListener("abort", onClientStopped); - try { - try { - for (var _d = true, servers_1 = __asyncValues(servers), servers_1_1; servers_1_1 = yield servers_1.next(), _a = servers_1_1.done, !_a;) { - _c = servers_1_1.value; - _d = false; - try { - const server = _c; - this.turnServers = [ - { - urls: server.uris, - username: server.username, - credential: server.password, - }, - ]; - this.emit(client_1.ClientEvent.TurnServers, this.turnServers); - logger_1.logger.log(`Received TURN server: ${server.uris}`); - } - finally { - _d = true; - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (!_d && !_a && (_b = servers_1.return)) yield _b.call(servers_1); - } - finally { if (e_1) throw e_1.error; } - } - } - catch (e) { - logger_1.logger.warn("Error watching TURN servers", e); - } - finally { - this.lifecycle.signal.removeEventListener("abort", onClientStopped); - } - }); - } -} -exports.RoomWidgetClient = RoomWidgetClient; - -},{"./@types/event":306,"./client":321,"./logger":374,"./models/event":383,"./models/user":396,"./sliding-sync-sdk":406,"./sync":414,"./utils":416,"matrix-widget-api":178}],359:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.KeySignatureUploadError = exports.InvalidCryptoStoreError = exports.InvalidCryptoStoreState = exports.InvalidStoreError = exports.InvalidStoreState = void 0; -var InvalidStoreState; -(function (InvalidStoreState) { - InvalidStoreState[InvalidStoreState["ToggledLazyLoading"] = 0] = "ToggledLazyLoading"; -})(InvalidStoreState = exports.InvalidStoreState || (exports.InvalidStoreState = {})); -class InvalidStoreError extends Error { - constructor(reason, value) { - const message = `Store is invalid because ${reason}, ` + - `please stop the client, delete all data and start the client again`; - super(message); - this.reason = reason; - this.value = value; - this.name = "InvalidStoreError"; - } -} -exports.InvalidStoreError = InvalidStoreError; -InvalidStoreError.TOGGLED_LAZY_LOADING = InvalidStoreState.ToggledLazyLoading; -var InvalidCryptoStoreState; -(function (InvalidCryptoStoreState) { - InvalidCryptoStoreState["TooNew"] = "TOO_NEW"; -})(InvalidCryptoStoreState = exports.InvalidCryptoStoreState || (exports.InvalidCryptoStoreState = {})); -class InvalidCryptoStoreError extends Error { - constructor(reason) { - const message = `Crypto store is invalid because ${reason}, ` + - `please stop the client, delete all data and start the client again`; - super(message); - this.reason = reason; - this.name = "InvalidCryptoStoreError"; - } -} -exports.InvalidCryptoStoreError = InvalidCryptoStoreError; -InvalidCryptoStoreError.TOO_NEW = InvalidCryptoStoreState.TooNew; -class KeySignatureUploadError extends Error { - constructor(message, value) { - super(message); - this.value = value; - } -} -exports.KeySignatureUploadError = KeySignatureUploadError; - -},{}],360:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.eventMapperFor = void 0; -const event_1 = require("./models/event"); -const event_2 = require("./@types/event"); -function eventMapperFor(client, options) { - let preventReEmit = Boolean(options.preventReEmit); - const decrypt = options.decrypt !== false; - function mapper(plainOldJsObject) { - if (options.toDevice) { - delete plainOldJsObject.room_id; - } - const room = client.getRoom(plainOldJsObject.room_id); - let event; - // If the event is already known to the room, let's re-use the model rather than duplicating. - // We avoid doing this to state events as they may be forward or backwards looking which tweaks behaviour. - if (room && plainOldJsObject.state_key === undefined) { - event = room.findEventById(plainOldJsObject.event_id); - } - if (!event || event.status) { - event = new event_1.MatrixEvent(plainOldJsObject); - } - else { - // merge the latest unsigned data from the server - event.setUnsigned(Object.assign(Object.assign({}, event.getUnsigned()), plainOldJsObject.unsigned)); - // prevent doubling up re-emitters - preventReEmit = true; - } - // if there is a complete edit bundled alongside the event, perform the replacement. - // (prior to MSC3925, events were automatically replaced on the server-side. MSC3925 proposes that that doesn't - // happen automatically but the server does provide us with the whole content of the edit event.) - const bundledEdit = event.getServerAggregatedRelation(event_2.RelationType.Replace); - if (bundledEdit === null || bundledEdit === void 0 ? void 0 : bundledEdit.content) { - const replacement = mapper(bundledEdit); - // XXX: it's worth noting that the spec says we should only respect encrypted edits if, once decrypted, the - // replacement has a `m.new_content` property. The problem is that we haven't yet decrypted the replacement - // (it should be happening in the background), so we can't enforce this. Possibly we should for decryption - // to complete, but that sounds a bit racy. For now, we just assume it's ok. - event.makeReplaced(replacement); - } - const thread = room === null || room === void 0 ? void 0 : room.findThreadForEvent(event); - if (thread) { - event.setThread(thread); - } - // TODO: once we get rid of the old libolm-backed crypto, we can restrict this to room events (rather than - // to-device events), because the rust implementation decrypts to-device messages at a higher level. - // Generally we probably want to use a different eventMapper implementation for to-device events because - if (event.isEncrypted()) { - if (!preventReEmit) { - client.reEmitter.reEmit(event, [event_1.MatrixEventEvent.Decrypted]); - } - if (decrypt) { - client.decryptEventIfNeeded(event); - } - } - if (!preventReEmit) { - client.reEmitter.reEmit(event, [event_1.MatrixEventEvent.Replaced, event_1.MatrixEventEvent.VisibilityChange]); - room === null || room === void 0 ? void 0 : room.reEmitter.reEmit(event, [event_1.MatrixEventEvent.BeforeRedaction]); - } - return event; - } - return mapper; -} -exports.eventMapperFor = eventMapperFor; - -},{"./@types/event":306,"./models/event":383}],361:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isOptionalAString = exports.isProvided = void 0; -/** - * Determines if the given optional was provided a value. - * @param s - The optional to test. - * @returns True if the value is defined. - */ -function isProvided(s) { - return s !== null && s !== undefined; -} -exports.isProvided = isProvided; -/** - * Determines if the given optional string is a defined string. - * @param s - The input string. - * @returns True if the input is a defined string. - */ -function isOptionalAString(s) { - return isProvided(s) && typeof s === "string"; -} -exports.isOptionalAString = isOptionalAString; - -},{}],362:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.buildFeatureSupportMap = exports.Feature = exports.ServerSupport = void 0; -var ServerSupport; -(function (ServerSupport) { - ServerSupport[ServerSupport["Stable"] = 0] = "Stable"; - ServerSupport[ServerSupport["Unstable"] = 1] = "Unstable"; - ServerSupport[ServerSupport["Unsupported"] = 2] = "Unsupported"; -})(ServerSupport = exports.ServerSupport || (exports.ServerSupport = {})); -var Feature; -(function (Feature) { - Feature["Thread"] = "Thread"; - Feature["ThreadUnreadNotifications"] = "ThreadUnreadNotifications"; - Feature["LoginTokenRequest"] = "LoginTokenRequest"; - Feature["RelationBasedRedactions"] = "RelationBasedRedactions"; - Feature["AccountDataDeletion"] = "AccountDataDeletion"; -})(Feature = exports.Feature || (exports.Feature = {})); -const featureSupportResolver = { - [Feature.Thread]: { - unstablePrefixes: ["org.matrix.msc3440"], - matrixVersion: "v1.3", - }, - [Feature.ThreadUnreadNotifications]: { - unstablePrefixes: ["org.matrix.msc3771", "org.matrix.msc3773"], - matrixVersion: "v1.4", - }, - [Feature.LoginTokenRequest]: { - unstablePrefixes: ["org.matrix.msc3882"], - }, - [Feature.RelationBasedRedactions]: { - unstablePrefixes: ["org.matrix.msc3912"], - }, - [Feature.AccountDataDeletion]: { - unstablePrefixes: ["org.matrix.msc3391"], - }, -}; -function buildFeatureSupportMap(versions) { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - const supportMap = new Map(); - for (const [feature, supportCondition] of Object.entries(featureSupportResolver)) { - const supportMatrixVersion = (_b = (_a = versions.versions) === null || _a === void 0 ? void 0 : _a.includes(supportCondition.matrixVersion || "")) !== null && _b !== void 0 ? _b : false; - const supportUnstablePrefixes = (_d = (_c = supportCondition.unstablePrefixes) === null || _c === void 0 ? void 0 : _c.every((unstablePrefix) => { - var _a; - return ((_a = versions.unstable_features) === null || _a === void 0 ? void 0 : _a[unstablePrefix]) === true; - })) !== null && _d !== void 0 ? _d : false; - if (supportMatrixVersion) { - supportMap.set(feature, ServerSupport.Stable); - } - else if (supportUnstablePrefixes) { - supportMap.set(feature, ServerSupport.Unstable); - } - else { - supportMap.set(feature, ServerSupport.Unsupported); - } - } - return supportMap; - }); -} -exports.buildFeatureSupportMap = buildFeatureSupportMap; - -},{}],363:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FilterComponent = void 0; -const thread_1 = require("./models/thread"); -/** - * Checks if a value matches a given field value, which may be a * terminated - * wildcard pattern. - * @param actualValue - The value to be compared - * @param filterValue - The filter pattern to be compared - * @returns true if the actualValue matches the filterValue - */ -function matchesWildcard(actualValue, filterValue) { - if (filterValue.endsWith("*")) { - const typePrefix = filterValue.slice(0, -1); - return actualValue.slice(0, typePrefix.length) === typePrefix; - } - else { - return actualValue === filterValue; - } -} -/* eslint-enable camelcase */ -/** - * FilterComponent is a section of a Filter definition which defines the - * types, rooms, senders filters etc to be applied to a particular type of resource. - * This is all ported over from synapse's Filter object. - * - * N.B. that synapse refers to these as 'Filters', and what js-sdk refers to as - * 'Filters' are referred to as 'FilterCollections'. - */ -class FilterComponent { - constructor(filterJson, userId) { - this.filterJson = filterJson; - this.userId = userId; - } - /** - * Checks with the filter component matches the given event - * @param event - event to be checked against the filter - * @returns true if the event matches the filter - */ - check(event) { - var _a, _b; - const bundledRelationships = ((_a = event.getUnsigned()) === null || _a === void 0 ? void 0 : _a["m.relations"]) || {}; - const relations = Object.keys(bundledRelationships); - // Relation senders allows in theory a look-up of any senders - // however clients can only know about the current user participation status - // as sending a whole list of participants could be proven problematic in terms - // of performance - // This should be improved when bundled relationships solve that problem - const relationSenders = []; - if (this.userId && ((_b = bundledRelationships === null || bundledRelationships === void 0 ? void 0 : bundledRelationships[thread_1.THREAD_RELATION_TYPE.name]) === null || _b === void 0 ? void 0 : _b.current_user_participated)) { - relationSenders.push(this.userId); - } - return this.checkFields(event.getRoomId(), event.getSender(), event.getType(), event.getContent() ? event.getContent().url !== undefined : false, relations, relationSenders); - } - /** - * Converts the filter component into the form expected over the wire - */ - toJSON() { - return { - types: this.filterJson.types || null, - not_types: this.filterJson.not_types || [], - rooms: this.filterJson.rooms || null, - not_rooms: this.filterJson.not_rooms || [], - senders: this.filterJson.senders || null, - not_senders: this.filterJson.not_senders || [], - contains_url: this.filterJson.contains_url || null, - [thread_1.FILTER_RELATED_BY_SENDERS.name]: this.filterJson[thread_1.FILTER_RELATED_BY_SENDERS.name] || [], - [thread_1.FILTER_RELATED_BY_REL_TYPES.name]: this.filterJson[thread_1.FILTER_RELATED_BY_REL_TYPES.name] || [], - }; - } - /** - * Checks whether the filter component matches the given event fields. - * @param roomId - the roomId for the event being checked - * @param sender - the sender of the event being checked - * @param eventType - the type of the event being checked - * @param containsUrl - whether the event contains a content.url field - * @param relationTypes - whether has aggregated relation of the given type - * @param relationSenders - whether one of the relation is sent by the user listed - * @returns true if the event fields match the filter - */ - checkFields(roomId, sender, eventType, containsUrl, relationTypes, relationSenders) { - const literalKeys = { - rooms: function (v) { - return roomId === v; - }, - senders: function (v) { - return sender === v; - }, - types: function (v) { - return matchesWildcard(eventType, v); - }, - }; - for (const name in literalKeys) { - const matchFunc = literalKeys[name]; - const notName = "not_" + name; - const disallowedValues = this.filterJson[notName]; - if (disallowedValues === null || disallowedValues === void 0 ? void 0 : disallowedValues.some(matchFunc)) { - return false; - } - const allowedValues = this.filterJson[name]; - if (allowedValues && !allowedValues.some(matchFunc)) { - return false; - } - } - const containsUrlFilter = this.filterJson.contains_url; - if (containsUrlFilter !== undefined && containsUrlFilter !== containsUrl) { - return false; - } - const relationTypesFilter = this.filterJson[thread_1.FILTER_RELATED_BY_REL_TYPES.name]; - if (relationTypesFilter !== undefined) { - if (!this.arrayMatchesFilter(relationTypesFilter, relationTypes)) { - return false; - } - } - const relationSendersFilter = this.filterJson[thread_1.FILTER_RELATED_BY_SENDERS.name]; - if (relationSendersFilter !== undefined) { - if (!this.arrayMatchesFilter(relationSendersFilter, relationSenders)) { - return false; - } - } - return true; - } - arrayMatchesFilter(filter, values) { - return (values.length > 0 && - filter.every((value) => { - return values.includes(value); - })); - } - /** - * Filters a list of events down to those which match this filter component - * @param events - Events to be checked against the filter component - * @returns events which matched the filter component - */ - filter(events) { - return events.filter(this.check, this); - } - /** - * Returns the limit field for a given filter component, providing a default of - * 10 if none is otherwise specified. Cargo-culted from Synapse. - * @returns the limit for this filter component. - */ - limit() { - return this.filterJson.limit !== undefined ? this.filterJson.limit : 10; - } -} -exports.FilterComponent = FilterComponent; - -},{"./models/thread":394}],364:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Filter = void 0; -const sync_1 = require("./@types/sync"); -const filter_component_1 = require("./filter-component"); -/** - */ -function setProp(obj, keyNesting, val) { - const nestedKeys = keyNesting.split("."); - let currentObj = obj; - for (let i = 0; i < nestedKeys.length - 1; i++) { - if (!currentObj[nestedKeys[i]]) { - currentObj[nestedKeys[i]] = {}; - } - currentObj = currentObj[nestedKeys[i]]; - } - currentObj[nestedKeys[nestedKeys.length - 1]] = val; -} -/* eslint-enable camelcase */ -class Filter { - /** - * Create a filter from existing data. - */ - static fromJson(userId, filterId, jsonObj) { - const filter = new Filter(userId, filterId); - filter.setDefinition(jsonObj); - return filter; - } - /** - * Construct a new Filter. - * @param userId - The user ID for this filter. - * @param filterId - The filter ID if known. - */ - constructor(userId, filterId) { - this.userId = userId; - this.filterId = filterId; - this.definition = {}; - } - /** - * Get the ID of this filter on your homeserver (if known) - * @returns The filter ID - */ - getFilterId() { - return this.filterId; - } - /** - * Get the JSON body of the filter. - * @returns The filter definition - */ - getDefinition() { - return this.definition; - } - /** - * Set the JSON body of the filter - * @param definition - The filter definition - */ - setDefinition(definition) { - this.definition = definition; - // This is all ported from synapse's FilterCollection() - // definitions look something like: - // { - // "room": { - // "rooms": ["!abcde:example.com"], - // "not_rooms": ["!123456:example.com"], - // "state": { - // "types": ["m.room.*"], - // "not_rooms": ["!726s6s6q:example.com"], - // "lazy_load_members": true, - // }, - // "timeline": { - // "limit": 10, - // "types": ["m.room.message"], - // "not_rooms": ["!726s6s6q:example.com"], - // "not_senders": ["@spam:example.com"] - // "contains_url": true - // }, - // "ephemeral": { - // "types": ["m.receipt", "m.typing"], - // "not_rooms": ["!726s6s6q:example.com"], - // "not_senders": ["@spam:example.com"] - // } - // }, - // "presence": { - // "types": ["m.presence"], - // "not_senders": ["@alice:example.com"] - // }, - // "event_format": "client", - // "event_fields": ["type", "content", "sender"] - // } - const roomFilterJson = definition.room; - // consider the top level rooms/not_rooms filter - const roomFilterFields = {}; - if (roomFilterJson) { - if (roomFilterJson.rooms) { - roomFilterFields.rooms = roomFilterJson.rooms; - } - if (roomFilterJson.rooms) { - roomFilterFields.not_rooms = roomFilterJson.not_rooms; - } - } - this.roomFilter = new filter_component_1.FilterComponent(roomFilterFields, this.userId); - this.roomTimelineFilter = new filter_component_1.FilterComponent((roomFilterJson === null || roomFilterJson === void 0 ? void 0 : roomFilterJson.timeline) || {}, this.userId); - // don't bother porting this from synapse yet: - // this._room_state_filter = - // new FilterComponent(roomFilterJson.state || {}); - // this._room_ephemeral_filter = - // new FilterComponent(roomFilterJson.ephemeral || {}); - // this._room_account_data_filter = - // new FilterComponent(roomFilterJson.account_data || {}); - // this._presence_filter = - // new FilterComponent(definition.presence || {}); - // this._account_data_filter = - // new FilterComponent(definition.account_data || {}); - } - /** - * Get the room.timeline filter component of the filter - * @returns room timeline filter component - */ - getRoomTimelineFilterComponent() { - return this.roomTimelineFilter; - } - /** - * Filter the list of events based on whether they are allowed in a timeline - * based on this filter - * @param events - the list of events being filtered - * @returns the list of events which match the filter - */ - filterRoomTimeline(events) { - if (this.roomFilter) { - events = this.roomFilter.filter(events); - } - if (this.roomTimelineFilter) { - events = this.roomTimelineFilter.filter(events); - } - return events; - } - /** - * Set the max number of events to return for each room's timeline. - * @param limit - The max number of events to return for each room. - */ - setTimelineLimit(limit) { - setProp(this.definition, "room.timeline.limit", limit); - } - /** - * Enable threads unread notification - */ - setUnreadThreadNotifications(enabled) { - var _a, _b, _c; - this.definition = Object.assign(Object.assign({}, this.definition), { room: Object.assign(Object.assign({}, (_a = this.definition) === null || _a === void 0 ? void 0 : _a.room), { timeline: Object.assign(Object.assign({}, (_c = (_b = this.definition) === null || _b === void 0 ? void 0 : _b.room) === null || _c === void 0 ? void 0 : _c.timeline), { [sync_1.UNREAD_THREAD_NOTIFICATIONS.name]: enabled }) }) }); - } - setLazyLoadMembers(enabled) { - setProp(this.definition, "room.state.lazy_load_members", enabled); - } - /** - * Control whether left rooms should be included in responses. - * @param includeLeave - True to make rooms the user has left appear - * in responses. - */ - setIncludeLeaveRooms(includeLeave) { - setProp(this.definition, "room.include_leave", includeLeave); - } -} -exports.Filter = Filter; -Filter.LAZY_LOADING_MESSAGES_FILTER = { - lazy_load_members: true, -}; - -},{"./@types/sync":314,"./filter-component":363}],365:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ConnectionError = exports.MatrixError = exports.HTTPError = void 0; -/** - * Construct a generic HTTP error. This is a JavaScript Error with additional information - * specific to HTTP responses. - * @param msg - The error message to include. - * @param httpStatus - The HTTP response status code. - */ -class HTTPError extends Error { - constructor(msg, httpStatus) { - super(msg); - this.httpStatus = httpStatus; - } -} -exports.HTTPError = HTTPError; -class MatrixError extends HTTPError { - /** - * Construct a Matrix error. This is a JavaScript Error with additional - * information specific to the standard Matrix error response. - * @param errorJson - The Matrix error JSON returned from the homeserver. - * @param httpStatus - The numeric HTTP status code given - */ - constructor(errorJson = {}, httpStatus, url, event) { - let message = errorJson.error || "Unknown message"; - if (httpStatus) { - message = `[${httpStatus}] ${message}`; - } - if (url) { - message = `${message} (${url})`; - } - super(`MatrixError: ${message}`, httpStatus); - this.httpStatus = httpStatus; - this.url = url; - this.event = event; - this.errcode = errorJson.errcode; - this.name = errorJson.errcode || "Unknown error code"; - this.data = errorJson; - } -} -exports.MatrixError = MatrixError; -/** - * Construct a ConnectionError. This is a JavaScript Error indicating - * that a request failed because of some error with the connection, either - * CORS was not correctly configured on the server, the server didn't response, - * the request timed out, or the internet connection on the client side went down. - */ -class ConnectionError extends Error { - constructor(message, cause) { - super(message + (cause ? `: ${cause.message}` : "")); - } - get name() { - return "ConnectionError"; - } -} -exports.ConnectionError = ConnectionError; - -},{}],366:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FetchHttpApi = void 0; -/** - * This is an internal module. See {@link MatrixHttpApi} for the public class. - */ -const utils = __importStar(require("../utils")); -const method_1 = require("./method"); -const errors_1 = require("./errors"); -const interface_1 = require("./interface"); -const utils_1 = require("./utils"); -class FetchHttpApi { - constructor(eventEmitter, opts) { - var _a; - this.eventEmitter = eventEmitter; - this.opts = opts; - this.abortController = new AbortController(); - utils.checkObjectHasKeys(opts, ["baseUrl", "prefix"]); - opts.onlyData = !!opts.onlyData; - opts.useAuthorizationHeader = (_a = opts.useAuthorizationHeader) !== null && _a !== void 0 ? _a : true; - } - abort() { - this.abortController.abort(); - this.abortController = new AbortController(); - } - fetch(resource, options) { - if (this.opts.fetchFn) { - return this.opts.fetchFn(resource, options); - } - return global.fetch(resource, options); - } - /** - * Sets the base URL for the identity server - * @param url - The new base url - */ - setIdBaseUrl(url) { - this.opts.idBaseUrl = url; - } - idServerRequest(method, path, params, prefix, accessToken) { - if (!this.opts.idBaseUrl) { - throw new Error("No identity server base URL set"); - } - let queryParams = undefined; - let body = undefined; - if (method === method_1.Method.Get) { - queryParams = params; - } - else { - body = params; - } - const fullUri = this.getUrl(path, queryParams, prefix, this.opts.idBaseUrl); - const opts = { - json: true, - headers: {}, - }; - if (accessToken) { - opts.headers.Authorization = `Bearer ${accessToken}`; - } - return this.requestOtherUrl(method, fullUri, body, opts); - } - /** - * Perform an authorised request to the homeserver. - * @param method - The HTTP method e.g. "GET". - * @param path - The HTTP path after the supplied prefix e.g. - * "/createRoom". - * - * @param queryParams - A dict of query params (these will NOT be - * urlencoded). If unspecified, there will be no query params. - * - * @param body - The HTTP JSON body. - * - * @param opts - additional options. If a number is specified, - * this is treated as `opts.localTimeoutMs`. - * - * @returns Promise which resolves to - * ``` - * { - * data: {Object}, - * headers: {Object}, - * code: {Number}, - * } - * ``` - * If `onlyData` is set, this will resolve to the `data` object only. - * @returns Rejects with an error if a problem occurred. - * This includes network problems and Matrix-specific error JSON. - */ - authedRequest(method, path, queryParams, body, opts = {}) { - if (!queryParams) - queryParams = {}; - if (this.opts.accessToken) { - if (this.opts.useAuthorizationHeader) { - if (!opts.headers) { - opts.headers = {}; - } - if (!opts.headers.Authorization) { - opts.headers.Authorization = "Bearer " + this.opts.accessToken; - } - if (queryParams.access_token) { - delete queryParams.access_token; - } - } - else if (!queryParams.access_token) { - queryParams.access_token = this.opts.accessToken; - } - } - const requestPromise = this.request(method, path, queryParams, body, opts); - requestPromise.catch((err) => { - if (err.errcode == "M_UNKNOWN_TOKEN" && !(opts === null || opts === void 0 ? void 0 : opts.inhibitLogoutEmit)) { - this.eventEmitter.emit(interface_1.HttpApiEvent.SessionLoggedOut, err); - } - else if (err.errcode == "M_CONSENT_NOT_GIVEN") { - this.eventEmitter.emit(interface_1.HttpApiEvent.NoConsent, err.message, err.data.consent_uri); - } - }); - // return the original promise, otherwise tests break due to it having to - // go around the event loop one more time to process the result of the request - return requestPromise; - } - /** - * Perform a request to the homeserver without any credentials. - * @param method - The HTTP method e.g. "GET". - * @param path - The HTTP path after the supplied prefix e.g. - * "/createRoom". - * - * @param queryParams - A dict of query params (these will NOT be - * urlencoded). If unspecified, there will be no query params. - * - * @param body - The HTTP JSON body. - * - * @param opts - additional options - * - * @returns Promise which resolves to - * ``` - * { - * data: {Object}, - * headers: {Object}, - * code: {Number}, - * } - * ``` - * If `onlyData is set, this will resolve to the data` - * object only. - * @returns Rejects with an error if a problem - * occurred. This includes network problems and Matrix-specific error JSON. - */ - request(method, path, queryParams, body, opts) { - const fullUri = this.getUrl(path, queryParams, opts === null || opts === void 0 ? void 0 : opts.prefix, opts === null || opts === void 0 ? void 0 : opts.baseUrl); - return this.requestOtherUrl(method, fullUri, body, opts); - } - /** - * Perform a request to an arbitrary URL. - * @param method - The HTTP method e.g. "GET". - * @param url - The HTTP URL object. - * - * @param body - The HTTP JSON body. - * - * @param opts - additional options - * - * @returns Promise which resolves to data unless `onlyData` is specified as false, - * where the resolved value will be a fetch Response object. - * @returns Rejects with an error if a problem - * occurred. This includes network problems and Matrix-specific error JSON. - */ - requestOtherUrl(method, url, body, opts = {}) { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - const headers = Object.assign({}, opts.headers || {}); - const json = (_a = opts.json) !== null && _a !== void 0 ? _a : true; - // We can't use getPrototypeOf here as objects made in other contexts e.g. over postMessage won't have same ref - const jsonBody = json && ((_b = body === null || body === void 0 ? void 0 : body.constructor) === null || _b === void 0 ? void 0 : _b.name) === Object.name; - if (json) { - if (jsonBody && !headers["Content-Type"]) { - headers["Content-Type"] = "application/json"; - } - if (!headers["Accept"]) { - headers["Accept"] = "application/json"; - } - } - const timeout = (_c = opts.localTimeoutMs) !== null && _c !== void 0 ? _c : this.opts.localTimeoutMs; - const keepAlive = (_d = opts.keepAlive) !== null && _d !== void 0 ? _d : false; - const signals = [this.abortController.signal]; - if (timeout !== undefined) { - signals.push((0, utils_1.timeoutSignal)(timeout)); - } - if (opts.abortSignal) { - signals.push(opts.abortSignal); - } - let data; - if (jsonBody) { - data = JSON.stringify(body); - } - else { - data = body; - } - const { signal, cleanup } = (0, utils_1.anySignal)(signals); - let res; - try { - res = yield this.fetch(url, { - signal, - method, - body: data, - headers, - mode: "cors", - redirect: "follow", - referrer: "", - referrerPolicy: "no-referrer", - cache: "no-cache", - credentials: "omit", - keepalive: keepAlive, - }); - } - catch (e) { - if (e.name === "AbortError") { - throw e; - } - throw new errors_1.ConnectionError("fetch failed", e); - } - finally { - cleanup(); - } - if (!res.ok) { - throw (0, utils_1.parseErrorResponse)(res, yield res.text()); - } - if (this.opts.onlyData) { - return json ? res.json() : res.text(); - } - return res; - }); - } - /** - * Form and return a homeserver request URL based on the given path params and prefix. - * @param path - The HTTP path after the supplied prefix e.g. "/createRoom". - * @param queryParams - A dict of query params (these will NOT be urlencoded). - * @param prefix - The full prefix to use e.g. "/_matrix/client/v2_alpha", defaulting to this.opts.prefix. - * @param baseUrl - The baseUrl to use e.g. "https://matrix.org/", defaulting to this.opts.baseUrl. - * @returns URL - */ - getUrl(path, queryParams, prefix, baseUrl) { - const url = new URL((baseUrl !== null && baseUrl !== void 0 ? baseUrl : this.opts.baseUrl) + (prefix !== null && prefix !== void 0 ? prefix : this.opts.prefix) + path); - if (queryParams) { - utils.encodeParams(queryParams, url.searchParams); - } - return url; - } -} -exports.FetchHttpApi = FetchHttpApi; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../utils":416,"./errors":365,"./interface":368,"./method":369,"./utils":371}],367:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MatrixHttpApi = void 0; -const fetch_1 = require("./fetch"); -const prefix_1 = require("./prefix"); -const utils = __importStar(require("../utils")); -const callbacks = __importStar(require("../realtime-callbacks")); -const method_1 = require("./method"); -const errors_1 = require("./errors"); -const utils_1 = require("./utils"); -__exportStar(require("./interface"), exports); -__exportStar(require("./prefix"), exports); -__exportStar(require("./errors"), exports); -__exportStar(require("./method"), exports); -__exportStar(require("./utils"), exports); -class MatrixHttpApi extends fetch_1.FetchHttpApi { - constructor() { - super(...arguments); - this.uploads = []; - } - /** - * Upload content to the homeserver - * - * @param file - The object to upload. On a browser, something that - * can be sent to XMLHttpRequest.send (typically a File). Under node.js, - * a Buffer, String or ReadStream. - * - * @param opts - options object - * - * @returns Promise which resolves to response object, as - * determined by this.opts.onlyData, opts.rawResponse, and - * opts.onlyContentUri. Rejects with an error (usually a MatrixError). - */ - uploadContent(file, opts = {}) { - var _a, _b, _c, _d, _e; - const includeFilename = (_a = opts.includeFilename) !== null && _a !== void 0 ? _a : true; - const abortController = (_b = opts.abortController) !== null && _b !== void 0 ? _b : new AbortController(); - // If the file doesn't have a mime type, use a default since the HS errors if we don't supply one. - const contentType = (_d = (_c = opts.type) !== null && _c !== void 0 ? _c : file.type) !== null && _d !== void 0 ? _d : "application/octet-stream"; - const fileName = (_e = opts.name) !== null && _e !== void 0 ? _e : file.name; - const upload = { - loaded: 0, - total: 0, - abortController, - }; - const defer = utils.defer(); - if (global.XMLHttpRequest) { - const xhr = new global.XMLHttpRequest(); - const timeoutFn = function () { - xhr.abort(); - defer.reject(new Error("Timeout")); - }; - // set an initial timeout of 30s; we'll advance it each time we get a progress notification - let timeoutTimer = callbacks.setTimeout(timeoutFn, 30000); - xhr.onreadystatechange = function () { - switch (xhr.readyState) { - case global.XMLHttpRequest.DONE: - callbacks.clearTimeout(timeoutTimer); - try { - if (xhr.status === 0) { - throw new DOMException(xhr.statusText, "AbortError"); // mimic fetch API - } - if (!xhr.responseText) { - throw new Error("No response body."); - } - if (xhr.status >= 400) { - defer.reject((0, utils_1.parseErrorResponse)(xhr, xhr.responseText)); - } - else { - defer.resolve(JSON.parse(xhr.responseText)); - } - } - catch (err) { - if (err.name === "AbortError") { - defer.reject(err); - return; - } - defer.reject(new errors_1.ConnectionError("request failed", err)); - } - break; - } - }; - xhr.upload.onprogress = (ev) => { - var _a; - callbacks.clearTimeout(timeoutTimer); - upload.loaded = ev.loaded; - upload.total = ev.total; - timeoutTimer = callbacks.setTimeout(timeoutFn, 30000); - (_a = opts.progressHandler) === null || _a === void 0 ? void 0 : _a.call(opts, { - loaded: ev.loaded, - total: ev.total, - }); - }; - const url = this.getUrl("/upload", undefined, prefix_1.MediaPrefix.R0); - if (includeFilename && fileName) { - url.searchParams.set("filename", encodeURIComponent(fileName)); - } - if (!this.opts.useAuthorizationHeader && this.opts.accessToken) { - url.searchParams.set("access_token", encodeURIComponent(this.opts.accessToken)); - } - xhr.open(method_1.Method.Post, url.href); - if (this.opts.useAuthorizationHeader && this.opts.accessToken) { - xhr.setRequestHeader("Authorization", "Bearer " + this.opts.accessToken); - } - xhr.setRequestHeader("Content-Type", contentType); - xhr.send(file); - abortController.signal.addEventListener("abort", () => { - xhr.abort(); - }); - } - else { - const queryParams = {}; - if (includeFilename && fileName) { - queryParams.filename = fileName; - } - const headers = { "Content-Type": contentType }; - this.authedRequest(method_1.Method.Post, "/upload", queryParams, file, { - prefix: prefix_1.MediaPrefix.R0, - headers, - abortSignal: abortController.signal, - }) - .then((response) => { - return this.opts.onlyData ? response : response.json(); - }) - .then(defer.resolve, defer.reject); - } - // remove the upload from the list on completion - upload.promise = defer.promise.finally(() => { - utils.removeElement(this.uploads, (elem) => elem === upload); - }); - abortController.signal.addEventListener("abort", () => { - utils.removeElement(this.uploads, (elem) => elem === upload); - defer.reject(new DOMException("Aborted", "AbortError")); - }); - this.uploads.push(upload); - return upload.promise; - } - cancelUpload(promise) { - const upload = this.uploads.find((u) => u.promise === promise); - if (upload) { - upload.abortController.abort(); - return true; - } - return false; - } - getCurrentUploads() { - return this.uploads; - } - /** - * Get the content repository url with query parameters. - * @returns An object with a 'base', 'path' and 'params' for base URL, - * path and query parameters respectively. - */ - getContentUri() { - return { - base: this.opts.baseUrl, - path: prefix_1.MediaPrefix.R0 + "/upload", - params: { - access_token: this.opts.accessToken, - }, - }; - } -} -exports.MatrixHttpApi = MatrixHttpApi; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../realtime-callbacks":399,"../utils":416,"./errors":365,"./fetch":366,"./interface":368,"./method":369,"./prefix":370,"./utils":371}],368:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HttpApiEvent = void 0; -var HttpApiEvent; -(function (HttpApiEvent) { - HttpApiEvent["SessionLoggedOut"] = "Session.logged_out"; - HttpApiEvent["NoConsent"] = "no_consent"; -})(HttpApiEvent = exports.HttpApiEvent || (exports.HttpApiEvent = {})); - -},{}],369:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Method = void 0; -var Method; -(function (Method) { - Method["Get"] = "GET"; - Method["Put"] = "PUT"; - Method["Post"] = "POST"; - Method["Delete"] = "DELETE"; -})(Method = exports.Method || (exports.Method = {})); - -},{}],370:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MediaPrefix = exports.IdentityPrefix = exports.ClientPrefix = void 0; -var ClientPrefix; -(function (ClientPrefix) { - /** - * A constant representing the URI path for release 0 of the Client-Server HTTP API. - */ - ClientPrefix["R0"] = "/_matrix/client/r0"; - /** - * A constant representing the URI path for the legacy release v1 of the Client-Server HTTP API. - */ - ClientPrefix["V1"] = "/_matrix/client/v1"; - /** - * A constant representing the URI path for Client-Server API endpoints versioned at v3. - */ - ClientPrefix["V3"] = "/_matrix/client/v3"; - /** - * A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs. - */ - ClientPrefix["Unstable"] = "/_matrix/client/unstable"; -})(ClientPrefix = exports.ClientPrefix || (exports.ClientPrefix = {})); -var IdentityPrefix; -(function (IdentityPrefix) { - /** - * URI path for the v2 identity API - */ - IdentityPrefix["V2"] = "/_matrix/identity/v2"; -})(IdentityPrefix = exports.IdentityPrefix || (exports.IdentityPrefix = {})); -var MediaPrefix; -(function (MediaPrefix) { - /** - * URI path for the media repo API - */ - MediaPrefix["R0"] = "/_matrix/media/r0"; -})(MediaPrefix = exports.MediaPrefix || (exports.MediaPrefix = {})); - -},{}],371:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.retryNetworkOperation = exports.parseErrorResponse = exports.anySignal = exports.timeoutSignal = void 0; -const content_type_1 = require("content-type"); -const logger_1 = require("../logger"); -const utils_1 = require("../utils"); -const errors_1 = require("./errors"); -// Ponyfill for https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout -function timeoutSignal(ms) { - const controller = new AbortController(); - setTimeout(() => { - controller.abort(); - }, ms); - return controller.signal; -} -exports.timeoutSignal = timeoutSignal; -function anySignal(signals) { - const controller = new AbortController(); - function cleanup() { - for (const signal of signals) { - signal.removeEventListener("abort", onAbort); - } - } - function onAbort() { - controller.abort(); - cleanup(); - } - for (const signal of signals) { - if (signal.aborted) { - onAbort(); - break; - } - signal.addEventListener("abort", onAbort); - } - return { - signal: controller.signal, - cleanup, - }; -} -exports.anySignal = anySignal; -/** - * Attempt to turn an HTTP error response into a Javascript Error. - * - * If it is a JSON response, we will parse it into a MatrixError. Otherwise - * we return a generic Error. - * - * @param response - response object - * @param body - raw body of the response - * @returns - */ -function parseErrorResponse(response, body) { - let contentType; - try { - contentType = getResponseContentType(response); - } - catch (e) { - return e; - } - if ((contentType === null || contentType === void 0 ? void 0 : contentType.type) === "application/json" && body) { - return new errors_1.MatrixError(JSON.parse(body), response.status, isXhr(response) ? response.responseURL : response.url); - } - if ((contentType === null || contentType === void 0 ? void 0 : contentType.type) === "text/plain") { - return new errors_1.HTTPError(`Server returned ${response.status} error: ${body}`, response.status); - } - return new errors_1.HTTPError(`Server returned ${response.status} error`, response.status); -} -exports.parseErrorResponse = parseErrorResponse; -function isXhr(response) { - return "getResponseHeader" in response; -} -/** - * extract the Content-Type header from the response object, and - * parse it to a `{type, parameters}` object. - * - * returns null if no content-type header could be found. - * - * @param response - response object - * @returns parsed content-type header, or null if not found - */ -function getResponseContentType(response) { - let contentType; - if (isXhr(response)) { - contentType = response.getResponseHeader("Content-Type"); - } - else { - contentType = response.headers.get("Content-Type"); - } - if (!contentType) - return null; - try { - return (0, content_type_1.parse)(contentType); - } - catch (e) { - throw new Error(`Error parsing Content-Type '${contentType}': ${e}`); - } -} -/** - * Retries a network operation run in a callback. - * @param maxAttempts - maximum attempts to try - * @param callback - callback that returns a promise of the network operation. If rejected with ConnectionError, it will be retried by calling the callback again. - * @returns the result of the network operation - * @throws {@link ConnectionError} If after maxAttempts the callback still throws ConnectionError - */ -function retryNetworkOperation(maxAttempts, callback) { - return __awaiter(this, void 0, void 0, function* () { - let attempts = 0; - let lastConnectionError = null; - while (attempts < maxAttempts) { - try { - if (attempts > 0) { - const timeout = 1000 * Math.pow(2, attempts); - logger_1.logger.log(`network operation failed ${attempts} times, retrying in ${timeout}ms...`); - yield (0, utils_1.sleep)(timeout); - } - return yield callback(); - } - catch (err) { - if (err instanceof errors_1.ConnectionError) { - attempts += 1; - lastConnectionError = err; - } - else { - throw err; - } - } - } - throw lastConnectionError; - }); -} -exports.retryNetworkOperation = retryNetworkOperation; - -},{"../logger":374,"../utils":416,"./errors":365,"content-type":72}],372:[function(require,module,exports){ -"use strict"; -/* -Copyright 2019 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.exists = void 0; -/** - * Check if an IndexedDB database exists. The only way to do so is to try opening it, so - * we do that and then delete it did not exist before. - * - * @param indexedDB - The `indexedDB` interface - * @param dbName - The database name to test for - * @returns Whether the database exists - */ -function exists(indexedDB, dbName) { - return new Promise((resolve, reject) => { - let exists = true; - const req = indexedDB.open(dbName); - req.onupgradeneeded = () => { - // Since we did not provide an explicit version when opening, this event - // should only fire if the DB did not exist before at any version. - exists = false; - }; - req.onblocked = () => reject(req.error); - req.onsuccess = () => { - const db = req.result; - db.close(); - if (!exists) { - // The DB did not exist before, but has been created as part of this - // existence check. Delete it now to restore previous state. Delete can - // actually take a while to complete in some browsers, so don't wait for - // it. This won't block future open calls that a store might issue next to - // properly set up the DB. - indexedDB.deleteDatabase(dbName); - } - resolve(exists); - }; - req.onerror = () => reject(req.error); - }); -} -exports.exists = exists; - -},{}],373:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InteractiveAuth = exports.AuthType = void 0; -const logger_1 = require("./logger"); -const utils_1 = require("./utils"); -const EMAIL_STAGE_TYPE = "m.login.email.identity"; -const MSISDN_STAGE_TYPE = "m.login.msisdn"; -var AuthType; -(function (AuthType) { - AuthType["Password"] = "m.login.password"; - AuthType["Recaptcha"] = "m.login.recaptcha"; - AuthType["Terms"] = "m.login.terms"; - AuthType["Email"] = "m.login.email.identity"; - AuthType["Msisdn"] = "m.login.msisdn"; - AuthType["Sso"] = "m.login.sso"; - AuthType["SsoUnstable"] = "org.matrix.login.sso"; - AuthType["Dummy"] = "m.login.dummy"; - AuthType["RegistrationToken"] = "m.login.registration_token"; - // For backwards compatability with servers that have not yet updated to - // use the stable "m.login.registration_token" type. - // The authentication flow is the same in both cases. - AuthType["UnstableRegistrationToken"] = "org.matrix.msc3231.login.registration_token"; -})(AuthType = exports.AuthType || (exports.AuthType = {})); -class NoAuthFlowFoundError extends Error { - // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase - constructor(m, required_stages, flows) { - super(m); - this.required_stages = required_stages; - this.flows = flows; - this.name = "NoAuthFlowFoundError"; - } -} -/** - * Abstracts the logic used to drive the interactive auth process. - * - *

Components implementing an interactive auth flow should instantiate one of - * these, passing in the necessary callbacks to the constructor. They should - * then call attemptAuth, which will return a promise which will resolve or - * reject when the interactive-auth process completes. - * - *

Meanwhile, calls will be made to the startAuthStage and doRequest - * callbacks, and information gathered from the user can be submitted with - * submitAuthDict. - * - * @param opts - options object - */ -class InteractiveAuth { - constructor(opts) { - this.requestingEmailToken = false; - this.attemptAuthDeferred = null; - this.chosenFlow = null; - this.currentStage = null; - this.emailAttempt = 1; - // if we are currently trying to submit an auth dict (which includes polling) - // the promise the will resolve/reject when it completes - this.submitPromise = null; - /** - * Requests a new email token and sets the email sid for the validation session - */ - this.requestEmailToken = () => __awaiter(this, void 0, void 0, function* () { - if (!this.requestingEmailToken) { - logger_1.logger.trace("Requesting email token. Attempt: " + this.emailAttempt); - // If we've picked a flow with email auth, we send the email - // now because we want the request to fail as soon as possible - // if the email address is not valid (ie. already taken or not - // registered, depending on what the operation is). - this.requestingEmailToken = true; - try { - const requestTokenResult = yield this.requestEmailTokenCallback(this.inputs.emailAddress, this.clientSecret, this.emailAttempt++, this.data.session); - this.emailSid = requestTokenResult.sid; - logger_1.logger.trace("Email token request succeeded"); - } - finally { - this.requestingEmailToken = false; - } - } - else { - logger_1.logger.warn("Could not request email token: Already requesting"); - } - }); - this.matrixClient = opts.matrixClient; - this.data = opts.authData || {}; - this.requestCallback = opts.doRequest; - this.busyChangedCallback = opts.busyChanged; - // startAuthStage included for backwards compat - this.stateUpdatedCallback = opts.stateUpdated || opts.startAuthStage; - this.requestEmailTokenCallback = opts.requestEmailToken; - this.inputs = opts.inputs || {}; - if (opts.sessionId) - this.data.session = opts.sessionId; - this.clientSecret = opts.clientSecret || this.matrixClient.generateClientSecret(); - this.emailSid = opts.emailSid; - } - /** - * begin the authentication process. - * - * @returns which resolves to the response on success, - * or rejects with the error on failure. Rejects with NoAuthFlowFoundError if - * no suitable authentication flow can be found - */ - attemptAuth() { - var _a, _b; - // This promise will be quite long-lived and will resolve when the - // request is authenticated and completes successfully. - this.attemptAuthDeferred = (0, utils_1.defer)(); - // pluck the promise out now, as doRequest may clear before we return - const promise = this.attemptAuthDeferred.promise; - // if we have no flows, try a request to acquire the flows - if (!((_a = this.data) === null || _a === void 0 ? void 0 : _a.flows)) { - (_b = this.busyChangedCallback) === null || _b === void 0 ? void 0 : _b.call(this, true); - // use the existing sessionId, if one is present. - const auth = this.data.session ? { session: this.data.session } : null; - this.doRequest(auth).finally(() => { - var _a; - (_a = this.busyChangedCallback) === null || _a === void 0 ? void 0 : _a.call(this, false); - }); - } - else { - this.startNextAuthStage(); - } - return promise; - } - /** - * Poll to check if the auth session or current stage has been - * completed out-of-band. If so, the attemptAuth promise will - * be resolved. - */ - poll() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.data.session) - return; - // likewise don't poll if there is no auth session in progress - if (!this.attemptAuthDeferred) - return; - // if we currently have a request in flight, there's no point making - // another just to check what the status is - if (this.submitPromise) - return; - let authDict = {}; - if (this.currentStage == EMAIL_STAGE_TYPE) { - // The email can be validated out-of-band, but we need to provide the - // creds so the HS can go & check it. - if (this.emailSid) { - const creds = { - sid: this.emailSid, - client_secret: this.clientSecret, - }; - if (yield this.matrixClient.doesServerRequireIdServerParam()) { - const idServerParsedUrl = new URL(this.matrixClient.getIdentityServerUrl()); - creds.id_server = idServerParsedUrl.host; - } - authDict = { - type: EMAIL_STAGE_TYPE, - // TODO: Remove `threepid_creds` once servers support proper UIA - // See https://github.com/matrix-org/synapse/issues/5665 - // See https://github.com/matrix-org/matrix-doc/issues/2220 - threepid_creds: creds, - threepidCreds: creds, - }; - } - } - this.submitAuthDict(authDict, true); - }); - } - /** - * get the auth session ID - * - * @returns session id - */ - getSessionId() { - var _a; - return (_a = this.data) === null || _a === void 0 ? void 0 : _a.session; - } - /** - * get the client secret used for validation sessions - * with the identity server. - * - * @returns client secret - */ - getClientSecret() { - return this.clientSecret; - } - /** - * get the server params for a given stage - * - * @param loginType - login type for the stage - * @returns any parameters from the server for this stage - */ - getStageParams(loginType) { - var _a; - return (_a = this.data.params) === null || _a === void 0 ? void 0 : _a[loginType]; - } - getChosenFlow() { - return this.chosenFlow; - } - /** - * submit a new auth dict and fire off the request. This will either - * make attemptAuth resolve/reject, or cause the startAuthStage callback - * to be called for a new stage. - * - * @param authData - new auth dict to send to the server. Should - * include a `type` property denoting the login type, as well as any - * other params for that stage. - * @param background - If true, this request failing will not result - * in the attemptAuth promise being rejected. This can be set to true - * for requests that just poll to see if auth has been completed elsewhere. - */ - submitAuthDict(authData, background = false) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - if (!this.attemptAuthDeferred) { - throw new Error("submitAuthDict() called before attemptAuth()"); - } - if (!background) { - (_a = this.busyChangedCallback) === null || _a === void 0 ? void 0 : _a.call(this, true); - } - // if we're currently trying a request, wait for it to finish - // as otherwise we can get multiple 200 responses which can mean - // things like multiple logins for register requests. - // (but discard any exceptions as we only care when its done, - // not whether it worked or not) - while (this.submitPromise) { - try { - yield this.submitPromise; - } - catch (e) { } - } - // use the sessionid from the last request, if one is present. - let auth; - if (this.data.session) { - auth = { - session: this.data.session, - }; - Object.assign(auth, authData); - } - else { - auth = authData; - } - try { - // NB. the 'background' flag is deprecated by the busyChanged - // callback and is here for backwards compat - this.submitPromise = this.doRequest(auth, background); - yield this.submitPromise; - } - finally { - this.submitPromise = null; - if (!background) { - (_b = this.busyChangedCallback) === null || _b === void 0 ? void 0 : _b.call(this, false); - } - } - }); - } - /** - * Gets the sid for the email validation session - * Specific to m.login.email.identity - * - * @returns The sid of the email auth session - */ - getEmailSid() { - return this.emailSid; - } - /** - * Sets the sid for the email validation session - * This must be set in order to successfully poll for completion - * of the email validation. - * Specific to m.login.email.identity - * - * @param sid - The sid for the email validation session - */ - setEmailSid(sid) { - this.emailSid = sid; - } - /** - * Fire off a request, and either resolve the promise, or call - * startAuthStage. - * - * @internal - * @param auth - new auth dict, including session id - * @param background - If true, this request is a background poll, so it - * failing will not result in the attemptAuth promise being rejected. - * This can be set to true for requests that just poll to see if auth has - * been completed elsewhere. - */ - doRequest(auth, background = false) { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - try { - const result = yield this.requestCallback(auth, background); - this.attemptAuthDeferred.resolve(result); - this.attemptAuthDeferred = null; - } - catch (error) { - // sometimes UI auth errors don't come with flows - const errorFlows = (_b = (_a = error.data) === null || _a === void 0 ? void 0 : _a.flows) !== null && _b !== void 0 ? _b : null; - const haveFlows = this.data.flows || Boolean(errorFlows); - if (error.httpStatus !== 401 || !error.data || !haveFlows) { - // doesn't look like an interactive-auth failure. - if (!background) { - (_c = this.attemptAuthDeferred) === null || _c === void 0 ? void 0 : _c.reject(error); - } - else { - // We ignore all failures here (even non-UI auth related ones) - // since we don't want to suddenly fail if the internet connection - // had a blip whilst we were polling - logger_1.logger.log("Background poll request failed doing UI auth: ignoring", error); - } - } - if (!error.data) { - error.data = {}; - } - // if the error didn't come with flows, completed flows or session ID, - // copy over the ones we have. Synapse sometimes sends responses without - // any UI auth data (eg. when polling for email validation, if the email - // has not yet been validated). This appears to be a Synapse bug, which - // we workaround here. - if (!error.data.flows && - !error.data.completed && - !error.data.session) { - error.data.flows = this.data.flows; - error.data.completed = this.data.completed; - error.data.session = this.data.session; - } - this.data = error.data; - try { - this.startNextAuthStage(); - } - catch (e) { - this.attemptAuthDeferred.reject(e); - this.attemptAuthDeferred = null; - return; - } - if (!this.emailSid && ((_d = this.chosenFlow) === null || _d === void 0 ? void 0 : _d.stages.includes(AuthType.Email))) { - try { - yield this.requestEmailToken(); - // NB. promise is not resolved here - at some point, doRequest - // will be called again and if the user has jumped through all - // the hoops correctly, auth will be complete and the request - // will succeed. - // Also, we should expose the fact that this request has compledted - // so clients can know that the email has actually been sent. - } - catch (e) { - // we failed to request an email token, so fail the request. - // This could be due to the email already beeing registered - // (or not being registered, depending on what we're trying - // to do) or it could be a network failure. Either way, pass - // the failure up as the user can't complete auth if we can't - // send the email, for whatever reason. - this.attemptAuthDeferred.reject(e); - this.attemptAuthDeferred = null; - } - } - } - }); - } - /** - * Pick the next stage and call the callback - * - * @internal - * @throws {@link NoAuthFlowFoundError} If no suitable authentication flow can be found - */ - startNextAuthStage() { - var _a, _b, _c, _d; - const nextStage = this.chooseStage(); - if (!nextStage) { - throw new Error("No incomplete flows from the server"); - } - this.currentStage = nextStage; - if (nextStage === AuthType.Dummy) { - this.submitAuthDict({ - type: "m.login.dummy", - }); - return; - } - if (((_a = this.data) === null || _a === void 0 ? void 0 : _a.errcode) || ((_b = this.data) === null || _b === void 0 ? void 0 : _b.error)) { - this.stateUpdatedCallback(nextStage, { - errcode: ((_c = this.data) === null || _c === void 0 ? void 0 : _c.errcode) || "", - error: ((_d = this.data) === null || _d === void 0 ? void 0 : _d.error) || "", - }); - return; - } - this.stateUpdatedCallback(nextStage, nextStage === EMAIL_STAGE_TYPE ? { emailSid: this.emailSid } : {}); - } - /** - * Pick the next auth stage - * - * @internal - * @returns login type - * @throws {@link NoAuthFlowFoundError} If no suitable authentication flow can be found - */ - chooseStage() { - if (this.chosenFlow === null) { - this.chosenFlow = this.chooseFlow(); - } - logger_1.logger.log("Active flow => %s", JSON.stringify(this.chosenFlow)); - const nextStage = this.firstUncompletedStage(this.chosenFlow); - logger_1.logger.log("Next stage: %s", nextStage); - return nextStage; - } - /** - * Pick one of the flows from the returned list - * If a flow using all of the inputs is found, it will - * be returned, otherwise, null will be returned. - * - * Only flows using all given inputs are chosen because it - * is likely to be surprising if the user provides a - * credential and it is not used. For example, for registration, - * this could result in the email not being used which would leave - * the account with no means to reset a password. - * - * @internal - * @returns flow - * @throws {@link NoAuthFlowFoundError} If no suitable authentication flow can be found - */ - chooseFlow() { - const flows = this.data.flows || []; - // we've been given an email or we've already done an email part - const haveEmail = Boolean(this.inputs.emailAddress) || Boolean(this.emailSid); - const haveMsisdn = Boolean(this.inputs.phoneCountry) && Boolean(this.inputs.phoneNumber); - for (const flow of flows) { - let flowHasEmail = false; - let flowHasMsisdn = false; - for (const stage of flow.stages) { - if (stage === EMAIL_STAGE_TYPE) { - flowHasEmail = true; - } - else if (stage == MSISDN_STAGE_TYPE) { - flowHasMsisdn = true; - } - } - if (flowHasEmail == haveEmail && flowHasMsisdn == haveMsisdn) { - return flow; - } - } - const requiredStages = []; - if (haveEmail) - requiredStages.push(EMAIL_STAGE_TYPE); - if (haveMsisdn) - requiredStages.push(MSISDN_STAGE_TYPE); - // Throw an error with a fairly generic description, but with more - // information such that the app can give a better one if so desired. - throw new NoAuthFlowFoundError("No appropriate authentication flow found", requiredStages, flows); - } - /** - * Get the first uncompleted stage in the given flow - * - * @internal - * @returns login type - */ - firstUncompletedStage(flow) { - const completed = this.data.completed || []; - return flow.stages.find((stageType) => !completed.includes(stageType)); - } -} -exports.InteractiveAuth = InteractiveAuth; - -},{"./logger":374,"./utils":416}],374:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 André Jaenisch -Copyright 2019, 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.logger = void 0; -const loglevel_1 = __importDefault(require("loglevel")); -// This is to demonstrate, that you can use any namespace you want. -// Namespaces allow you to turn on/off the logging for specific parts of the -// application. -// An idea would be to control this via an environment variable (on Node.js). -// See https://www.npmjs.com/package/debug to see how this could be implemented -// Part of #332 is introducing a logging library in the first place. -const DEFAULT_NAMESPACE = "matrix"; -// because rageshakes in react-sdk hijack the console log, also at module load time, -// initializing the logger here races with the initialization of rageshakes. -// to avoid the issue, we override the methodFactory of loglevel that binds to the -// console methods at initialization time by a factory that looks up the console methods -// when logging so we always get the current value of console methods. -loglevel_1.default.methodFactory = function (methodName, logLevel, loggerName) { - return function (...args) { - /* eslint-disable @typescript-eslint/no-invalid-this */ - if (this.prefix) { - args.unshift(this.prefix); - } - /* eslint-enable @typescript-eslint/no-invalid-this */ - const supportedByConsole = methodName === "error" || methodName === "warn" || methodName === "trace" || methodName === "info"; - /* eslint-disable no-console */ - if (supportedByConsole) { - return console[methodName](...args); - } - else { - return console.log(...args); - } - /* eslint-enable no-console */ - }; -}; -/** - * Drop-in replacement for `console` using {@link https://www.npmjs.com/package/loglevel|loglevel}. - * Can be tailored down to specific use cases if needed. - */ -exports.logger = loglevel_1.default.getLogger(DEFAULT_NAMESPACE); -exports.logger.setLevel(loglevel_1.default.levels.DEBUG, false); -function extendLogger(logger) { - logger.withPrefix = function (prefix) { - const existingPrefix = this.prefix || ""; - return getPrefixedLogger(existingPrefix + prefix); - }; -} -extendLogger(exports.logger); -function getPrefixedLogger(prefix) { - const prefixLogger = loglevel_1.default.getLogger(`${DEFAULT_NAMESPACE}-${prefix}`); - if (prefixLogger.prefix !== prefix) { - // Only do this setup work the first time through, as loggers are saved by name. - extendLogger(prefixLogger); - prefixLogger.prefix = prefix; - prefixLogger.setLevel(loglevel_1.default.levels.DEBUG, false); - } - return prefixLogger; -} - -},{"loglevel":151}],375:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2015-2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createRoomWidgetClient = exports.createClient = exports.setCryptoStoreFactory = exports.GroupCallType = exports.GroupCallState = exports.GroupCallIntent = exports.GroupCallEvent = exports.createNewMatrixCall = exports.SecretStorage = exports.ContentHelpers = void 0; -const memory_crypto_store_1 = require("./crypto/store/memory-crypto-store"); -const memory_1 = require("./store/memory"); -const scheduler_1 = require("./scheduler"); -const client_1 = require("./client"); -const embedded_1 = require("./embedded"); -__exportStar(require("./client"), exports); -__exportStar(require("./embedded"), exports); -__exportStar(require("./http-api"), exports); -__exportStar(require("./autodiscovery"), exports); -__exportStar(require("./sync-accumulator"), exports); -__exportStar(require("./errors"), exports); -__exportStar(require("./models/beacon"), exports); -__exportStar(require("./models/event"), exports); -__exportStar(require("./models/room"), exports); -__exportStar(require("./models/event-timeline"), exports); -__exportStar(require("./models/event-timeline-set"), exports); -__exportStar(require("./models/poll"), exports); -__exportStar(require("./models/room-member"), exports); -__exportStar(require("./models/room-state"), exports); -__exportStar(require("./models/user"), exports); -__exportStar(require("./scheduler"), exports); -__exportStar(require("./filter"), exports); -__exportStar(require("./timeline-window"), exports); -__exportStar(require("./interactive-auth"), exports); -__exportStar(require("./service-types"), exports); -__exportStar(require("./store/memory"), exports); -__exportStar(require("./store/indexeddb"), exports); -__exportStar(require("./crypto/store/memory-crypto-store"), exports); -__exportStar(require("./crypto/store/indexeddb-crypto-store"), exports); -__exportStar(require("./content-repo"), exports); -__exportStar(require("./@types/event"), exports); -__exportStar(require("./@types/PushRules"), exports); -__exportStar(require("./@types/partials"), exports); -__exportStar(require("./@types/requests"), exports); -__exportStar(require("./@types/search"), exports); -__exportStar(require("./models/room-summary"), exports); -exports.ContentHelpers = __importStar(require("./content-helpers")); -exports.SecretStorage = __importStar(require("./secret-storage")); -var call_1 = require("./webrtc/call"); -Object.defineProperty(exports, "createNewMatrixCall", { enumerable: true, get: function () { return call_1.createNewMatrixCall; } }); -var groupCall_1 = require("./webrtc/groupCall"); -Object.defineProperty(exports, "GroupCallEvent", { enumerable: true, get: function () { return groupCall_1.GroupCallEvent; } }); -Object.defineProperty(exports, "GroupCallIntent", { enumerable: true, get: function () { return groupCall_1.GroupCallIntent; } }); -Object.defineProperty(exports, "GroupCallState", { enumerable: true, get: function () { return groupCall_1.GroupCallState; } }); -Object.defineProperty(exports, "GroupCallType", { enumerable: true, get: function () { return groupCall_1.GroupCallType; } }); -let cryptoStoreFactory = () => new memory_crypto_store_1.MemoryCryptoStore(); -/** - * Configure a different factory to be used for creating crypto stores - * - * @param fac - a function which will return a new {@link CryptoStore} - */ -function setCryptoStoreFactory(fac) { - cryptoStoreFactory = fac; -} -exports.setCryptoStoreFactory = setCryptoStoreFactory; -function amendClientOpts(opts) { - var _a, _b, _c; - opts.store = - (_a = opts.store) !== null && _a !== void 0 ? _a : new memory_1.MemoryStore({ - localStorage: global.localStorage, - }); - opts.scheduler = (_b = opts.scheduler) !== null && _b !== void 0 ? _b : new scheduler_1.MatrixScheduler(); - opts.cryptoStore = (_c = opts.cryptoStore) !== null && _c !== void 0 ? _c : cryptoStoreFactory(); - return opts; -} -/** - * Construct a Matrix Client. Similar to {@link MatrixClient} - * except that the 'request', 'store' and 'scheduler' dependencies are satisfied. - * @param opts - The configuration options for this client. These configuration - * options will be passed directly to {@link MatrixClient}. - * - * @returns A new matrix client. - * @see {@link MatrixClient} for the full list of options for - * `opts`. - */ -function createClient(opts) { - return new client_1.MatrixClient(amendClientOpts(opts)); -} -exports.createClient = createClient; -function createRoomWidgetClient(widgetApi, capabilities, roomId, opts) { - return new embedded_1.RoomWidgetClient(widgetApi, capabilities, roomId, amendClientOpts(opts)); -} -exports.createRoomWidgetClient = createRoomWidgetClient; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./@types/PushRules":304,"./@types/event":306,"./@types/partials":309,"./@types/requests":312,"./@types/search":313,"./autodiscovery":319,"./client":321,"./content-helpers":322,"./content-repo":323,"./crypto/store/indexeddb-crypto-store":346,"./crypto/store/memory-crypto-store":348,"./embedded":358,"./errors":359,"./filter":364,"./http-api":367,"./interactive-auth":373,"./models/beacon":378,"./models/event":383,"./models/event-timeline":382,"./models/event-timeline-set":381,"./models/poll":385,"./models/room":392,"./models/room-member":389,"./models/room-state":390,"./models/room-summary":391,"./models/user":396,"./scheduler":403,"./secret-storage":404,"./service-types":405,"./store/indexeddb":410,"./store/memory":411,"./sync-accumulator":413,"./timeline-window":415,"./webrtc/call":418,"./webrtc/groupCall":422}],376:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MSC3089Branch = void 0; -const event_1 = require("../@types/event"); -const event_timeline_1 = require("./event-timeline"); -/** - * Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) branch - a reference - * to a file (leaf) in the tree. Note that this is UNSTABLE and subject to breaking changes - * without notice. - */ -class MSC3089Branch { - constructor(client, indexEvent, directory) { - this.client = client; - this.indexEvent = indexEvent; - this.directory = directory; - // Nothing to do - } - /** - * The file ID. - */ - get id() { - const stateKey = this.indexEvent.getStateKey(); - if (!stateKey) { - throw new Error("State key not found for branch"); - } - return stateKey; - } - /** - * Whether this branch is active/valid. - */ - get isActive() { - return this.indexEvent.getContent()["active"] === true; - } - /** - * Version for the file, one-indexed. - */ - get version() { - var _a; - return (_a = this.indexEvent.getContent()["version"]) !== null && _a !== void 0 ? _a : 1; - } - get roomId() { - return this.indexEvent.getRoomId(); - } - /** - * Deletes the file from the tree, including all prior edits/versions. - * @returns Promise which resolves when complete. - */ - delete() { - return __awaiter(this, void 0, void 0, function* () { - yield this.client.sendStateEvent(this.roomId, event_1.UNSTABLE_MSC3089_BRANCH.name, {}, this.id); - yield this.client.redactEvent(this.roomId, this.id); - const nextVersion = (yield this.getVersionHistory())[1]; // [0] will be us - if (nextVersion) - yield nextVersion.delete(); // implicit recursion - }); - } - /** - * Gets the name for this file. - * @returns The name, or "Unnamed File" if unknown. - */ - getName() { - return this.indexEvent.getContent()["name"] || "Unnamed File"; - } - /** - * Sets the name for this file. - * @param name - The new name for this file. - * @returns Promise which resolves when complete. - */ - setName(name) { - return __awaiter(this, void 0, void 0, function* () { - yield this.client.sendStateEvent(this.roomId, event_1.UNSTABLE_MSC3089_BRANCH.name, Object.assign(Object.assign({}, this.indexEvent.getContent()), { name: name }), this.id); - }); - } - /** - * Gets whether or not a file is locked. - * @returns True if locked, false otherwise. - */ - isLocked() { - return this.indexEvent.getContent()["locked"] || false; - } - /** - * Sets a file as locked or unlocked. - * @param locked - True to lock the file, false otherwise. - * @returns Promise which resolves when complete. - */ - setLocked(locked) { - return __awaiter(this, void 0, void 0, function* () { - yield this.client.sendStateEvent(this.roomId, event_1.UNSTABLE_MSC3089_BRANCH.name, Object.assign(Object.assign({}, this.indexEvent.getContent()), { locked: locked }), this.id); - }); - } - /** - * Gets information about the file needed to download it. - * @returns Information about the file. - */ - getFileInfo() { - return __awaiter(this, void 0, void 0, function* () { - const event = yield this.getFileEvent(); - const file = event.getOriginalContent()["file"]; - const httpUrl = this.client.mxcUrlToHttp(file["url"]); - if (!httpUrl) { - throw new Error(`No HTTP URL available for ${file["url"]}`); - } - return { info: file, httpUrl: httpUrl }; - }); - } - /** - * Gets the event the file points to. - * @returns Promise which resolves to the file's event. - */ - getFileEvent() { - return __awaiter(this, void 0, void 0, function* () { - const room = this.client.getRoom(this.roomId); - if (!room) - throw new Error("Unknown room"); - let event = room.getUnfilteredTimelineSet().findEventById(this.id); - // keep scrolling back if needed until we find the event or reach the start of the room: - while (!event && room.getLiveTimeline().getState(event_timeline_1.EventTimeline.BACKWARDS).paginationToken) { - yield this.client.scrollback(room, 100); - event = room.getUnfilteredTimelineSet().findEventById(this.id); - } - if (!event) - throw new Error("Failed to find event"); - // Sometimes the event isn't decrypted for us, so do that. We specifically set `emit: true` - // to ensure that the relations system in the sdk will function. - yield this.client.decryptEventIfNeeded(event, { emit: true, isRetry: true }); - return event; - }); - } - /** - * Creates a new version of this file with contents in a type that is compatible with MatrixClient.uploadContent(). - * @param name - The name of the file. - * @param encryptedContents - The encrypted contents. - * @param info - The encrypted file information. - * @param additionalContent - Optional event content fields to include in the message. - * @returns Promise which resolves to the file event's sent response. - */ - createNewVersion(name, encryptedContents, info, additionalContent) { - return __awaiter(this, void 0, void 0, function* () { - const fileEventResponse = yield this.directory.createFile(name, encryptedContents, info, Object.assign(Object.assign({}, (additionalContent !== null && additionalContent !== void 0 ? additionalContent : {})), { "m.new_content": true, "m.relates_to": { - rel_type: event_1.RelationType.Replace, - event_id: this.id, - } })); - // Update the version of the new event - yield this.client.sendStateEvent(this.roomId, event_1.UNSTABLE_MSC3089_BRANCH.name, { - active: true, - name: name, - version: this.version + 1, - }, fileEventResponse["event_id"]); - // Deprecate ourselves - yield this.client.sendStateEvent(this.roomId, event_1.UNSTABLE_MSC3089_BRANCH.name, Object.assign(Object.assign({}, this.indexEvent.getContent()), { active: false }), this.id); - return fileEventResponse; - }); - } - /** - * Gets the file's version history, starting at this file. - * @returns Promise which resolves to the file's version history, with the - * first element being the current version and the last element being the first version. - */ - getVersionHistory() { - return __awaiter(this, void 0, void 0, function* () { - const fileHistory = []; - fileHistory.push(this); // start with ourselves - const room = this.client.getRoom(this.roomId); - if (!room) - throw new Error("Invalid or unknown room"); - // Clone the timeline to reverse it, getting most-recent-first ordering, hopefully - // shortening the awful loop below. Without the clone, we can unintentionally mutate - // the timeline. - const timelineEvents = [...room.getLiveTimeline().getEvents()].reverse(); - // XXX: This is a very inefficient search, but it's the best we can do with the - // relations structure we have in the SDK. As of writing, it is not worth the - // investment in improving the structure. - let childEvent; - let parentEvent = yield this.getFileEvent(); - do { - childEvent = timelineEvents.find((e) => e.replacingEventId() === parentEvent.getId()); - if (childEvent) { - const branch = this.directory.getFile(childEvent.getId()); - if (branch) { - fileHistory.push(branch); - parentEvent = childEvent; - } - else { - break; // prevent infinite loop - } - } - } while (childEvent); - return fileHistory; - }); - } -} -exports.MSC3089Branch = MSC3089Branch; - -},{"../@types/event":306,"./event-timeline":382}],377:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MSC3089TreeSpace = exports.TreePermissions = exports.DEFAULT_TREE_POWER_LEVELS_TEMPLATE = void 0; -const p_retry_1 = __importDefault(require("p-retry")); -const event_1 = require("../@types/event"); -const logger_1 = require("../logger"); -const utils_1 = require("../utils"); -const MSC3089Branch_1 = require("./MSC3089Branch"); -const megolm_1 = require("../crypto/algorithms/megolm"); -/** - * The recommended defaults for a tree space's power levels. Note that this - * is UNSTABLE and subject to breaking changes without notice. - */ -exports.DEFAULT_TREE_POWER_LEVELS_TEMPLATE = { - // Owner - invite: 100, - kick: 100, - ban: 100, - // Editor - redact: 50, - state_default: 50, - events_default: 50, - // Viewer - users_default: 0, - // Mixed - events: { - [event_1.EventType.RoomPowerLevels]: 100, - [event_1.EventType.RoomHistoryVisibility]: 100, - [event_1.EventType.RoomTombstone]: 100, - [event_1.EventType.RoomEncryption]: 100, - [event_1.EventType.RoomName]: 50, - [event_1.EventType.RoomMessage]: 50, - [event_1.EventType.RoomMessageEncrypted]: 50, - [event_1.EventType.Sticker]: 50, - }, - users: {}, // defined by calling code -}; -/** - * Ease-of-use representation for power levels represented as simple roles. - * Note that this is UNSTABLE and subject to breaking changes without notice. - */ -var TreePermissions; -(function (TreePermissions) { - TreePermissions["Viewer"] = "viewer"; - TreePermissions["Editor"] = "editor"; - TreePermissions["Owner"] = "owner"; -})(TreePermissions = exports.TreePermissions || (exports.TreePermissions = {})); -/** - * Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) - * file tree Space. Note that this is UNSTABLE and subject to breaking changes - * without notice. - */ -class MSC3089TreeSpace { - constructor(client, roomId) { - this.client = client; - this.roomId = roomId; - this.room = this.client.getRoom(this.roomId); - if (!this.room) - throw new Error("Unknown room"); - } - /** - * Syntactic sugar for room ID of the Space. - */ - get id() { - return this.roomId; - } - /** - * Whether or not this is a top level space. - */ - get isTopLevel() { - // XXX: This is absolutely not how you find out if the space is top level - // but is safe for a managed usecase like we offer in the SDK. - const parentEvents = this.room.currentState.getStateEvents(event_1.EventType.SpaceParent); - if (!(parentEvents === null || parentEvents === void 0 ? void 0 : parentEvents.length)) - return true; - return parentEvents.every((e) => { var _a; return !((_a = e.getContent()) === null || _a === void 0 ? void 0 : _a["via"]); }); - } - /** - * Sets the name of the tree space. - * @param name - The new name for the space. - * @returns Promise which resolves when complete. - */ - setName(name) { - return __awaiter(this, void 0, void 0, function* () { - yield this.client.sendStateEvent(this.roomId, event_1.EventType.RoomName, { name }, ""); - }); - } - /** - * Invites a user to the tree space. They will be given the default Viewer - * permission level unless specified elsewhere. - * @param userId - The user ID to invite. - * @param andSubspaces - True (default) to invite the user to all - * directories/subspaces too, recursively. - * @param shareHistoryKeys - True (default) to share encryption keys - * with the invited user. This will allow them to decrypt the events (files) - * in the tree. Keys will not be shared if the room is lacking appropriate - * history visibility (by default, history visibility is "shared" in trees, - * which is an appropriate visibility for these purposes). - * @returns Promise which resolves when complete. - */ - invite(userId, andSubspaces = true, shareHistoryKeys = true) { - return __awaiter(this, void 0, void 0, function* () { - const promises = [this.retryInvite(userId)]; - if (andSubspaces) { - promises.push(...this.getDirectories().map((d) => d.invite(userId, andSubspaces, shareHistoryKeys))); - } - return Promise.all(promises).then(() => { - // Note: key sharing is default on because for file trees it is relatively important that the invite - // target can actually decrypt the files. The implied use case is that by inviting a user to the tree - // it means the sender would like the receiver to view/download the files contained within, much like - // sharing a folder in other circles. - if (shareHistoryKeys && (0, megolm_1.isRoomSharedHistory)(this.room)) { - // noinspection JSIgnoredPromiseFromCall - we aren't concerned as much if this fails. - this.client.sendSharedHistoryKeys(this.roomId, [userId]); - } - }); - }); - } - retryInvite(userId) { - return (0, utils_1.simpleRetryOperation)(() => __awaiter(this, void 0, void 0, function* () { - yield this.client.invite(this.roomId, userId).catch((e) => { - // We don't want to retry permission errors forever... - if ((e === null || e === void 0 ? void 0 : e.errcode) === "M_FORBIDDEN") { - throw new p_retry_1.default.AbortError(e); - } - throw e; - }); - })); - } - /** - * Sets the permissions of a user to the given role. Note that if setting a user - * to Owner then they will NOT be able to be demoted. If the user does not have - * permission to change the power level of the target, an error will be thrown. - * @param userId - The user ID to change the role of. - * @param role - The role to assign. - * @returns Promise which resolves when complete. - */ - setPermissions(userId, role) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const currentPls = this.room.currentState.getStateEvents(event_1.EventType.RoomPowerLevels, ""); - if (Array.isArray(currentPls)) - throw new Error("Unexpected return type for power levels"); - const pls = (currentPls === null || currentPls === void 0 ? void 0 : currentPls.getContent()) || {}; - const viewLevel = pls["users_default"] || 0; - const editLevel = pls["events_default"] || 50; - const adminLevel = ((_a = pls["events"]) === null || _a === void 0 ? void 0 : _a[event_1.EventType.RoomPowerLevels]) || 100; - const users = pls["users"] || {}; - switch (role) { - case TreePermissions.Viewer: - users[userId] = viewLevel; - break; - case TreePermissions.Editor: - users[userId] = editLevel; - break; - case TreePermissions.Owner: - users[userId] = adminLevel; - break; - default: - throw new Error("Invalid role: " + role); - } - pls["users"] = users; - yield this.client.sendStateEvent(this.roomId, event_1.EventType.RoomPowerLevels, pls, ""); - }); - } - /** - * Gets the current permissions of a user. Note that any users missing explicit permissions (or not - * in the space) will be considered Viewers. Appropriate membership checks need to be performed - * elsewhere. - * @param userId - The user ID to check permissions of. - * @returns The permissions for the user, defaulting to Viewer. - */ - getPermissions(userId) { - var _a, _b; - const currentPls = this.room.currentState.getStateEvents(event_1.EventType.RoomPowerLevels, ""); - if (Array.isArray(currentPls)) - throw new Error("Unexpected return type for power levels"); - const pls = (currentPls === null || currentPls === void 0 ? void 0 : currentPls.getContent()) || {}; - const viewLevel = pls["users_default"] || 0; - const editLevel = pls["events_default"] || 50; - const adminLevel = ((_a = pls["events"]) === null || _a === void 0 ? void 0 : _a[event_1.EventType.RoomPowerLevels]) || 100; - const userLevel = ((_b = pls["users"]) === null || _b === void 0 ? void 0 : _b[userId]) || viewLevel; - if (userLevel >= adminLevel) - return TreePermissions.Owner; - if (userLevel >= editLevel) - return TreePermissions.Editor; - return TreePermissions.Viewer; - } - /** - * Creates a directory under this tree space, represented as another tree space. - * @param name - The name for the directory. - * @returns Promise which resolves to the created directory. - */ - createDirectory(name) { - return __awaiter(this, void 0, void 0, function* () { - const directory = yield this.client.unstableCreateFileTree(name); - yield this.client.sendStateEvent(this.roomId, event_1.EventType.SpaceChild, { - via: [this.client.getDomain()], - }, directory.roomId); - yield this.client.sendStateEvent(directory.roomId, event_1.EventType.SpaceParent, { - via: [this.client.getDomain()], - }, this.roomId); - return directory; - }); - } - /** - * Gets a list of all known immediate subdirectories to this tree space. - * @returns The tree spaces (directories). May be empty, but not null. - */ - getDirectories() { - const trees = []; - const children = this.room.currentState.getStateEvents(event_1.EventType.SpaceChild); - for (const child of children) { - try { - const stateKey = child.getStateKey(); - if (stateKey) { - const tree = this.client.unstableGetFileTreeSpace(stateKey); - if (tree) - trees.push(tree); - } - } - catch (e) { - logger_1.logger.warn("Unable to create tree space instance for listing. Are we joined?", e); - } - } - return trees; - } - /** - * Gets a subdirectory of a given ID under this tree space. Note that this will not recurse - * into children and instead only look one level deep. - * @param roomId - The room ID (directory ID) to find. - * @returns The directory, or undefined if not found. - */ - getDirectory(roomId) { - return this.getDirectories().find((r) => r.roomId === roomId); - } - /** - * Deletes the tree, kicking all members and deleting **all subdirectories**. - * @returns Promise which resolves when complete. - */ - delete() { - return __awaiter(this, void 0, void 0, function* () { - const subdirectories = this.getDirectories(); - for (const dir of subdirectories) { - yield dir.delete(); - } - const kickMemberships = ["invite", "knock", "join"]; - const members = this.room.currentState.getStateEvents(event_1.EventType.RoomMember); - for (const member of members) { - const isNotUs = member.getStateKey() !== this.client.getUserId(); - if (isNotUs && kickMemberships.includes(member.getContent().membership)) { - const stateKey = member.getStateKey(); - if (!stateKey) { - throw new Error("State key not found for branch"); - } - yield this.client.kick(this.roomId, stateKey, "Room deleted"); - } - } - yield this.client.leave(this.roomId); - }); - } - getOrderedChildren(children) { - const ordered = children - .map((c) => ({ roomId: c.getStateKey(), order: c.getContent()["order"] })) - .filter((c) => c.roomId); - ordered.sort((a, b) => { - var _a, _b, _c, _d; - if (a.order && !b.order) { - return -1; - } - else if (!a.order && b.order) { - return 1; - } - else if (!a.order && !b.order) { - const roomA = this.client.getRoom(a.roomId); - const roomB = this.client.getRoom(b.roomId); - if (!roomA || !roomB) { - // just don't bother trying to do more partial sorting - return (0, utils_1.lexicographicCompare)(a.roomId, b.roomId); - } - const createTsA = (_b = (_a = roomA.currentState.getStateEvents(event_1.EventType.RoomCreate, "")) === null || _a === void 0 ? void 0 : _a.getTs()) !== null && _b !== void 0 ? _b : 0; - const createTsB = (_d = (_c = roomB.currentState.getStateEvents(event_1.EventType.RoomCreate, "")) === null || _c === void 0 ? void 0 : _c.getTs()) !== null && _d !== void 0 ? _d : 0; - if (createTsA === createTsB) { - return (0, utils_1.lexicographicCompare)(a.roomId, b.roomId); - } - return createTsA - createTsB; - } - else { - // both not-null orders - return (0, utils_1.lexicographicCompare)(a.order, b.order); - } - }); - return ordered; - } - getParentRoom() { - const parents = this.room.currentState.getStateEvents(event_1.EventType.SpaceParent); - const parent = parents[0]; // XXX: Wild assumption - if (!parent) - throw new Error("Expected to have a parent in a non-top level space"); - // XXX: We are assuming the parent is a valid tree space. - // We probably don't need to validate the parent room state for this usecase though. - const stateKey = parent.getStateKey(); - if (!stateKey) - throw new Error("No state key found for parent"); - const parentRoom = this.client.getRoom(stateKey); - if (!parentRoom) - throw new Error("Unable to locate room for parent"); - return parentRoom; - } - /** - * Gets the current order index for this directory. Note that if this is the top level space - * then -1 will be returned. - * @returns The order index of this space. - */ - getOrder() { - if (this.isTopLevel) - return -1; - const parentRoom = this.getParentRoom(); - const children = parentRoom.currentState.getStateEvents(event_1.EventType.SpaceChild); - const ordered = this.getOrderedChildren(children); - return ordered.findIndex((c) => c.roomId === this.roomId); - } - /** - * Sets the order index for this directory within its parent. Note that if this is a top level - * space then an error will be thrown. -1 can be used to move the child to the start, and numbers - * larger than the number of children can be used to move the child to the end. - * @param index - The new order index for this space. - * @returns Promise which resolves when complete. - * @throws Throws if this is a top level space. - */ - setOrder(index) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - if (this.isTopLevel) - throw new Error("Cannot set order of top level spaces currently"); - const parentRoom = this.getParentRoom(); - const children = parentRoom.currentState.getStateEvents(event_1.EventType.SpaceChild); - const ordered = this.getOrderedChildren(children); - index = Math.max(Math.min(index, ordered.length - 1), 0); - const currentIndex = this.getOrder(); - const movingUp = currentIndex < index; - if (movingUp && index === ordered.length - 1) { - index--; - } - else if (!movingUp && index === 0) { - index++; - } - const prev = ordered[movingUp ? index : index - 1]; - const next = ordered[movingUp ? index + 1 : index]; - let newOrder = utils_1.DEFAULT_ALPHABET[0]; - let ensureBeforeIsSane = false; - if (!prev) { - // Move to front - if (next === null || next === void 0 ? void 0 : next.order) { - newOrder = (0, utils_1.prevString)(next.order); - } - } - else if (index === ordered.length - 1) { - // Move to back - if (next === null || next === void 0 ? void 0 : next.order) { - newOrder = (0, utils_1.nextString)(next.order); - } - } - else { - // Move somewhere in the middle - const startOrder = prev === null || prev === void 0 ? void 0 : prev.order; - const endOrder = next === null || next === void 0 ? void 0 : next.order; - if (startOrder && endOrder) { - if (startOrder === endOrder) { - // Error case: just move +1 to break out of awful math - newOrder = (0, utils_1.nextString)(startOrder); - } - else { - newOrder = (0, utils_1.averageBetweenStrings)(startOrder, endOrder); - } - } - else { - if (startOrder) { - // We're at the end (endOrder is null, so no explicit order) - newOrder = (0, utils_1.nextString)(startOrder); - } - else if (endOrder) { - // We're at the start (startOrder is null, so nothing before us) - newOrder = (0, utils_1.prevString)(endOrder); - } - else { - // Both points are unknown. We're likely in a range where all the children - // don't have particular order values, so we may need to update them too. - // The other possibility is there's only us as a child, but we should have - // shown up in the other states. - ensureBeforeIsSane = true; - } - } - } - if (ensureBeforeIsSane) { - // We were asked by the order algorithm to prepare the moving space for a landing - // in the undefined order part of the order array, which means we need to update the - // spaces that come before it with a stable order value. - let lastOrder; - for (let i = 0; i <= index; i++) { - const target = ordered[i]; - if (i === 0) { - lastOrder = target.order; - } - if (!target.order) { - // XXX: We should be creating gaps to avoid conflicts - lastOrder = lastOrder ? (0, utils_1.nextString)(lastOrder) : utils_1.DEFAULT_ALPHABET[0]; - const currentChild = parentRoom.currentState.getStateEvents(event_1.EventType.SpaceChild, target.roomId); - const content = (_a = currentChild === null || currentChild === void 0 ? void 0 : currentChild.getContent()) !== null && _a !== void 0 ? _a : { via: [this.client.getDomain()] }; - yield this.client.sendStateEvent(parentRoom.roomId, event_1.EventType.SpaceChild, Object.assign(Object.assign({}, content), { order: lastOrder }), target.roomId); - } - else { - lastOrder = target.order; - } - } - if (lastOrder) { - newOrder = (0, utils_1.nextString)(lastOrder); - } - } - // TODO: Deal with order conflicts by reordering - // Now we can finally update our own order state - const currentChild = parentRoom.currentState.getStateEvents(event_1.EventType.SpaceChild, this.roomId); - const content = (_b = currentChild === null || currentChild === void 0 ? void 0 : currentChild.getContent()) !== null && _b !== void 0 ? _b : { via: [this.client.getDomain()] }; - yield this.client.sendStateEvent(parentRoom.roomId, event_1.EventType.SpaceChild, Object.assign(Object.assign({}, content), { - // TODO: Safely constrain to 50 character limit required by spaces. - order: newOrder }), this.roomId); - }); - } - /** - * Creates (uploads) a new file to this tree. The file must have already been encrypted for the room. - * The file contents are in a type that is compatible with MatrixClient.uploadContent(). - * @param name - The name of the file. - * @param encryptedContents - The encrypted contents. - * @param info - The encrypted file information. - * @param additionalContent - Optional event content fields to include in the message. - * @returns Promise which resolves to the file event's sent response. - */ - createFile(name, encryptedContents, info, additionalContent) { - return __awaiter(this, void 0, void 0, function* () { - const { content_uri: mxc } = yield this.client.uploadContent(encryptedContents, { - includeFilename: false, - }); - info.url = mxc; - const fileContent = { - msgtype: event_1.MsgType.File, - body: name, - url: mxc, - file: info, - }; - additionalContent = additionalContent !== null && additionalContent !== void 0 ? additionalContent : {}; - if (additionalContent["m.new_content"]) { - // We do the right thing according to the spec, but due to how relations are - // handled we also end up duplicating this information to the regular `content` - // as well. - additionalContent["m.new_content"] = fileContent; - } - const res = yield this.client.sendMessage(this.roomId, Object.assign(Object.assign(Object.assign({}, additionalContent), fileContent), { [event_1.UNSTABLE_MSC3089_LEAF.name]: {} })); - yield this.client.sendStateEvent(this.roomId, event_1.UNSTABLE_MSC3089_BRANCH.name, { - active: true, - name: name, - }, res["event_id"]); - return res; - }); - } - /** - * Retrieves a file from the tree. - * @param fileEventId - The event ID of the file. - * @returns The file, or null if not found. - */ - getFile(fileEventId) { - const branch = this.room.currentState.getStateEvents(event_1.UNSTABLE_MSC3089_BRANCH.name, fileEventId); - return branch ? new MSC3089Branch_1.MSC3089Branch(this.client, branch, this) : null; - } - /** - * Gets an array of all known files for the tree. - * @returns The known files. May be empty, but not null. - */ - listFiles() { - return this.listAllFiles().filter((b) => b.isActive); - } - /** - * Gets an array of all known files for the tree, including inactive/invalid ones. - * @returns The known files. May be empty, but not null. - */ - listAllFiles() { - var _a; - const branches = (_a = this.room.currentState.getStateEvents(event_1.UNSTABLE_MSC3089_BRANCH.name)) !== null && _a !== void 0 ? _a : []; - return branches.map((e) => new MSC3089Branch_1.MSC3089Branch(this.client, e, this)); - } -} -exports.MSC3089TreeSpace = MSC3089TreeSpace; - -},{"../@types/event":306,"../crypto/algorithms/megolm":334,"../logger":374,"../utils":416,"./MSC3089Branch":376,"p-retry":225}],378:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Beacon = exports.getBeaconInfoIdentifier = exports.isTimestampInDuration = exports.BeaconEvent = void 0; -const content_helpers_1 = require("../content-helpers"); -const utils_1 = require("../utils"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -var BeaconEvent; -(function (BeaconEvent) { - BeaconEvent["New"] = "Beacon.new"; - BeaconEvent["Update"] = "Beacon.update"; - BeaconEvent["LivenessChange"] = "Beacon.LivenessChange"; - BeaconEvent["Destroy"] = "Beacon.Destroy"; - BeaconEvent["LocationUpdate"] = "Beacon.LocationUpdate"; -})(BeaconEvent = exports.BeaconEvent || (exports.BeaconEvent = {})); -const isTimestampInDuration = (startTimestamp, durationMs, timestamp) => timestamp >= startTimestamp && startTimestamp + durationMs >= timestamp; -exports.isTimestampInDuration = isTimestampInDuration; -const getBeaconInfoIdentifier = (event) => `${event.getRoomId()}_${event.getStateKey()}`; -exports.getBeaconInfoIdentifier = getBeaconInfoIdentifier; -// https://github.com/matrix-org/matrix-spec-proposals/pull/3672 -class Beacon extends typed_event_emitter_1.TypedEventEmitter { - constructor(rootEvent) { - super(); - this.rootEvent = rootEvent; - this.clearLatestLocation = () => { - this._latestLocationEvent = undefined; - this.emit(BeaconEvent.LocationUpdate, this.latestLocationState); - }; - this.roomId = this.rootEvent.getRoomId(); - this.setBeaconInfo(this.rootEvent); - } - get isLive() { - return !!this._isLive; - } - get identifier() { - return (0, exports.getBeaconInfoIdentifier)(this.rootEvent); - } - get beaconInfoId() { - return this.rootEvent.getId(); - } - get beaconInfoOwner() { - return this.rootEvent.getStateKey(); - } - get beaconInfoEventType() { - return this.rootEvent.getType(); - } - get beaconInfo() { - return this._beaconInfo; - } - get latestLocationState() { - return this._latestLocationEvent && (0, content_helpers_1.parseBeaconContent)(this._latestLocationEvent.getContent()); - } - get latestLocationEvent() { - return this._latestLocationEvent; - } - update(beaconInfoEvent) { - if ((0, exports.getBeaconInfoIdentifier)(beaconInfoEvent) !== this.identifier) { - throw new Error("Invalid updating event"); - } - // don't update beacon with an older event - if (beaconInfoEvent.getTs() < this.rootEvent.getTs()) { - return; - } - this.rootEvent = beaconInfoEvent; - this.setBeaconInfo(this.rootEvent); - this.emit(BeaconEvent.Update, beaconInfoEvent, this); - this.clearLatestLocation(); - } - destroy() { - if (this.livenessWatchTimeout) { - clearTimeout(this.livenessWatchTimeout); - } - this._isLive = false; - this.emit(BeaconEvent.Destroy, this.identifier); - } - /** - * Monitor liveness of a beacon - * Emits BeaconEvent.LivenessChange when beacon expires - */ - monitorLiveness() { - if (this.livenessWatchTimeout) { - clearTimeout(this.livenessWatchTimeout); - } - this.checkLiveness(); - if (!this.beaconInfo) - return; - if (this.isLive) { - const expiryInMs = this.beaconInfo.timestamp + this.beaconInfo.timeout - Date.now(); - if (expiryInMs > 1) { - this.livenessWatchTimeout = setTimeout(() => { - this.monitorLiveness(); - }, expiryInMs); - } - } - else if (this.beaconInfo.timestamp > Date.now()) { - // beacon start timestamp is in the future - // check liveness again then - this.livenessWatchTimeout = setTimeout(() => { - this.monitorLiveness(); - }, this.beaconInfo.timestamp - Date.now()); - } - } - /** - * Process Beacon locations - * Emits BeaconEvent.LocationUpdate - */ - addLocations(beaconLocationEvents) { - var _a; - // discard locations for beacons that are not live - if (!this.isLive) { - return; - } - const validLocationEvents = beaconLocationEvents.filter((event) => { - const content = event.getContent(); - const parsed = (0, content_helpers_1.parseBeaconContent)(content); - if (!parsed.uri || !parsed.timestamp) - return false; // we won't be able to process these - const { timestamp } = parsed; - return (this._beaconInfo.timestamp && - // only include positions that were taken inside the beacon's live period - (0, exports.isTimestampInDuration)(this._beaconInfo.timestamp, this._beaconInfo.timeout, timestamp) && - // ignore positions older than our current latest location - (!this.latestLocationState || timestamp > this.latestLocationState.timestamp)); - }); - const latestLocationEvent = (_a = validLocationEvents.sort(utils_1.sortEventsByLatestContentTimestamp)) === null || _a === void 0 ? void 0 : _a[0]; - if (latestLocationEvent) { - this._latestLocationEvent = latestLocationEvent; - this.emit(BeaconEvent.LocationUpdate, this.latestLocationState); - } - } - setBeaconInfo(event) { - this._beaconInfo = (0, content_helpers_1.parseBeaconInfoContent)(event.getContent()); - this.checkLiveness(); - } - checkLiveness() { - const prevLiveness = this.isLive; - // element web sets a beacon's start timestamp to the senders local current time - // when Alice's system clock deviates slightly from Bob's a beacon Alice intended to be live - // may have a start timestamp in the future from Bob's POV - // handle this by adding 6min of leniency to the start timestamp when it is in the future - if (!this.beaconInfo) - return; - const startTimestamp = this.beaconInfo.timestamp > Date.now() - ? this.beaconInfo.timestamp - 360000 /* 6min */ - : this.beaconInfo.timestamp; - this._isLive = - !!this._beaconInfo.live && - !!startTimestamp && - (0, exports.isTimestampInDuration)(startTimestamp, this._beaconInfo.timeout, Date.now()); - if (prevLiveness !== this.isLive) { - this.emit(BeaconEvent.LivenessChange, this.isLive, this); - } - } -} -exports.Beacon = Beacon; - -},{"../content-helpers":322,"../utils":416,"./typed-event-emitter":395}],379:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventContext = void 0; -const event_timeline_1 = require("./event-timeline"); -class EventContext { - /** - * Construct a new EventContext - * - * An eventcontext is used for circumstances such as search results, when we - * have a particular event of interest, and a bunch of events before and after - * it. - * - * It also stores pagination tokens for going backwards and forwards in the - * timeline. - * - * @param ourEvent - the event at the centre of this context - */ - constructor(ourEvent) { - this.ourEvent = ourEvent; - this.ourEventIndex = 0; - this.paginateTokens = { - [event_timeline_1.Direction.Backward]: null, - [event_timeline_1.Direction.Forward]: null, - }; - this.timeline = [ourEvent]; - } - /** - * Get the main event of interest - * - * This is a convenience function for getTimeline()[getOurEventIndex()]. - * - * @returns The event at the centre of this context. - */ - getEvent() { - return this.timeline[this.ourEventIndex]; - } - /** - * Get the list of events in this context - * - * @returns An array of MatrixEvents - */ - getTimeline() { - return this.timeline; - } - /** - * Get the index in the timeline of our event - */ - getOurEventIndex() { - return this.ourEventIndex; - } - /** - * Get a pagination token. - * - * @param backwards - true to get the pagination token for going - */ - getPaginateToken(backwards = false) { - return this.paginateTokens[backwards ? event_timeline_1.Direction.Backward : event_timeline_1.Direction.Forward]; - } - /** - * Set a pagination token. - * - * Generally this will be used only by the matrix js sdk. - * - * @param token - pagination token - * @param backwards - true to set the pagination token for going - * backwards in time - */ - setPaginateToken(token, backwards = false) { - this.paginateTokens[backwards ? event_timeline_1.Direction.Backward : event_timeline_1.Direction.Forward] = token !== null && token !== void 0 ? token : null; - } - /** - * Add more events to the timeline - * - * @param events - new events, in timeline order - * @param atStart - true to insert new events at the start - */ - addEvents(events, atStart = false) { - // TODO: should we share logic with Room.addEventsToTimeline? - // Should Room even use EventContext? - if (atStart) { - this.timeline = events.concat(this.timeline); - this.ourEventIndex += events.length; - } - else { - this.timeline = this.timeline.concat(events); - } - } -} -exports.EventContext = EventContext; - -},{"./event-timeline":382}],380:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventStatus = void 0; -/** - * Enum for event statuses. - * @readonly - */ -var EventStatus; -(function (EventStatus) { - /** The event was not sent and will no longer be retried. */ - EventStatus["NOT_SENT"] = "not_sent"; - /** The message is being encrypted */ - EventStatus["ENCRYPTING"] = "encrypting"; - /** The event is in the process of being sent. */ - EventStatus["SENDING"] = "sending"; - /** The event is in a queue waiting to be sent. */ - EventStatus["QUEUED"] = "queued"; - /** The event has been sent to the server, but we have not yet received the echo. */ - EventStatus["SENT"] = "sent"; - /** The event was cancelled before it was successfully sent. */ - EventStatus["CANCELLED"] = "cancelled"; -})(EventStatus = exports.EventStatus || (exports.EventStatus = {})); - -},{}],381:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventTimelineSet = exports.DuplicateStrategy = void 0; -const event_timeline_1 = require("./event-timeline"); -const logger_1 = require("../logger"); -const room_1 = require("./room"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -const relations_container_1 = require("./relations-container"); -const DEBUG = true; -/* istanbul ignore next */ -let debuglog; -if (DEBUG) { - // using bind means that we get to keep useful line numbers in the console - debuglog = logger_1.logger.log.bind(logger_1.logger); -} -else { - /* istanbul ignore next */ - debuglog = function () { }; -} -var DuplicateStrategy; -(function (DuplicateStrategy) { - DuplicateStrategy["Ignore"] = "ignore"; - DuplicateStrategy["Replace"] = "replace"; -})(DuplicateStrategy = exports.DuplicateStrategy || (exports.DuplicateStrategy = {})); -class EventTimelineSet extends typed_event_emitter_1.TypedEventEmitter { - /** - * Construct a set of EventTimeline objects, typically on behalf of a given - * room. A room may have multiple EventTimelineSets for different levels - * of filtering. The global notification list is also an EventTimelineSet, but - * lacks a room. - * - *

This is an ordered sequence of timelines, which may or may not - * be continuous. Each timeline lists a series of events, as well as tracking - * the room state at the start and the end of the timeline (if appropriate). - * It also tracks forward and backward pagination tokens, as well as containing - * links to the next timeline in the sequence. - * - *

There is one special timeline - the 'live' timeline, which represents the - * timeline to which events are being added in real-time as they are received - * from the /sync API. Note that you should not retain references to this - * timeline - even if it is the current timeline right now, it may not remain - * so if the server gives us a timeline gap in /sync. - * - *

In order that we can find events from their ids later, we also maintain a - * map from event_id to timeline and index. - * - * @param room - Room for this timelineSet. May be null for non-room cases, such as the - * notification timeline. - * @param opts - Options inherited from Room. - * @param client - the Matrix client which owns this EventTimelineSet, - * can be omitted if room is specified. - * @param thread - the thread to which this timeline set relates. - * @param isThreadTimeline - Whether this timeline set relates to a thread list timeline - * (e.g., All threads or My threads) - */ - constructor(room, opts = {}, client, thread, threadListType = null) { - var _a, _b, _c; - super(); - this.room = room; - this.thread = thread; - this.threadListType = threadListType; - this._eventIdToTimeline = new Map(); - this.timelineSupport = Boolean(opts.timelineSupport); - this.liveTimeline = new event_timeline_1.EventTimeline(this); - this.displayPendingEvents = opts.pendingEvents !== false; - // just a list - *not* ordered. - this.timelines = [this.liveTimeline]; - this._eventIdToTimeline = new Map(); - this.filter = opts.filter; - this.relations = (_b = (_a = this.room) === null || _a === void 0 ? void 0 : _a.relations) !== null && _b !== void 0 ? _b : new relations_container_1.RelationsContainer((_c = room === null || room === void 0 ? void 0 : room.client) !== null && _c !== void 0 ? _c : client); - } - /** - * Get all the timelines in this set - * @returns the timelines in this set - */ - getTimelines() { - return this.timelines; - } - /** - * Get the filter object this timeline set is filtered on, if any - * @returns the optional filter for this timelineSet - */ - getFilter() { - return this.filter; - } - /** - * Set the filter object this timeline set is filtered on - * (passed to the server when paginating via /messages). - * @param filter - the filter for this timelineSet - */ - setFilter(filter) { - this.filter = filter; - } - /** - * Get the list of pending sent events for this timelineSet's room, filtered - * by the timelineSet's filter if appropriate. - * - * @returns A list of the sent events - * waiting for remote echo. - * - * @throws If `opts.pendingEventOrdering` was not 'detached' - */ - getPendingEvents() { - if (!this.room || !this.displayPendingEvents) { - return []; - } - return this.room.getPendingEvents(); - } - /** - * Get the live timeline for this room. - * - * @returns live timeline - */ - getLiveTimeline() { - return this.liveTimeline; - } - /** - * Set the live timeline for this room. - * - * @returns live timeline - */ - setLiveTimeline(timeline) { - this.liveTimeline = timeline; - } - /** - * Return the timeline (if any) this event is in. - * @param eventId - the eventId being sought - * @returns timeline - */ - eventIdToTimeline(eventId) { - return this._eventIdToTimeline.get(eventId); - } - /** - * Track a new event as if it were in the same timeline as an old event, - * replacing it. - * @param oldEventId - event ID of the original event - * @param newEventId - event ID of the replacement event - */ - replaceEventId(oldEventId, newEventId) { - const existingTimeline = this._eventIdToTimeline.get(oldEventId); - if (existingTimeline) { - this._eventIdToTimeline.delete(oldEventId); - this._eventIdToTimeline.set(newEventId, existingTimeline); - } - } - /** - * Reset the live timeline, and start a new one. - * - *

This is used when /sync returns a 'limited' timeline. - * - * @param backPaginationToken - token for back-paginating the new timeline - * @param forwardPaginationToken - token for forward-paginating the old live timeline, - * if absent or null, all timelines are reset. - * - * @remarks - * Fires {@link RoomEvent.TimelineReset} - */ - resetLiveTimeline(backPaginationToken, forwardPaginationToken) { - // Each EventTimeline has RoomState objects tracking the state at the start - // and end of that timeline. The copies at the end of the live timeline are - // special because they will have listeners attached to monitor changes to - // the current room state, so we move this RoomState from the end of the - // current live timeline to the end of the new one and, if necessary, - // replace it with a newly created one. We also make a copy for the start - // of the new timeline. - // if timeline support is disabled, forget about the old timelines - const resetAllTimelines = !this.timelineSupport || !forwardPaginationToken; - const oldTimeline = this.liveTimeline; - const newTimeline = resetAllTimelines - ? oldTimeline.forkLive(event_timeline_1.EventTimeline.FORWARDS) - : oldTimeline.fork(event_timeline_1.EventTimeline.FORWARDS); - if (resetAllTimelines) { - this.timelines = [newTimeline]; - this._eventIdToTimeline = new Map(); - } - else { - this.timelines.push(newTimeline); - } - if (forwardPaginationToken) { - // Now set the forward pagination token on the old live timeline - // so it can be forward-paginated. - oldTimeline.setPaginationToken(forwardPaginationToken, event_timeline_1.EventTimeline.FORWARDS); - } - // make sure we set the pagination token before firing timelineReset, - // otherwise clients which start back-paginating will fail, and then get - // stuck without realising that they *can* back-paginate. - newTimeline.setPaginationToken(backPaginationToken !== null && backPaginationToken !== void 0 ? backPaginationToken : null, event_timeline_1.EventTimeline.BACKWARDS); - // Now we can swap the live timeline to the new one. - this.liveTimeline = newTimeline; - this.emit(room_1.RoomEvent.TimelineReset, this.room, this, resetAllTimelines); - } - /** - * Get the timeline which contains the given event, if any - * - * @param eventId - event ID to look for - * @returns timeline containing - * the given event, or null if unknown - */ - getTimelineForEvent(eventId) { - if (eventId === null || eventId === undefined) { - return null; - } - const res = this._eventIdToTimeline.get(eventId); - return res === undefined ? null : res; - } - /** - * Get an event which is stored in our timelines - * - * @param eventId - event ID to look for - * @returns the given event, or undefined if unknown - */ - findEventById(eventId) { - const tl = this.getTimelineForEvent(eventId); - if (!tl) { - return undefined; - } - return tl.getEvents().find(function (ev) { - return ev.getId() == eventId; - }); - } - /** - * Add a new timeline to this timeline list - * - * @returns newly-created timeline - */ - addTimeline() { - if (!this.timelineSupport) { - throw new Error("timeline support is disabled. Set the 'timelineSupport'" + - " parameter to true when creating MatrixClient to enable" + - " it."); - } - const timeline = new event_timeline_1.EventTimeline(this); - this.timelines.push(timeline); - return timeline; - } - /** - * Add events to a timeline - * - *

Will fire "Room.timeline" for each event added. - * - * @param events - A list of events to add. - * - * @param toStartOfTimeline - True to add these events to the start - * (oldest) instead of the end (newest) of the timeline. If true, the oldest - * event will be the last element of 'events'. - * - * @param timeline - timeline to - * add events to. - * - * @param paginationToken - token for the next batch of events - * - * @remarks - * Fires {@link RoomEvent.Timeline} - * - */ - addEventsToTimeline(events, toStartOfTimeline, timeline, paginationToken) { - if (!timeline) { - throw new Error("'timeline' not specified for EventTimelineSet.addEventsToTimeline"); - } - if (!toStartOfTimeline && timeline == this.liveTimeline) { - throw new Error("EventTimelineSet.addEventsToTimeline cannot be used for adding events to " + - "the live timeline - use Room.addLiveEvents instead"); - } - if (this.filter) { - events = this.filter.filterRoomTimeline(events); - if (!events.length) { - return; - } - } - const direction = toStartOfTimeline ? event_timeline_1.EventTimeline.BACKWARDS : event_timeline_1.EventTimeline.FORWARDS; - const inverseDirection = toStartOfTimeline ? event_timeline_1.EventTimeline.FORWARDS : event_timeline_1.EventTimeline.BACKWARDS; - // Adding events to timelines can be quite complicated. The following - // illustrates some of the corner-cases. - // - // Let's say we start by knowing about four timelines. timeline3 and - // timeline4 are neighbours: - // - // timeline1 timeline2 timeline3 timeline4 - // [M] [P] [S] <------> [T] - // - // Now we paginate timeline1, and get the following events from the server: - // [M, N, P, R, S, T, U]. - // - // 1. First, we ignore event M, since we already know about it. - // - // 2. Next, we append N to timeline 1. - // - // 3. Next, we don't add event P, since we already know about it, - // but we do link together the timelines. We now have: - // - // timeline1 timeline2 timeline3 timeline4 - // [M, N] <---> [P] [S] <------> [T] - // - // 4. Now we add event R to timeline2: - // - // timeline1 timeline2 timeline3 timeline4 - // [M, N] <---> [P, R] [S] <------> [T] - // - // Note that we have switched the timeline we are working on from - // timeline1 to timeline2. - // - // 5. We ignore event S, but again join the timelines: - // - // timeline1 timeline2 timeline3 timeline4 - // [M, N] <---> [P, R] <---> [S] <------> [T] - // - // 6. We ignore event T, and the timelines are already joined, so there - // is nothing to do. - // - // 7. Finally, we add event U to timeline4: - // - // timeline1 timeline2 timeline3 timeline4 - // [M, N] <---> [P, R] <---> [S] <------> [T, U] - // - // The important thing to note in the above is what happened when we - // already knew about a given event: - // - // - if it was appropriate, we joined up the timelines (steps 3, 5). - // - in any case, we started adding further events to the timeline which - // contained the event we knew about (steps 3, 5, 6). - // - // - // So much for adding events to the timeline. But what do we want to do - // with the pagination token? - // - // In the case above, we will be given a pagination token which tells us how to - // get events beyond 'U' - in this case, it makes sense to store this - // against timeline4. But what if timeline4 already had 'U' and beyond? in - // that case, our best bet is to throw away the pagination token we were - // given and stick with whatever token timeline4 had previously. In short, - // we want to only store the pagination token if the last event we receive - // is one we didn't previously know about. - // - // We make an exception for this if it turns out that we already knew about - // *all* of the events, and we weren't able to join up any timelines. When - // that happens, it means our existing pagination token is faulty, since it - // is only telling us what we already know. Rather than repeatedly - // paginating with the same token, we might as well use the new pagination - // token in the hope that we eventually work our way out of the mess. - let didUpdate = false; - let lastEventWasNew = false; - for (const event of events) { - const eventId = event.getId(); - const existingTimeline = this._eventIdToTimeline.get(eventId); - if (!existingTimeline) { - // we don't know about this event yet. Just add it to the timeline. - this.addEventToTimeline(event, timeline, { - toStartOfTimeline, - }); - lastEventWasNew = true; - didUpdate = true; - continue; - } - lastEventWasNew = false; - if (existingTimeline == timeline) { - debuglog("Event " + eventId + " already in timeline " + timeline); - continue; - } - const neighbour = timeline.getNeighbouringTimeline(direction); - if (neighbour) { - // this timeline already has a neighbour in the relevant direction; - // let's assume the timelines are already correctly linked up, and - // skip over to it. - // - // there's probably some edge-case here where we end up with an - // event which is in a timeline a way down the chain, and there is - // a break in the chain somewhere. But I can't really imagine how - // that would happen, so I'm going to ignore it for now. - // - if (existingTimeline == neighbour) { - debuglog("Event " + eventId + " in neighbouring timeline - " + "switching to " + existingTimeline); - } - else { - debuglog("Event " + eventId + " already in a different " + "timeline " + existingTimeline); - } - timeline = existingTimeline; - continue; - } - // time to join the timelines. - logger_1.logger.info("Already have timeline for " + eventId + " - joining timeline " + timeline + " to " + existingTimeline); - // Variables to keep the line length limited below. - const existingIsLive = existingTimeline === this.liveTimeline; - const timelineIsLive = timeline === this.liveTimeline; - const backwardsIsLive = direction === event_timeline_1.EventTimeline.BACKWARDS && existingIsLive; - const forwardsIsLive = direction === event_timeline_1.EventTimeline.FORWARDS && timelineIsLive; - if (backwardsIsLive || forwardsIsLive) { - // The live timeline should never be spliced into a non-live position. - // We use independent logging to better discover the problem at a glance. - if (backwardsIsLive) { - logger_1.logger.warn("Refusing to set a preceding existingTimeLine on our " + - "timeline as the existingTimeLine is live (" + - existingTimeline + - ")"); - } - if (forwardsIsLive) { - logger_1.logger.warn("Refusing to set our preceding timeline on a existingTimeLine " + - "as our timeline is live (" + - timeline + - ")"); - } - continue; // abort splicing - try next event - } - timeline.setNeighbouringTimeline(existingTimeline, direction); - existingTimeline.setNeighbouringTimeline(timeline, inverseDirection); - timeline = existingTimeline; - didUpdate = true; - } - // see above - if the last event was new to us, or if we didn't find any - // new information, we update the pagination token for whatever - // timeline we ended up on. - if (lastEventWasNew || !didUpdate) { - if (direction === event_timeline_1.EventTimeline.FORWARDS && timeline === this.liveTimeline) { - logger_1.logger.warn({ lastEventWasNew, didUpdate }); // for debugging - logger_1.logger.warn(`Refusing to set forwards pagination token of live timeline ` + `${timeline} to ${paginationToken}`); - return; - } - timeline.setPaginationToken(paginationToken !== null && paginationToken !== void 0 ? paginationToken : null, direction); - } - } - addLiveEvent(event, duplicateStrategyOrOpts, fromCache = false, roomState) { - let duplicateStrategy = duplicateStrategyOrOpts || DuplicateStrategy.Ignore; - let timelineWasEmpty; - if (typeof duplicateStrategyOrOpts === "object") { - ({ - duplicateStrategy = DuplicateStrategy.Ignore, - fromCache = false, - roomState, - timelineWasEmpty, - } = duplicateStrategyOrOpts); - } - else if (duplicateStrategyOrOpts !== undefined) { - // Deprecation warning - // FIXME: Remove after 2023-06-01 (technical debt) - logger_1.logger.warn("Overload deprecated: " + - "`EventTimelineSet.addLiveEvent(event, duplicateStrategy?, fromCache?, roomState?)` " + - "is deprecated in favor of the overload with " + - "`EventTimelineSet.addLiveEvent(event, IAddLiveEventOptions)`"); - } - if (this.filter) { - const events = this.filter.filterRoomTimeline([event]); - if (!events.length) { - return; - } - } - const timeline = this._eventIdToTimeline.get(event.getId()); - if (timeline) { - if (duplicateStrategy === DuplicateStrategy.Replace) { - debuglog("EventTimelineSet.addLiveEvent: replacing duplicate event " + event.getId()); - const tlEvents = timeline.getEvents(); - for (let j = 0; j < tlEvents.length; j++) { - if (tlEvents[j].getId() === event.getId()) { - // still need to set the right metadata on this event - if (!roomState) { - roomState = timeline.getState(event_timeline_1.EventTimeline.FORWARDS); - } - event_timeline_1.EventTimeline.setEventMetadata(event, roomState, false); - tlEvents[j] = event; - // XXX: we need to fire an event when this happens. - break; - } - } - } - else { - debuglog("EventTimelineSet.addLiveEvent: ignoring duplicate event " + event.getId()); - } - return; - } - this.addEventToTimeline(event, this.liveTimeline, { - toStartOfTimeline: false, - fromCache, - roomState, - timelineWasEmpty, - }); - } - addEventToTimeline(event, timeline, toStartOfTimelineOrOpts, fromCache = false, roomState) { - var _a, _b; - let toStartOfTimeline = !!toStartOfTimelineOrOpts; - let timelineWasEmpty; - if (typeof toStartOfTimelineOrOpts === "object") { - ({ toStartOfTimeline, fromCache = false, roomState, timelineWasEmpty } = toStartOfTimelineOrOpts); - } - else if (toStartOfTimelineOrOpts !== undefined) { - // Deprecation warning - // FIXME: Remove after 2023-06-01 (technical debt) - logger_1.logger.warn("Overload deprecated: " + - "`EventTimelineSet.addEventToTimeline(event, timeline, toStartOfTimeline, fromCache?, roomState?)` " + - "is deprecated in favor of the overload with " + - "`EventTimelineSet.addEventToTimeline(event, timeline, IAddEventToTimelineOptions)`"); - } - if (timeline.getTimelineSet() !== this) { - throw new Error(`EventTimelineSet.addEventToTimeline: Timeline=${timeline.toString()} does not belong " + - "in timelineSet(threadId=${(_a = this.thread) === null || _a === void 0 ? void 0 : _a.id})`); - } - // Make sure events don't get mixed in timelines they shouldn't be in (e.g. a - // threaded message should not be in the main timeline). - // - // We can only run this check for timelines with a `room` because `canContain` - // requires it - if (this.room && !this.canContain(event)) { - let eventDebugString = `event=${event.getId()}`; - if (event.threadRootId) { - eventDebugString += `(belongs to thread=${event.threadRootId})`; - } - logger_1.logger.warn(`EventTimelineSet.addEventToTimeline: Ignoring ${eventDebugString} that does not belong ` + - `in timeline=${timeline.toString()} timelineSet(threadId=${(_b = this.thread) === null || _b === void 0 ? void 0 : _b.id})`); - return; - } - const eventId = event.getId(); - timeline.addEvent(event, { - toStartOfTimeline, - roomState, - timelineWasEmpty, - }); - this._eventIdToTimeline.set(eventId, timeline); - this.relations.aggregateParentEvent(event); - this.relations.aggregateChildEvent(event, this); - const data = { - timeline: timeline, - liveEvent: !toStartOfTimeline && timeline == this.liveTimeline && !fromCache, - }; - this.emit(room_1.RoomEvent.Timeline, event, this.room, Boolean(toStartOfTimeline), false, data); - } - /** - * Replaces event with ID oldEventId with one with newEventId, if oldEventId is - * recognised. Otherwise, add to the live timeline. Used to handle remote echos. - * - * @param localEvent - the new event to be added to the timeline - * @param oldEventId - the ID of the original event - * @param newEventId - the ID of the replacement event - * - * @remarks - * Fires {@link RoomEvent.Timeline} - */ - handleRemoteEcho(localEvent, oldEventId, newEventId) { - // XXX: why don't we infer newEventId from localEvent? - const existingTimeline = this._eventIdToTimeline.get(oldEventId); - if (existingTimeline) { - this._eventIdToTimeline.delete(oldEventId); - this._eventIdToTimeline.set(newEventId, existingTimeline); - } - else if (!this.filter || this.filter.filterRoomTimeline([localEvent]).length) { - this.addEventToTimeline(localEvent, this.liveTimeline, { - toStartOfTimeline: false, - }); - } - } - /** - * Removes a single event from this room. - * - * @param eventId - The id of the event to remove - * - * @returns the removed event, or null if the event was not found - * in this room. - */ - removeEvent(eventId) { - const timeline = this._eventIdToTimeline.get(eventId); - if (!timeline) { - return null; - } - const removed = timeline.removeEvent(eventId); - if (removed) { - this._eventIdToTimeline.delete(eventId); - const data = { - timeline: timeline, - }; - this.emit(room_1.RoomEvent.Timeline, removed, this.room, undefined, true, data); - } - return removed; - } - /** - * Determine where two events appear in the timeline relative to one another - * - * @param eventId1 - The id of the first event - * @param eventId2 - The id of the second event - - * @returns a number less than zero if eventId1 precedes eventId2, and - * greater than zero if eventId1 succeeds eventId2. zero if they are the - * same event; null if we can't tell (either because we don't know about one - * of the events, or because they are in separate timelines which don't join - * up). - */ - compareEventOrdering(eventId1, eventId2) { - if (eventId1 == eventId2) { - // optimise this case - return 0; - } - const timeline1 = this._eventIdToTimeline.get(eventId1); - const timeline2 = this._eventIdToTimeline.get(eventId2); - if (timeline1 === undefined) { - return null; - } - if (timeline2 === undefined) { - return null; - } - if (timeline1 === timeline2) { - // both events are in the same timeline - figure out their relative indices - let idx1 = undefined; - let idx2 = undefined; - const events = timeline1.getEvents(); - for (let idx = 0; idx < events.length && (idx1 === undefined || idx2 === undefined); idx++) { - const evId = events[idx].getId(); - if (evId == eventId1) { - idx1 = idx; - } - if (evId == eventId2) { - idx2 = idx; - } - } - return idx1 - idx2; - } - // the events are in different timelines. Iterate through the - // linkedlist to see which comes first. - // first work forwards from timeline1 - let tl = timeline1; - while (tl) { - if (tl === timeline2) { - // timeline1 is before timeline2 - return -1; - } - tl = tl.getNeighbouringTimeline(event_timeline_1.EventTimeline.FORWARDS); - } - // now try backwards from timeline1 - tl = timeline1; - while (tl) { - if (tl === timeline2) { - // timeline2 is before timeline1 - return 1; - } - tl = tl.getNeighbouringTimeline(event_timeline_1.EventTimeline.BACKWARDS); - } - // the timelines are not contiguous. - return null; - } - /** - * Determine whether a given event can sanely be added to this event timeline set, - * for timeline sets relating to a thread, only return true for events in the same - * thread timeline, for timeline sets not relating to a thread only return true - * for events which should be shown in the main room timeline. - * Requires the `room` property to have been set at EventTimelineSet construction time. - * - * @param event - the event to check whether it belongs to this timeline set. - * @throws Error if `room` was not set when constructing this timeline set. - * @returns whether the event belongs to this timeline set. - */ - canContain(event) { - if (!this.room) { - throw new Error("Cannot call `EventTimelineSet::canContain without a `room` set. " + - "Set the room when creating the EventTimelineSet to call this method."); - } - const { threadId, shouldLiveInRoom } = this.room.eventShouldLiveIn(event); - if (this.thread) { - return this.thread.id === threadId; - } - return shouldLiveInRoom; - } -} -exports.EventTimelineSet = EventTimelineSet; - -},{"../logger":374,"./event-timeline":382,"./relations-container":387,"./room":392,"./typed-event-emitter":395}],382:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventTimeline = exports.Direction = void 0; -const logger_1 = require("../logger"); -const room_state_1 = require("./room-state"); -const event_1 = require("../@types/event"); -var Direction; -(function (Direction) { - Direction["Backward"] = "b"; - Direction["Forward"] = "f"; -})(Direction = exports.Direction || (exports.Direction = {})); -class EventTimeline { - /** - * Static helper method to set sender and target properties - * - * @param event - the event whose metadata is to be set - * @param stateContext - the room state to be queried - * @param toStartOfTimeline - if true the event's forwardLooking flag is set false - */ - static setEventMetadata(event, stateContext, toStartOfTimeline) { - var _a, _b, _c, _d; - // When we try to generate a sentinel member before we have that member - // in the members object, we still generate a sentinel but it doesn't - // have a membership event, so test to see if events.member is set. We - // check this to avoid overriding non-sentinel members by sentinel ones - // when adding the event to a filtered timeline - if (!((_b = (_a = event.sender) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.member)) { - event.sender = stateContext.getSentinelMember(event.getSender()); - } - if (!((_d = (_c = event.target) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.member) && event.getType() === event_1.EventType.RoomMember) { - event.target = stateContext.getSentinelMember(event.getStateKey()); - } - if (event.isState()) { - // room state has no concept of 'old' or 'current', but we want the - // room state to regress back to previous values if toStartOfTimeline - // is set, which means inspecting prev_content if it exists. This - // is done by toggling the forwardLooking flag. - if (toStartOfTimeline) { - event.forwardLooking = false; - } - } - } - /** - * Construct a new EventTimeline - * - *

An EventTimeline represents a contiguous sequence of events in a room. - * - *

As well as keeping track of the events themselves, it stores the state of - * the room at the beginning and end of the timeline, and pagination tokens for - * going backwards and forwards in the timeline. - * - *

In order that clients can meaningfully maintain an index into a timeline, - * the EventTimeline object tracks a 'baseIndex'. This starts at zero, but is - * incremented when events are prepended to the timeline. The index of an event - * relative to baseIndex therefore remains constant. - * - *

Once a timeline joins up with its neighbour, they are linked together into a - * doubly-linked list. - * - * @param eventTimelineSet - the set of timelines this is part of - */ - constructor(eventTimelineSet) { - var _a, _b; - this.eventTimelineSet = eventTimelineSet; - this.events = []; - this.baseIndex = 0; - // If we have a roomId then we delegate pagination token storage to the room state objects `startState` and - // `endState`, but for things like the notification timeline which mix multiple rooms we store the tokens ourselves. - this.startToken = null; - this.endToken = null; - this.prevTimeline = null; - this.nextTimeline = null; - this.paginationRequests = { - [Direction.Backward]: null, - [Direction.Forward]: null, - }; - this.roomId = (_b = (_a = eventTimelineSet.room) === null || _a === void 0 ? void 0 : _a.roomId) !== null && _b !== void 0 ? _b : null; - if (this.roomId) { - this.startState = new room_state_1.RoomState(this.roomId); - this.endState = new room_state_1.RoomState(this.roomId); - } - // this is used by client.js - this.paginationRequests = { b: null, f: null }; - this.name = this.roomId + ":" + new Date().toISOString(); - } - /** - * Initialise the start and end state with the given events - * - *

This can only be called before any events are added. - * - * @param stateEvents - list of state events to initialise the - * state with. - * @throws Error if an attempt is made to call this after addEvent is called. - */ - initialiseState(stateEvents, { timelineWasEmpty } = {}) { - var _a, _b; - if (this.events.length > 0) { - throw new Error("Cannot initialise state after events are added"); - } - (_a = this.startState) === null || _a === void 0 ? void 0 : _a.setStateEvents(stateEvents, { timelineWasEmpty }); - (_b = this.endState) === null || _b === void 0 ? void 0 : _b.setStateEvents(stateEvents, { timelineWasEmpty }); - } - /** - * Forks the (live) timeline, taking ownership of the existing directional state of this timeline. - * All attached listeners will keep receiving state updates from the new live timeline state. - * The end state of this timeline gets replaced with an independent copy of the current RoomState, - * and will need a new pagination token if it ever needs to paginate forwards. - - * @param direction - EventTimeline.BACKWARDS to get the state at the - * start of the timeline; EventTimeline.FORWARDS to get the state at the end - * of the timeline. - * - * @returns the new timeline - */ - forkLive(direction) { - const forkState = this.getState(direction); - const timeline = new EventTimeline(this.eventTimelineSet); - timeline.startState = forkState === null || forkState === void 0 ? void 0 : forkState.clone(); - // Now clobber the end state of the new live timeline with that from the - // previous live timeline. It will be identical except that we'll keep - // using the same RoomMember objects for the 'live' set of members with any - // listeners still attached - timeline.endState = forkState; - // Firstly, we just stole the current timeline's end state, so it needs a new one. - // Make an immutable copy of the state so back pagination will get the correct sentinels. - this.endState = forkState === null || forkState === void 0 ? void 0 : forkState.clone(); - return timeline; - } - /** - * Creates an independent timeline, inheriting the directional state from this timeline. - * - * @param direction - EventTimeline.BACKWARDS to get the state at the - * start of the timeline; EventTimeline.FORWARDS to get the state at the end - * of the timeline. - * - * @returns the new timeline - */ - fork(direction) { - const forkState = this.getState(direction); - const timeline = new EventTimeline(this.eventTimelineSet); - timeline.startState = forkState === null || forkState === void 0 ? void 0 : forkState.clone(); - timeline.endState = forkState === null || forkState === void 0 ? void 0 : forkState.clone(); - return timeline; - } - /** - * Get the ID of the room for this timeline - * @returns room ID - */ - getRoomId() { - return this.roomId; - } - /** - * Get the filter for this timeline's timelineSet (if any) - * @returns filter - */ - getFilter() { - return this.eventTimelineSet.getFilter(); - } - /** - * Get the timelineSet for this timeline - * @returns timelineSet - */ - getTimelineSet() { - return this.eventTimelineSet; - } - /** - * Get the base index. - * - *

This is an index which is incremented when events are prepended to the - * timeline. An individual event therefore stays at the same index in the array - * relative to the base index (although note that a given event's index may - * well be less than the base index, thus giving that event a negative relative - * index). - */ - getBaseIndex() { - return this.baseIndex; - } - /** - * Get the list of events in this context - * - * @returns An array of MatrixEvents - */ - getEvents() { - return this.events; - } - /** - * Get the room state at the start/end of the timeline - * - * @param direction - EventTimeline.BACKWARDS to get the state at the - * start of the timeline; EventTimeline.FORWARDS to get the state at the end - * of the timeline. - * - * @returns state at the start/end of the timeline - */ - getState(direction) { - if (direction == EventTimeline.BACKWARDS) { - return this.startState; - } - else if (direction == EventTimeline.FORWARDS) { - return this.endState; - } - else { - throw new Error("Invalid direction '" + direction + "'"); - } - } - /** - * Get a pagination token - * - * @param direction - EventTimeline.BACKWARDS to get the pagination - * token for going backwards in time; EventTimeline.FORWARDS to get the - * pagination token for going forwards in time. - * - * @returns pagination token - */ - getPaginationToken(direction) { - if (this.roomId) { - return this.getState(direction).paginationToken; - } - else if (direction === Direction.Backward) { - return this.startToken; - } - else { - return this.endToken; - } - } - /** - * Set a pagination token - * - * @param token - pagination token - * - * @param direction - EventTimeline.BACKWARDS to set the pagination - * token for going backwards in time; EventTimeline.FORWARDS to set the - * pagination token for going forwards in time. - */ - setPaginationToken(token, direction) { - if (this.roomId) { - this.getState(direction).paginationToken = token; - } - else if (direction === Direction.Backward) { - this.startToken = token; - } - else { - this.endToken = token; - } - } - /** - * Get the next timeline in the series - * - * @param direction - EventTimeline.BACKWARDS to get the previous - * timeline; EventTimeline.FORWARDS to get the next timeline. - * - * @returns previous or following timeline, if they have been - * joined up. - */ - getNeighbouringTimeline(direction) { - if (direction == EventTimeline.BACKWARDS) { - return this.prevTimeline; - } - else if (direction == EventTimeline.FORWARDS) { - return this.nextTimeline; - } - else { - throw new Error("Invalid direction '" + direction + "'"); - } - } - /** - * Set the next timeline in the series - * - * @param neighbour - previous/following timeline - * - * @param direction - EventTimeline.BACKWARDS to set the previous - * timeline; EventTimeline.FORWARDS to set the next timeline. - * - * @throws Error if an attempt is made to set the neighbouring timeline when - * it is already set. - */ - setNeighbouringTimeline(neighbour, direction) { - if (this.getNeighbouringTimeline(direction)) { - throw new Error("timeline already has a neighbouring timeline - " + - "cannot reset neighbour (direction: " + - direction + - ")"); - } - if (direction == EventTimeline.BACKWARDS) { - this.prevTimeline = neighbour; - } - else if (direction == EventTimeline.FORWARDS) { - this.nextTimeline = neighbour; - } - else { - throw new Error("Invalid direction '" + direction + "'"); - } - // make sure we don't try to paginate this timeline - this.setPaginationToken(null, direction); - } - addEvent(event, toStartOfTimelineOrOpts, roomState) { - let toStartOfTimeline = !!toStartOfTimelineOrOpts; - let timelineWasEmpty; - if (typeof toStartOfTimelineOrOpts === "object") { - ({ toStartOfTimeline, roomState, timelineWasEmpty } = toStartOfTimelineOrOpts); - } - else if (toStartOfTimelineOrOpts !== undefined) { - // Deprecation warning - // FIXME: Remove after 2023-06-01 (technical debt) - logger_1.logger.warn("Overload deprecated: " + - "`EventTimeline.addEvent(event, toStartOfTimeline, roomState?)` " + - "is deprecated in favor of the overload with `EventTimeline.addEvent(event, IAddEventOptions)`"); - } - if (!roomState) { - roomState = toStartOfTimeline ? this.startState : this.endState; - } - const timelineSet = this.getTimelineSet(); - if (timelineSet.room) { - EventTimeline.setEventMetadata(event, roomState, toStartOfTimeline); - // modify state but only on unfiltered timelineSets - if (event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) { - roomState === null || roomState === void 0 ? void 0 : roomState.setStateEvents([event], { timelineWasEmpty }); - // it is possible that the act of setting the state event means we - // can set more metadata (specifically sender/target props), so try - // it again if the prop wasn't previously set. It may also mean that - // the sender/target is updated (if the event set was a room member event) - // so we want to use the *updated* member (new avatar/name) instead. - // - // However, we do NOT want to do this on member events if we're going - // back in time, else we'll set the .sender value for BEFORE the given - // member event, whereas we want to set the .sender value for the ACTUAL - // member event itself. - if (!event.sender || (event.getType() === event_1.EventType.RoomMember && !toStartOfTimeline)) { - EventTimeline.setEventMetadata(event, roomState, toStartOfTimeline); - } - } - } - let insertIndex; - if (toStartOfTimeline) { - insertIndex = 0; - } - else { - insertIndex = this.events.length; - } - this.events.splice(insertIndex, 0, event); // insert element - if (toStartOfTimeline) { - this.baseIndex++; - } - } - /** - * Remove an event from the timeline - * - * @param eventId - ID of event to be removed - * @returns removed event, or null if not found - */ - removeEvent(eventId) { - for (let i = this.events.length - 1; i >= 0; i--) { - const ev = this.events[i]; - if (ev.getId() == eventId) { - this.events.splice(i, 1); - if (i < this.baseIndex) { - this.baseIndex--; - } - return ev; - } - } - return null; - } - /** - * Return a string to identify this timeline, for debugging - * - * @returns name for this timeline - */ - toString() { - return this.name; - } -} -exports.EventTimeline = EventTimeline; -/** - * Symbolic constant for methods which take a 'direction' argument: - * refers to the start of the timeline, or backwards in time. - */ -EventTimeline.BACKWARDS = Direction.Backward; -/** - * Symbolic constant for methods which take a 'direction' argument: - * refers to the end of the timeline, or forwards in time. - */ -EventTimeline.FORWARDS = Direction.Forward; - -},{"../@types/event":306,"../logger":374,"./room-state":390}],383:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MatrixEvent = exports.MatrixEventEvent = exports.EventStatus = void 0; -/** - * This is an internal module. See {@link MatrixEvent} and {@link RoomEvent} for - * the public classes. - */ -const matrix_events_sdk_1 = require("matrix-events-sdk"); -const logger_1 = require("../logger"); -const event_1 = require("../@types/event"); -const utils_1 = require("../utils"); -const thread_1 = require("./thread"); -const ReEmitter_1 = require("../ReEmitter"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -const algorithms_1 = require("../crypto/algorithms"); -const OlmDevice_1 = require("../crypto/OlmDevice"); -var event_status_1 = require("./event-status"); -Object.defineProperty(exports, "EventStatus", { enumerable: true, get: function () { return event_status_1.EventStatus; } }); -// A singleton implementing `IMessageVisibilityVisible`. -const MESSAGE_VISIBLE = Object.freeze({ visible: true }); -var MatrixEventEvent; -(function (MatrixEventEvent) { - MatrixEventEvent["Decrypted"] = "Event.decrypted"; - MatrixEventEvent["BeforeRedaction"] = "Event.beforeRedaction"; - MatrixEventEvent["VisibilityChange"] = "Event.visibilityChange"; - MatrixEventEvent["LocalEventIdReplaced"] = "Event.localEventIdReplaced"; - MatrixEventEvent["Status"] = "Event.status"; - MatrixEventEvent["Replaced"] = "Event.replaced"; - MatrixEventEvent["RelationsCreated"] = "Event.relationsCreated"; -})(MatrixEventEvent = exports.MatrixEventEvent || (exports.MatrixEventEvent = {})); -class MatrixEvent extends typed_event_emitter_1.TypedEventEmitter { - /** - * Construct a Matrix Event object - * - * @param event - The raw (possibly encrypted) event. Do not access - * this property directly unless you absolutely have to. Prefer the getter - * methods defined on this class. Using the getter methods shields your app - * from changes to event JSON between Matrix versions. - */ - constructor(event = {}) { - var _a; - super(); - this.event = event; - this.pushActions = null; - this._replacingEvent = null; - this._localRedactionEvent = null; - this._isCancelled = false; - /* Message hiding, as specified by https://github.com/matrix-org/matrix-doc/pull/3531. - - Note: We're returning this object, so any value stored here MUST be frozen. - */ - this.visibility = MESSAGE_VISIBLE; - // Not all events will be extensible-event compatible, so cache a flag in - // addition to a falsy cached event value. We check the flag later on in - // a public getter to decide if the cache is valid. - this._hasCachedExtEv = false; - this._cachedExtEv = undefined; - /* curve25519 key which we believe belongs to the sender of the event. See - * getSenderKey() - */ - this.senderCurve25519Key = null; - /* ed25519 key which the sender of this event (for olm) or the creator of - * the megolm session (for megolm) claims to own. See getClaimedEd25519Key() - */ - this.claimedEd25519Key = null; - /* curve25519 keys of devices involved in telling us about the - * senderCurve25519Key and claimedEd25519Key. - * See getForwardingCurve25519KeyChain(). - */ - this.forwardingCurve25519KeyChain = []; - /* where the decryption key is untrusted - */ - this.untrusted = null; - /* if we have a process decrypting this event, a Promise which resolves - * when it is finished. Normally null. - */ - this.decryptionPromise = null; - /* flag to indicate if we should retry decrypting this event after the - * first attempt (eg, we have received new data which means that a second - * attempt may succeed) - */ - this.retryDecryption = false; - /* - * True if this event is an encrypted event which we failed to decrypt, the receiver's device is unverified and - * the sender has disabled encrypting to unverified devices. - */ - this.encryptedDisabledForUnverifiedDevices = false; - /** - * The room member who sent this event, or null e.g. - * this is a presence event. This is only guaranteed to be set for events that - * appear in a timeline, ie. do not guarantee that it will be set on state - * events. - * @privateRemarks - * Should be read-only - */ - this.sender = null; - /** - * The room member who is the target of this event, e.g. - * the invitee, the person being banned, etc. - * @privateRemarks - * Should be read-only - */ - this.target = null; - /** - * The sending status of the event. - * @privateRemarks - * Should be read-only - */ - this.status = null; - /** - * most recent error associated with sending the event, if any - * @privateRemarks - * Should be read-only - */ - this.error = null; - /** - * True if this event is 'forward looking', meaning - * that getDirectionalContent() will return event.content and not event.prev_content. - * Only state events may be backwards looking - * Default: true. This property is experimental and may change. - * @privateRemarks - * Should be read-only - */ - this.forwardLooking = true; - // intern the values of matrix events to force share strings and reduce the - // amount of needless string duplication. This can save moderate amounts of - // memory (~10% on a 350MB heap). - // 'membership' at the event level (rather than the content level) is a legacy - // field that Element never otherwise looks at, but it will still take up a lot - // of space if we don't intern it. - ["state_key", "type", "sender", "room_id", "membership"].forEach((prop) => { - if (typeof event[prop] !== "string") - return; - event[prop] = (0, utils_1.internaliseString)(event[prop]); - }); - ["membership", "avatar_url", "displayname"].forEach((prop) => { - var _a; - if (typeof ((_a = event.content) === null || _a === void 0 ? void 0 : _a[prop]) !== "string") - return; - event.content[prop] = (0, utils_1.internaliseString)(event.content[prop]); - }); - ["rel_type"].forEach((prop) => { - var _a, _b; - if (typeof ((_b = (_a = event.content) === null || _a === void 0 ? void 0 : _a["m.relates_to"]) === null || _b === void 0 ? void 0 : _b[prop]) !== "string") - return; - event.content["m.relates_to"][prop] = (0, utils_1.internaliseString)(event.content["m.relates_to"][prop]); - }); - this.txnId = event.txn_id; - this.localTimestamp = Date.now() - ((_a = this.getAge()) !== null && _a !== void 0 ? _a : 0); - this.reEmitter = new ReEmitter_1.TypedReEmitter(this); - } - /** - * Unstable getter to try and get an extensible event. Note that this might - * return a falsy value if the event could not be parsed as an extensible - * event. - * - * @deprecated Use stable functions where possible. - */ - get unstableExtensibleEvent() { - if (!this._hasCachedExtEv) { - this._cachedExtEv = matrix_events_sdk_1.ExtensibleEvents.parse(this.getEffectiveEvent()); - } - return this._cachedExtEv; - } - invalidateExtensibleEvent() { - // just reset the flag - that'll trick the getter into parsing a new event - this._hasCachedExtEv = false; - } - /** - * Gets the event as though it would appear unencrypted. If the event is already not - * encrypted, it is simply returned as-is. - * @returns The event in wire format. - */ - getEffectiveEvent() { - const content = Object.assign({}, this.getContent()); // clone for mutation - if (this.getWireType() === event_1.EventType.RoomMessageEncrypted) { - // Encrypted events sometimes aren't symmetrical on the `content` so we'll copy - // that over too, but only for missing properties. We don't copy over mismatches - // between the plain and decrypted copies of `content` because we assume that the - // app is relying on the decrypted version, so we want to expose that as a source - // of truth here too. - for (const [key, value] of Object.entries(this.getWireContent())) { - // Skip fields from the encrypted event schema though - we don't want to leak - // these. - if (["algorithm", "ciphertext", "device_id", "sender_key", "session_id"].includes(key)) { - continue; - } - if (content[key] === undefined) - content[key] = value; - } - } - // clearEvent doesn't have all the fields, so we'll copy what we can from this.event. - // We also copy over our "fixed" content key. - return Object.assign({}, this.event, this.clearEvent, { content }); - } - /** - * Get the event_id for this event. - * @returns The event ID, e.g. $143350589368169JsLZx:localhost - * - */ - getId() { - return this.event.event_id; - } - /** - * Get the user_id for this event. - * @returns The user ID, e.g. `@alice:matrix.org` - */ - getSender() { - return this.event.sender || this.event.user_id; // v2 / v1 - } - /** - * Get the (decrypted, if necessary) type of event. - * - * @returns The event type, e.g. `m.room.message` - */ - getType() { - if (this.clearEvent) { - return this.clearEvent.type; - } - return this.event.type; - } - /** - * Get the (possibly encrypted) type of the event that will be sent to the - * homeserver. - * - * @returns The event type. - */ - getWireType() { - return this.event.type; - } - /** - * Get the room_id for this event. This will return `undefined` - * for `m.presence` events. - * @returns The room ID, e.g. !cURbafjkfsMDVwdRDQ:matrix.org - * - */ - getRoomId() { - return this.event.room_id; - } - /** - * Get the timestamp of this event. - * @returns The event timestamp, e.g. `1433502692297` - */ - getTs() { - return this.event.origin_server_ts; - } - /** - * Get the timestamp of this event, as a Date object. - * @returns The event date, e.g. `new Date(1433502692297)` - */ - getDate() { - return this.event.origin_server_ts ? new Date(this.event.origin_server_ts) : null; - } - /** - * Get a string containing details of this event - * - * This is intended for logging, to help trace errors. Example output: - * - * @example - * ``` - * id=$HjnOHV646n0SjLDAqFrgIjim7RCpB7cdMXFrekWYAn type=m.room.encrypted - * sender=@user:example.com room=!room:example.com ts=2022-10-25T17:30:28.404Z - * ``` - */ - getDetails() { - let details = `id=${this.getId()} type=${this.getWireType()} sender=${this.getSender()}`; - const room = this.getRoomId(); - if (room) { - details += ` room=${room}`; - } - const date = this.getDate(); - if (date) { - details += ` ts=${date.toISOString()}`; - } - return details; - } - /** - * Get the (decrypted, if necessary) event content JSON, even if the event - * was replaced by another event. - * - * @returns The event content JSON, or an empty object. - */ - getOriginalContent() { - if (this._localRedactionEvent) { - return {}; - } - if (this.clearEvent) { - return (this.clearEvent.content || {}); - } - return (this.event.content || {}); - } - /** - * Get the (decrypted, if necessary) event content JSON, - * or the content from the replacing event, if any. - * See `makeReplaced`. - * - * @returns The event content JSON, or an empty object. - */ - getContent() { - if (this._localRedactionEvent) { - return {}; - } - else if (this._replacingEvent) { - return this._replacingEvent.getContent()["m.new_content"] || {}; - } - else { - return this.getOriginalContent(); - } - } - /** - * Get the (possibly encrypted) event content JSON that will be sent to the - * homeserver. - * - * @returns The event content JSON, or an empty object. - */ - getWireContent() { - return this.event.content || {}; - } - /** - * Get the event ID of the thread head - */ - get threadRootId() { - var _a, _b; - const relatesTo = (_a = this.getWireContent()) === null || _a === void 0 ? void 0 : _a["m.relates_to"]; - if ((relatesTo === null || relatesTo === void 0 ? void 0 : relatesTo.rel_type) === thread_1.THREAD_RELATION_TYPE.name) { - return relatesTo.event_id; - } - else { - return ((_b = this.getThread()) === null || _b === void 0 ? void 0 : _b.id) || this.threadId; - } - } - /** - * A helper to check if an event is a thread's head or not - */ - get isThreadRoot() { - var _a; - const threadDetails = this.getServerAggregatedRelation(thread_1.THREAD_RELATION_TYPE.name); - // Bundled relationships only returned when the sync response is limited - // hence us having to check both bundled relation and inspect the thread - // model - return !!threadDetails || ((_a = this.getThread()) === null || _a === void 0 ? void 0 : _a.id) === this.getId(); - } - get replyEventId() { - var _a, _b; - return (_b = (_a = this.getWireContent()["m.relates_to"]) === null || _a === void 0 ? void 0 : _a["m.in_reply_to"]) === null || _b === void 0 ? void 0 : _b.event_id; - } - get relationEventId() { - var _a, _b; - return (_b = (_a = this.getWireContent()) === null || _a === void 0 ? void 0 : _a["m.relates_to"]) === null || _b === void 0 ? void 0 : _b.event_id; - } - /** - * Get the previous event content JSON. This will only return something for - * state events which exist in the timeline. - * @returns The previous event content JSON, or an empty object. - */ - getPrevContent() { - // v2 then v1 then default - return this.getUnsigned().prev_content || this.event.prev_content || {}; - } - /** - * Get either 'content' or 'prev_content' depending on if this event is - * 'forward-looking' or not. This can be modified via event.forwardLooking. - * In practice, this means we get the chronologically earlier content value - * for this event (this method should surely be called getEarlierContent) - * This method is experimental and may change. - * @returns event.content if this event is forward-looking, else - * event.prev_content. - */ - getDirectionalContent() { - return this.forwardLooking ? this.getContent() : this.getPrevContent(); - } - /** - * Get the age of this event. This represents the age of the event when the - * event arrived at the device, and not the age of the event when this - * function was called. - * Can only be returned once the server has echo'ed back - * @returns The age of this event in milliseconds. - */ - getAge() { - return this.getUnsigned().age || this.event.age; // v2 / v1 - } - /** - * Get the age of the event when this function was called. - * This is the 'age' field adjusted according to how long this client has - * had the event. - * @returns The age of this event in milliseconds. - */ - getLocalAge() { - return Date.now() - this.localTimestamp; - } - /** - * Get the event state_key if it has one. This will return undefined - * for message events. - * @returns The event's `state_key`. - */ - getStateKey() { - return this.event.state_key; - } - /** - * Check if this event is a state event. - * @returns True if this is a state event. - */ - isState() { - return this.event.state_key !== undefined; - } - /** - * Replace the content of this event with encrypted versions. - * (This is used when sending an event; it should not be used by applications). - * - * @internal - * - * @param cryptoType - type of the encrypted event - typically - * "m.room.encrypted" - * - * @param cryptoContent - raw 'content' for the encrypted event. - * - * @param senderCurve25519Key - curve25519 key to record for the - * sender of this event. - * See {@link MatrixEvent#getSenderKey}. - * - * @param claimedEd25519Key - claimed ed25519 key to record for the - * sender if this event. - * See {@link MatrixEvent#getClaimedEd25519Key} - */ - makeEncrypted(cryptoType, cryptoContent, senderCurve25519Key, claimedEd25519Key) { - // keep the plain-text data for 'view source' - this.clearEvent = { - type: this.event.type, - content: this.event.content, - }; - this.event.type = cryptoType; - this.event.content = cryptoContent; - this.senderCurve25519Key = senderCurve25519Key; - this.claimedEd25519Key = claimedEd25519Key; - } - /** - * Check if this event is currently being decrypted. - * - * @returns True if this event is currently being decrypted, else false. - */ - isBeingDecrypted() { - return this.decryptionPromise != null; - } - getDecryptionPromise() { - return this.decryptionPromise; - } - /** - * Check if this event is an encrypted event which we failed to decrypt - * - * (This implies that we might retry decryption at some point in the future) - * - * @returns True if this event is an encrypted event which we - * couldn't decrypt. - */ - isDecryptionFailure() { - var _a, _b; - return ((_b = (_a = this.clearEvent) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.msgtype) === "m.bad.encrypted"; - } - /* - * True if this event is an encrypted event which we failed to decrypt, the receiver's device is unverified and - * the sender has disabled encrypting to unverified devices. - */ - get isEncryptedDisabledForUnverifiedDevices() { - return this.isDecryptionFailure() && this.encryptedDisabledForUnverifiedDevices; - } - shouldAttemptDecryption() { - if (this.isRedacted()) - return false; - if (this.isBeingDecrypted()) - return false; - if (this.clearEvent) - return false; - if (!this.isEncrypted()) - return false; - return true; - } - /** - * Start the process of trying to decrypt this event. - * - * (This is used within the SDK: it isn't intended for use by applications) - * - * @internal - * - * @param crypto - crypto module - * - * @returns promise which resolves (to undefined) when the decryption - * attempt is completed. - */ - attemptDecryption(crypto, options = {}) { - return __awaiter(this, void 0, void 0, function* () { - // start with a couple of sanity checks. - if (!this.isEncrypted()) { - throw new Error("Attempt to decrypt event which isn't encrypted"); - } - const alreadyDecrypted = this.clearEvent && !this.isDecryptionFailure(); - const forceRedecrypt = options.forceRedecryptIfUntrusted && this.isKeySourceUntrusted(); - if (alreadyDecrypted && !forceRedecrypt) { - // we may want to just ignore this? let's start with rejecting it. - throw new Error("Attempt to decrypt event which has already been decrypted"); - } - // if we already have a decryption attempt in progress, then it may - // fail because it was using outdated info. We now have reason to - // succeed where it failed before, but we don't want to have multiple - // attempts going at the same time, so just set a flag that says we have - // new info. - // - if (this.decryptionPromise) { - logger_1.logger.log(`Event ${this.getId()} already being decrypted; queueing a retry`); - this.retryDecryption = true; - return this.decryptionPromise; - } - this.decryptionPromise = this.decryptionLoop(crypto, options); - return this.decryptionPromise; - }); - } - /** - * Cancel any room key request for this event and resend another. - * - * @param crypto - crypto module - * @param userId - the user who received this event - * - * @returns a promise that resolves when the request is queued - */ - cancelAndResendKeyRequest(crypto, userId) { - const wireContent = this.getWireContent(); - return crypto.requestRoomKey({ - algorithm: wireContent.algorithm, - room_id: this.getRoomId(), - session_id: wireContent.session_id, - sender_key: wireContent.sender_key, - }, this.getKeyRequestRecipients(userId), true); - } - /** - * Calculate the recipients for keyshare requests. - * - * @param userId - the user who received this event. - * - * @returns array of recipients - */ - getKeyRequestRecipients(userId) { - // send the request to all of our own devices - const recipients = [ - { - userId, - deviceId: "*", - }, - ]; - return recipients; - } - decryptionLoop(crypto, options = {}) { - return __awaiter(this, void 0, void 0, function* () { - // make sure that this method never runs completely synchronously. - // (doing so would mean that we would clear decryptionPromise *before* - // it is set in attemptDecryption - and hence end up with a stuck - // `decryptionPromise`). - yield Promise.resolve(); - // eslint-disable-next-line no-constant-condition - while (true) { - this.retryDecryption = false; - let res; - let err = undefined; - try { - if (!crypto) { - res = this.badEncryptedMessage("Encryption not enabled"); - } - else { - res = yield crypto.decryptEvent(this); - if (options.isRetry === true) { - logger_1.logger.info(`Decrypted event on retry (${this.getDetails()})`); - } - } - } - catch (e) { - const detailedError = e instanceof algorithms_1.DecryptionError ? e.detailedString : String(e); - err = e; - // see if we have a retry queued. - // - // NB: make sure to keep this check in the same tick of the - // event loop as `decryptionPromise = null` below - otherwise we - // risk a race: - // - // * A: we check retryDecryption here and see that it is - // false - // * B: we get a second call to attemptDecryption, which sees - // that decryptionPromise is set so sets - // retryDecryption - // * A: we continue below, clear decryptionPromise, and - // never do the retry. - // - if (this.retryDecryption) { - // decryption error, but we have a retry queued. - logger_1.logger.log(`Error decrypting event (${this.getDetails()}), but retrying: ${detailedError}`); - continue; - } - // decryption error, no retries queued. Warn about the error and - // set it to m.bad.encrypted. - // - // the detailedString already includes the name and message of the error, and the stack isn't much use, - // so we don't bother to log `e` separately. - logger_1.logger.warn(`Error decrypting event (${this.getDetails()}): ${detailedError}`); - res = this.badEncryptedMessage(String(e)); - } - // at this point, we've either successfully decrypted the event, or have given up - // (and set res to a 'badEncryptedMessage'). Either way, we can now set the - // cleartext of the event and raise Event.decrypted. - // - // make sure we clear 'decryptionPromise' before sending the 'Event.decrypted' event, - // otherwise the app will be confused to see `isBeingDecrypted` still set when - // there isn't an `Event.decrypted` on the way. - // - // see also notes on retryDecryption above. - // - this.decryptionPromise = null; - this.retryDecryption = false; - this.setClearData(res); - // Before we emit the event, clear the push actions so that they can be recalculated - // by relevant code. We do this because the clear event has now changed, making it - // so that existing rules can be re-run over the applicable properties. Stuff like - // highlighting when the user's name is mentioned rely on this happening. We also want - // to set the push actions before emitting so that any notification listeners don't - // pick up the wrong contents. - this.setPushActions(null); - if (options.emit !== false) { - this.emit(MatrixEventEvent.Decrypted, this, err); - } - return; - } - }); - } - badEncryptedMessage(reason) { - return { - clearEvent: { - type: event_1.EventType.RoomMessage, - content: { - msgtype: "m.bad.encrypted", - body: "** Unable to decrypt: " + reason + " **", - }, - }, - encryptedDisabledForUnverifiedDevices: reason === `DecryptionError: ${OlmDevice_1.WITHHELD_MESSAGES["m.unverified"]}`, - }; - } - /** - * Update the cleartext data on this event. - * - * (This is used after decrypting an event; it should not be used by applications). - * - * @internal - * - * @param decryptionResult - the decryption result, including the plaintext and some key info - * - * @remarks - * Fires {@link MatrixEventEvent.Decrypted} - */ - setClearData(decryptionResult) { - var _a, _b; - this.clearEvent = decryptionResult.clearEvent; - this.senderCurve25519Key = (_a = decryptionResult.senderCurve25519Key) !== null && _a !== void 0 ? _a : null; - this.claimedEd25519Key = (_b = decryptionResult.claimedEd25519Key) !== null && _b !== void 0 ? _b : null; - this.forwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain || []; - this.untrusted = decryptionResult.untrusted || false; - this.encryptedDisabledForUnverifiedDevices = decryptionResult.encryptedDisabledForUnverifiedDevices || false; - this.invalidateExtensibleEvent(); - } - /** - * Gets the cleartext content for this event. If the event is not encrypted, - * or encryption has not been completed, this will return null. - * - * @returns The cleartext (decrypted) content for the event - */ - getClearContent() { - return this.clearEvent ? this.clearEvent.content : null; - } - /** - * Check if the event is encrypted. - * @returns True if this event is encrypted. - */ - isEncrypted() { - return !this.isState() && this.event.type === event_1.EventType.RoomMessageEncrypted; - } - /** - * The curve25519 key for the device that we think sent this event - * - * For an Olm-encrypted event, this is inferred directly from the DH - * exchange at the start of the session: the curve25519 key is involved in - * the DH exchange, so only a device which holds the private part of that - * key can establish such a session. - * - * For a megolm-encrypted event, it is inferred from the Olm message which - * established the megolm session - */ - getSenderKey() { - return this.senderCurve25519Key; - } - /** - * The additional keys the sender of this encrypted event claims to possess. - * - * Just a wrapper for #getClaimedEd25519Key (q.v.) - */ - getKeysClaimed() { - if (!this.claimedEd25519Key) - return {}; - return { - ed25519: this.claimedEd25519Key, - }; - } - /** - * Get the ed25519 the sender of this event claims to own. - * - * For Olm messages, this claim is encoded directly in the plaintext of the - * event itself. For megolm messages, it is implied by the m.room_key event - * which established the megolm session. - * - * Until we download the device list of the sender, it's just a claim: the - * device list gives a proof that the owner of the curve25519 key used for - * this event (and returned by #getSenderKey) also owns the ed25519 key by - * signing the public curve25519 key with the ed25519 key. - * - * In general, applications should not use this method directly, but should - * instead use MatrixClient.getEventSenderDeviceInfo. - */ - getClaimedEd25519Key() { - return this.claimedEd25519Key; - } - /** - * Get the curve25519 keys of the devices which were involved in telling us - * about the claimedEd25519Key and sender curve25519 key. - * - * Normally this will be empty, but in the case of a forwarded megolm - * session, the sender keys are sent to us by another device (the forwarding - * device), which we need to trust to do this. In that case, the result will - * be a list consisting of one entry. - * - * If the device that sent us the key (A) got it from another device which - * it wasn't prepared to vouch for (B), the result will be [A, B]. And so on. - * - * @returns base64-encoded curve25519 keys, from oldest to newest. - */ - getForwardingCurve25519KeyChain() { - return this.forwardingCurve25519KeyChain; - } - /** - * Whether the decryption key was obtained from an untrusted source. If so, - * we cannot verify the authenticity of the message. - */ - isKeySourceUntrusted() { - return !!this.untrusted; - } - getUnsigned() { - return this.event.unsigned || {}; - } - setUnsigned(unsigned) { - this.event.unsigned = unsigned; - } - unmarkLocallyRedacted() { - const value = this._localRedactionEvent; - this._localRedactionEvent = null; - if (this.event.unsigned) { - this.event.unsigned.redacted_because = undefined; - } - return !!value; - } - markLocallyRedacted(redactionEvent) { - if (this._localRedactionEvent) - return; - this.emit(MatrixEventEvent.BeforeRedaction, this, redactionEvent); - this._localRedactionEvent = redactionEvent; - if (!this.event.unsigned) { - this.event.unsigned = {}; - } - this.event.unsigned.redacted_because = redactionEvent.event; - } - /** - * Change the visibility of an event, as per https://github.com/matrix-org/matrix-doc/pull/3531 . - * - * @param visibilityChange - event holding a hide/unhide payload, or nothing - * if the event is being reset to its original visibility (presumably - * by a visibility event being redacted). - * - * @remarks - * Fires {@link MatrixEventEvent.VisibilityChange} if `visibilityEvent` - * caused a change in the actual visibility of this event, either by making it - * visible (if it was hidden), by making it hidden (if it was visible) or by - * changing the reason (if it was hidden). - */ - applyVisibilityEvent(visibilityChange) { - var _a, _b; - const visible = (_a = visibilityChange === null || visibilityChange === void 0 ? void 0 : visibilityChange.visible) !== null && _a !== void 0 ? _a : true; - const reason = (_b = visibilityChange === null || visibilityChange === void 0 ? void 0 : visibilityChange.reason) !== null && _b !== void 0 ? _b : null; - let change = false; - if (this.visibility.visible !== visible) { - change = true; - } - else if (!this.visibility.visible && this.visibility["reason"] !== reason) { - change = true; - } - if (change) { - if (visible) { - this.visibility = MESSAGE_VISIBLE; - } - else { - this.visibility = Object.freeze({ - visible: false, - reason, - }); - } - this.emit(MatrixEventEvent.VisibilityChange, this, visible); - } - } - /** - * Return instructions to display or hide the message. - * - * @returns Instructions determining whether the message - * should be displayed. - */ - messageVisibility() { - // Note: We may return `this.visibility` without fear, as - // this is a shallow frozen object. - return this.visibility; - } - /** - * Update the content of an event in the same way it would be by the server - * if it were redacted before it was sent to us - * - * @param redactionEvent - event causing the redaction - */ - makeRedacted(redactionEvent) { - // quick sanity-check - if (!redactionEvent.event) { - throw new Error("invalid redactionEvent in makeRedacted"); - } - this._localRedactionEvent = null; - this.emit(MatrixEventEvent.BeforeRedaction, this, redactionEvent); - this._replacingEvent = null; - // we attempt to replicate what we would see from the server if - // the event had been redacted before we saw it. - // - // The server removes (most of) the content of the event, and adds a - // "redacted_because" key to the unsigned section containing the - // redacted event. - if (!this.event.unsigned) { - this.event.unsigned = {}; - } - this.event.unsigned.redacted_because = redactionEvent.event; - for (const key in this.event) { - if (this.event.hasOwnProperty(key) && !REDACT_KEEP_KEYS.has(key)) { - delete this.event[key]; - } - } - // If the event is encrypted prune the decrypted bits - if (this.isEncrypted()) { - this.clearEvent = undefined; - } - const keeps = this.getType() in REDACT_KEEP_CONTENT_MAP - ? REDACT_KEEP_CONTENT_MAP[this.getType()] - : {}; - const content = this.getContent(); - for (const key in content) { - if (content.hasOwnProperty(key) && !keeps[key]) { - delete content[key]; - } - } - this.invalidateExtensibleEvent(); - } - /** - * Check if this event has been redacted - * - * @returns True if this event has been redacted - */ - isRedacted() { - return Boolean(this.getUnsigned().redacted_because); - } - /** - * Check if this event is a redaction of another event - * - * @returns True if this event is a redaction - */ - isRedaction() { - return this.getType() === event_1.EventType.RoomRedaction; - } - /** - * Return the visibility change caused by this event, - * as per https://github.com/matrix-org/matrix-doc/pull/3531. - * - * @returns If the event is a well-formed visibility change event, - * an instance of `IVisibilityChange`, otherwise `null`. - */ - asVisibilityChange() { - if (!event_1.EVENT_VISIBILITY_CHANGE_TYPE.matches(this.getType())) { - // Not a visibility change event. - return null; - } - const relation = this.getRelation(); - if (!relation || relation.rel_type != "m.reference") { - // Ill-formed, ignore this event. - return null; - } - const eventId = relation.event_id; - if (!eventId) { - // Ill-formed, ignore this event. - return null; - } - const content = this.getWireContent(); - const visible = !!content.visible; - const reason = content.reason; - if (reason && typeof reason != "string") { - // Ill-formed, ignore this event. - return null; - } - // Well-formed visibility change event. - return { - visible, - reason, - eventId, - }; - } - /** - * Check if this event alters the visibility of another event, - * as per https://github.com/matrix-org/matrix-doc/pull/3531. - * - * @returns True if this event alters the visibility - * of another event. - */ - isVisibilityEvent() { - return event_1.EVENT_VISIBILITY_CHANGE_TYPE.matches(this.getType()); - } - /** - * Get the (decrypted, if necessary) redaction event JSON - * if event was redacted - * - * @returns The redaction event JSON, or an empty object - */ - getRedactionEvent() { - var _a, _b, _c, _d; - if (!this.isRedacted()) - return null; - if ((_a = this.clearEvent) === null || _a === void 0 ? void 0 : _a.unsigned) { - return (_c = (_b = this.clearEvent) === null || _b === void 0 ? void 0 : _b.unsigned.redacted_because) !== null && _c !== void 0 ? _c : null; - } - else if ((_d = this.event.unsigned) === null || _d === void 0 ? void 0 : _d.redacted_because) { - return this.event.unsigned.redacted_because; - } - else { - return {}; - } - } - /** - * Get the push actions, if known, for this event - * - * @returns push actions - */ - getPushActions() { - return this.pushActions; - } - /** - * Set the push actions for this event. - * - * @param pushActions - push actions - */ - setPushActions(pushActions) { - this.pushActions = pushActions; - } - /** - * Replace the `event` property and recalculate any properties based on it. - * @param event - the object to assign to the `event` property - */ - handleRemoteEcho(event) { - const oldUnsigned = this.getUnsigned(); - const oldId = this.getId(); - this.event = event; - // if this event was redacted before it was sent, it's locally marked as redacted. - // At this point, we've received the remote echo for the event, but not yet for - // the redaction that we are sending ourselves. Preserve the locally redacted - // state by copying over redacted_because so we don't get a flash of - // redacted, not-redacted, redacted as remote echos come in - if (oldUnsigned.redacted_because) { - if (!this.event.unsigned) { - this.event.unsigned = {}; - } - this.event.unsigned.redacted_because = oldUnsigned.redacted_because; - } - // successfully sent. - this.setStatus(null); - if (this.getId() !== oldId) { - // emit the event if it changed - this.emit(MatrixEventEvent.LocalEventIdReplaced, this); - } - this.localTimestamp = Date.now() - this.getAge(); - } - /** - * Whether the event is in any phase of sending, send failure, waiting for - * remote echo, etc. - */ - isSending() { - return !!this.status; - } - /** - * Update the event's sending status and emit an event as well. - * - * @param status - The new status - */ - setStatus(status) { - this.status = status; - this.emit(MatrixEventEvent.Status, this, status); - } - replaceLocalEventId(eventId) { - this.event.event_id = eventId; - this.emit(MatrixEventEvent.LocalEventIdReplaced, this); - } - /** - * Get whether the event is a relation event, and of a given type if - * `relType` is passed in. State events cannot be relation events - * - * @param relType - if given, checks that the relation is of the - * given type - */ - isRelation(relType) { - var _a; - // Relation info is lifted out of the encrypted content when sent to - // encrypted rooms, so we have to check `getWireContent` for this. - const relation = (_a = this.getWireContent()) === null || _a === void 0 ? void 0 : _a["m.relates_to"]; - if (this.isState() && (relation === null || relation === void 0 ? void 0 : relation.rel_type) === event_1.RelationType.Replace) { - // State events cannot be m.replace relations - return false; - } - return !!((relation === null || relation === void 0 ? void 0 : relation.rel_type) && relation.event_id && (relType ? relation.rel_type === relType : true)); - } - /** - * Get relation info for the event, if any. - */ - getRelation() { - var _a; - if (!this.isRelation()) { - return null; - } - return (_a = this.getWireContent()["m.relates_to"]) !== null && _a !== void 0 ? _a : null; - } - /** - * Set an event that replaces the content of this event, through an m.replace relation. - * - * @param newEvent - the event with the replacing content, if any. - * - * @remarks - * Fires {@link MatrixEventEvent.Replaced} - */ - makeReplaced(newEvent) { - // don't allow redacted events to be replaced. - // if newEvent is null we allow to go through though, - // as with local redaction, the replacing event might get - // cancelled, which should be reflected on the target event. - if (this.isRedacted() && newEvent) { - return; - } - // don't allow state events to be replaced using this mechanism as per MSC2676 - if (this.isState()) { - return; - } - if (this._replacingEvent !== newEvent) { - this._replacingEvent = newEvent !== null && newEvent !== void 0 ? newEvent : null; - this.emit(MatrixEventEvent.Replaced, this); - this.invalidateExtensibleEvent(); - } - } - /** - * Returns the status of any associated edit or redaction - * (not for reactions/annotations as their local echo doesn't affect the original event), - * or else the status of the event. - */ - getAssociatedStatus() { - if (this._replacingEvent) { - return this._replacingEvent.status; - } - else if (this._localRedactionEvent) { - return this._localRedactionEvent.status; - } - return this.status; - } - getServerAggregatedRelation(relType) { - var _a; - return (_a = this.getUnsigned()["m.relations"]) === null || _a === void 0 ? void 0 : _a[relType]; - } - /** - * Returns the event ID of the event replacing the content of this event, if any. - */ - replacingEventId() { - const replaceRelation = this.getServerAggregatedRelation(event_1.RelationType.Replace); - if (replaceRelation) { - return replaceRelation.event_id; - } - else if (this._replacingEvent) { - return this._replacingEvent.getId(); - } - } - /** - * Returns the event replacing the content of this event, if any. - * Replacements are aggregated on the server, so this would only - * return an event in case it came down the sync, or for local echo of edits. - */ - replacingEvent() { - return this._replacingEvent; - } - /** - * Returns the origin_server_ts of the event replacing the content of this event, if any. - */ - replacingEventDate() { - var _a; - const replaceRelation = this.getServerAggregatedRelation(event_1.RelationType.Replace); - if (replaceRelation) { - const ts = replaceRelation.origin_server_ts; - if (Number.isFinite(ts)) { - return new Date(ts); - } - } - else if (this._replacingEvent) { - return (_a = this._replacingEvent.getDate()) !== null && _a !== void 0 ? _a : undefined; - } - } - /** - * Returns the event that wants to redact this event, but hasn't been sent yet. - * @returns the event - */ - localRedactionEvent() { - return this._localRedactionEvent; - } - /** - * For relations and redactions, returns the event_id this event is referring to. - */ - getAssociatedId() { - const relation = this.getRelation(); - if (this.replyEventId) { - return this.replyEventId; - } - else if (relation) { - return relation.event_id; - } - else if (this.isRedaction()) { - return this.event.redacts; - } - } - /** - * Checks if this event is associated with another event. See `getAssociatedId`. - * @deprecated use hasAssociation instead. - */ - hasAssocation() { - return !!this.getAssociatedId(); - } - /** - * Checks if this event is associated with another event. See `getAssociatedId`. - */ - hasAssociation() { - return !!this.getAssociatedId(); - } - /** - * Update the related id with a new one. - * - * Used to replace a local id with remote one before sending - * an event with a related id. - * - * @param eventId - the new event id - */ - updateAssociatedId(eventId) { - const relation = this.getRelation(); - if (relation) { - relation.event_id = eventId; - } - else if (this.isRedaction()) { - this.event.redacts = eventId; - } - } - /** - * Flags an event as cancelled due to future conditions. For example, a verification - * request event in the same sync transaction may be flagged as cancelled to warn - * listeners that a cancellation event is coming down the same pipe shortly. - * @param cancelled - Whether the event is to be cancelled or not. - */ - flagCancelled(cancelled = true) { - this._isCancelled = cancelled; - } - /** - * Gets whether or not the event is flagged as cancelled. See flagCancelled() for - * more information. - * @returns True if the event is cancelled, false otherwise. - */ - isCancelled() { - return this._isCancelled; - } - /** - * Get a copy/snapshot of this event. The returned copy will be loosely linked - * back to this instance, though will have "frozen" event information. Other - * properties of this MatrixEvent instance will be copied verbatim, which can - * mean they are in reference to this instance despite being on the copy too. - * The reference the snapshot uses does not change, however members aside from - * the underlying event will not be deeply cloned, thus may be mutated internally. - * For example, the sender profile will be copied over at snapshot time, and - * the sender profile internally may mutate without notice to the consumer. - * - * This is meant to be used to snapshot the event details themselves, not the - * features (such as sender) surrounding the event. - * @returns A snapshot of this event. - */ - toSnapshot() { - const ev = new MatrixEvent(JSON.parse(JSON.stringify(this.event))); - for (const [p, v] of Object.entries(this)) { - if (p !== "event") { - // exclude the thing we just cloned - // @ts-ignore - XXX: this is just nasty - ev[p] = v; - } - } - return ev; - } - /** - * Determines if this event is equivalent to the given event. This only checks - * the event object itself, not the other properties of the event. Intended for - * use with toSnapshot() to identify events changing. - * @param otherEvent - The other event to check against. - * @returns True if the events are the same, false otherwise. - */ - isEquivalentTo(otherEvent) { - if (!otherEvent) - return false; - if (otherEvent === this) - return true; - const myProps = (0, utils_1.deepSortedObjectEntries)(this.event); - const theirProps = (0, utils_1.deepSortedObjectEntries)(otherEvent.event); - return JSON.stringify(myProps) === JSON.stringify(theirProps); - } - /** - * Summarise the event as JSON. This is currently used by React SDK's view - * event source feature and Seshat's event indexing, so take care when - * adjusting the output here. - * - * If encrypted, include both the decrypted and encrypted view of the event. - * - * This is named `toJSON` for use with `JSON.stringify` which checks objects - * for functions named `toJSON` and will call them to customise the output - * if they are defined. - */ - toJSON() { - const event = this.getEffectiveEvent(); - if (!this.isEncrypted()) { - return event; - } - return { - decrypted: event, - encrypted: this.event, - }; - } - setVerificationRequest(request) { - this.verificationRequest = request; - } - setTxnId(txnId) { - this.txnId = txnId; - } - getTxnId() { - return this.txnId; - } - /** - * Set the instance of a thread associated with the current event - * @param thread - the thread - */ - setThread(thread) { - if (this.thread) { - this.reEmitter.stopReEmitting(this.thread, [thread_1.ThreadEvent.Update]); - } - this.thread = thread; - this.setThreadId(thread === null || thread === void 0 ? void 0 : thread.id); - if (thread) { - this.reEmitter.reEmit(thread, [thread_1.ThreadEvent.Update]); - } - } - /** - * Get the instance of the thread associated with the current event - */ - getThread() { - return this.thread; - } - setThreadId(threadId) { - this.threadId = threadId; - } -} -exports.MatrixEvent = MatrixEvent; -/* REDACT_KEEP_KEYS gives the keys we keep when an event is redacted - * - * This is specified here: - * http://matrix.org/speculator/spec/HEAD/client_server/latest.html#redactions - * - * Also: - * - We keep 'unsigned' since that is created by the local server - * - We keep user_id for backwards-compat with v1 - */ -const REDACT_KEEP_KEYS = new Set([ - "event_id", - "type", - "room_id", - "user_id", - "sender", - "state_key", - "prev_state", - "content", - "unsigned", - "origin_server_ts", -]); -// a map from state event type to the .content keys we keep when an event is redacted -const REDACT_KEEP_CONTENT_MAP = { - [event_1.EventType.RoomMember]: { membership: 1 }, - [event_1.EventType.RoomCreate]: { creator: 1 }, - [event_1.EventType.RoomJoinRules]: { join_rule: 1 }, - [event_1.EventType.RoomPowerLevels]: { - ban: 1, - events: 1, - events_default: 1, - kick: 1, - redact: 1, - state_default: 1, - users: 1, - users_default: 1, - }, -}; - -},{"../@types/event":306,"../ReEmitter":317,"../crypto/OlmDevice":327,"../crypto/algorithms":333,"../logger":374,"../utils":416,"./event-status":380,"./thread":394,"./typed-event-emitter":395,"matrix-events-sdk":167}],384:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IgnoredInvites = exports.PolicyScope = exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY = exports.POLICIES_ACCOUNT_EVENT_TYPE = void 0; -const matrix_events_sdk_1 = require("matrix-events-sdk"); -const event_timeline_1 = require("./event-timeline"); -const partials_1 = require("../@types/partials"); -const utils_1 = require("../utils"); -/// The event type storing the user's individual policies. -/// -/// Exported for testing purposes. -exports.POLICIES_ACCOUNT_EVENT_TYPE = new matrix_events_sdk_1.UnstableValue("m.policies", "org.matrix.msc3847.policies"); -/// The key within the user's individual policies storing the user's ignored invites. -/// -/// Exported for testing purposes. -exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY = new matrix_events_sdk_1.UnstableValue("m.ignore.invites", "org.matrix.msc3847.ignore.invites"); -/// The types of recommendations understood. -var PolicyRecommendation; -(function (PolicyRecommendation) { - PolicyRecommendation["Ban"] = "m.ban"; -})(PolicyRecommendation || (PolicyRecommendation = {})); -/** - * The various scopes for policies. - */ -var PolicyScope; -(function (PolicyScope) { - /** - * The policy deals with an individual user, e.g. reject invites - * from this user. - */ - PolicyScope["User"] = "m.policy.user"; - /** - * The policy deals with a room, e.g. reject invites towards - * a specific room. - */ - PolicyScope["Room"] = "m.policy.room"; - /** - * The policy deals with a server, e.g. reject invites from - * this server. - */ - PolicyScope["Server"] = "m.policy.server"; -})(PolicyScope = exports.PolicyScope || (exports.PolicyScope = {})); -/** - * A container for ignored invites. - * - * # Performance - * - * This implementation is extremely naive. It expects that we are dealing - * with a very short list of sources (e.g. only one). If real-world - * applications turn out to require longer lists, we may need to rework - * our data structures. - */ -class IgnoredInvites { - constructor(client) { - this.client = client; - } - /** - * Add a new rule. - * - * @param scope - The scope for this rule. - * @param entity - The entity covered by this rule. Globs are supported. - * @param reason - A human-readable reason for introducing this new rule. - * @returns The event id for the new rule. - */ - addRule(scope, entity, reason) { - return __awaiter(this, void 0, void 0, function* () { - const target = yield this.getOrCreateTargetRoom(); - const response = yield this.client.sendStateEvent(target.roomId, scope, { - entity, - reason, - recommendation: PolicyRecommendation.Ban, - }); - return response.event_id; - }); - } - /** - * Remove a rule. - */ - removeRule(event) { - return __awaiter(this, void 0, void 0, function* () { - yield this.client.redactEvent(event.getRoomId(), event.getId()); - }); - } - /** - * Add a new room to the list of sources. If the user isn't a member of the - * room, attempt to join it. - * - * @param roomId - A valid room id. If this room is already in the list - * of sources, it will not be duplicated. - * @returns `true` if the source was added, `false` if it was already present. - * @throws If `roomId` isn't the id of a room that the current user is already - * member of or can join. - * - * # Safety - * - * This method will rewrite the `Policies` object in the user's account data. - * This rewrite is inherently racy and could overwrite or be overwritten by - * other concurrent rewrites of the same object. - */ - addSource(roomId) { - return __awaiter(this, void 0, void 0, function* () { - // We attempt to join the room *before* calling - // `await this.getOrCreateSourceRooms()` to decrease the duration - // of the racy section. - yield this.client.joinRoom(roomId); - // Race starts. - const sources = (yield this.getOrCreateSourceRooms()).map((room) => room.roomId); - if (sources.includes(roomId)) { - return false; - } - sources.push(roomId); - yield this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => { - ignoreInvitesPolicies.sources = sources; - }); - // Race ends. - return true; - }); - } - /** - * Find out whether an invite should be ignored. - * - * @param sender - The user id for the user who issued the invite. - * @param roomId - The room to which the user is invited. - * @returns A rule matching the entity, if any was found, `null` otherwise. - */ - getRuleForInvite({ sender, roomId, }) { - return __awaiter(this, void 0, void 0, function* () { - // In this implementation, we perform a very naive lookup: - // - search in each policy room; - // - turn each (potentially glob) rule entity into a regexp. - // - // Real-world testing will tell us whether this is performant enough. - // In the (unfortunately likely) case it isn't, there are several manners - // in which we could optimize this: - // - match several entities per go; - // - pre-compile each rule entity into a regexp; - // - pre-compile entire rooms into a single regexp. - const policyRooms = yield this.getOrCreateSourceRooms(); - const senderServer = sender.split(":")[1]; - const roomServer = roomId.split(":")[1]; - for (const room of policyRooms) { - const state = room.getUnfilteredTimelineSet().getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS); - for (const { scope, entities } of [ - { scope: PolicyScope.Room, entities: [roomId] }, - { scope: PolicyScope.User, entities: [sender] }, - { scope: PolicyScope.Server, entities: [senderServer, roomServer] }, - ]) { - const events = state.getStateEvents(scope); - for (const event of events) { - const content = event.getContent(); - if ((content === null || content === void 0 ? void 0 : content.recommendation) != PolicyRecommendation.Ban) { - // Ignoring invites only looks at `m.ban` recommendations. - continue; - } - const glob = content === null || content === void 0 ? void 0 : content.entity; - if (!glob) { - // Invalid event. - continue; - } - let regexp; - try { - regexp = new RegExp((0, utils_1.globToRegexp)(glob, false)); - } - catch (ex) { - // Assume invalid event. - continue; - } - for (const entity of entities) { - if (entity && regexp.test(entity)) { - return event; - } - } - // No match. - } - } - } - return null; - }); - } - /** - * Get the target room, i.e. the room in which any new rule should be written. - * - * If there is no target room setup, a target room is created. - * - * Note: This method is public for testing reasons. Most clients should not need - * to call it directly. - * - * # Safety - * - * This method will rewrite the `Policies` object in the user's account data. - * This rewrite is inherently racy and could overwrite or be overwritten by - * other concurrent rewrites of the same object. - */ - getOrCreateTargetRoom() { - return __awaiter(this, void 0, void 0, function* () { - const ignoreInvitesPolicies = this.getIgnoreInvitesPolicies(); - let target = ignoreInvitesPolicies.target; - // Validate `target`. If it is invalid, trash out the current `target` - // and create a new room. - if (typeof target !== "string") { - target = null; - } - if (target) { - // Check that the room exists and is valid. - const room = this.client.getRoom(target); - if (room) { - return room; - } - else { - target = null; - } - } - // We need to create our own policy room for ignoring invites. - target = (yield this.client.createRoom({ - name: "Individual Policy Room", - preset: partials_1.Preset.PrivateChat, - })).room_id; - yield this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => { - ignoreInvitesPolicies.target = target; - }); - // Since we have just called `createRoom`, `getRoom` should not be `null`. - return this.client.getRoom(target); - }); - } - /** - * Get the list of source rooms, i.e. the rooms from which rules need to be read. - * - * If no source rooms are setup, the target room is used as sole source room. - * - * Note: This method is public for testing reasons. Most clients should not need - * to call it directly. - * - * # Safety - * - * This method will rewrite the `Policies` object in the user's account data. - * This rewrite is inherently racy and could overwrite or be overwritten by - * other concurrent rewrites of the same object. - */ - getOrCreateSourceRooms() { - return __awaiter(this, void 0, void 0, function* () { - const ignoreInvitesPolicies = this.getIgnoreInvitesPolicies(); - let sources = ignoreInvitesPolicies.sources; - // Validate `sources`. If it is invalid, trash out the current `sources` - // and create a new list of sources from `target`. - let hasChanges = false; - if (!Array.isArray(sources)) { - // `sources` could not be an array. - hasChanges = true; - sources = []; - } - let sourceRooms = sources - // `sources` could contain non-string / invalid room ids - .filter((roomId) => typeof roomId === "string") - .map((roomId) => this.client.getRoom(roomId)) - .filter((room) => !!room); - if (sourceRooms.length != sources.length) { - hasChanges = true; - } - if (sourceRooms.length == 0) { - // `sources` could be empty (possibly because we've removed - // invalid content) - const target = yield this.getOrCreateTargetRoom(); - hasChanges = true; - sourceRooms = [target]; - } - if (hasChanges) { - // Reload `policies`/`ignoreInvitesPolicies` in case it has been changed - // during or by our call to `this.getTargetRoom()`. - yield this.withIgnoreInvitesPolicies((ignoreInvitesPolicies) => { - ignoreInvitesPolicies.sources = sources; - }); - } - return sourceRooms; - }); - } - /** - * Fetch the `IGNORE_INVITES_POLICIES` object from account data. - * - * If both an unstable prefix version and a stable prefix version are available, - * it will return the stable prefix version preferentially. - * - * The result is *not* validated but is guaranteed to be a non-null object. - * - * @returns A non-null object. - */ - getIgnoreInvitesPolicies() { - return this.getPoliciesAndIgnoreInvitesPolicies().ignoreInvitesPolicies; - } - /** - * Modify in place the `IGNORE_INVITES_POLICIES` object from account data. - */ - withIgnoreInvitesPolicies(cb) { - return __awaiter(this, void 0, void 0, function* () { - const { policies, ignoreInvitesPolicies } = this.getPoliciesAndIgnoreInvitesPolicies(); - cb(ignoreInvitesPolicies); - policies[exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY.name] = ignoreInvitesPolicies; - yield this.client.setAccountData(exports.POLICIES_ACCOUNT_EVENT_TYPE.name, policies); - }); - } - /** - * As `getIgnoreInvitesPolicies` but also return the `POLICIES_ACCOUNT_EVENT_TYPE` - * object. - */ - getPoliciesAndIgnoreInvitesPolicies() { - var _a; - let policies = {}; - for (const key of [exports.POLICIES_ACCOUNT_EVENT_TYPE.name, exports.POLICIES_ACCOUNT_EVENT_TYPE.altName]) { - if (!key) { - continue; - } - const value = (_a = this.client.getAccountData(key)) === null || _a === void 0 ? void 0 : _a.getContent(); - if (value) { - policies = value; - break; - } - } - let ignoreInvitesPolicies = {}; - let hasIgnoreInvitesPolicies = false; - for (const key of [exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY.name, exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY.altName]) { - if (!key) { - continue; - } - const value = policies[key]; - if (value && typeof value == "object") { - ignoreInvitesPolicies = value; - hasIgnoreInvitesPolicies = true; - break; - } - } - if (!hasIgnoreInvitesPolicies) { - policies[exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY.name] = ignoreInvitesPolicies; - } - return { policies, ignoreInvitesPolicies }; - } -} -exports.IgnoredInvites = IgnoredInvites; - -},{"../@types/partials":309,"../utils":416,"./event-timeline":382,"matrix-events-sdk":167}],385:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Poll = exports.PollEvent = void 0; -const polls_1 = require("../@types/polls"); -const relations_1 = require("./relations"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -var PollEvent; -(function (PollEvent) { - PollEvent["New"] = "Poll.new"; - PollEvent["End"] = "Poll.end"; - PollEvent["Update"] = "Poll.update"; - PollEvent["Responses"] = "Poll.Responses"; - PollEvent["Destroy"] = "Poll.Destroy"; - PollEvent["UndecryptableRelations"] = "Poll.UndecryptableRelations"; -})(PollEvent = exports.PollEvent || (exports.PollEvent = {})); -const filterResponseRelations = (relationEvents, pollEndTimestamp) => { - const responseEvents = relationEvents.filter((event) => { - if (event.isDecryptionFailure()) { - return; - } - return (polls_1.M_POLL_RESPONSE.matches(event.getType()) && - // From MSC3381: - // "Votes sent on or before the end event's timestamp are valid votes" - event.getTs() <= pollEndTimestamp); - }); - return { responseEvents }; -}; -class Poll extends typed_event_emitter_1.TypedEventEmitter { - constructor(rootEvent, matrixClient, room) { - super(); - this.rootEvent = rootEvent; - this.matrixClient = matrixClient; - this.room = room; - this._isFetchingResponses = false; - this.responses = null; - /** - * Keep track of undecryptable relations - * As incomplete result sets affect poll results - */ - this.undecryptableRelationEventIds = new Set(); - this.countUndecryptableEvents = (events) => { - const undecryptableEventIds = events - .filter((event) => event.isDecryptionFailure()) - .map((event) => event.getId()); - const previousCount = this.undecryptableRelationsCount; - this.undecryptableRelationEventIds = new Set([...this.undecryptableRelationEventIds, ...undecryptableEventIds]); - if (this.undecryptableRelationsCount !== previousCount) { - this.emit(PollEvent.UndecryptableRelations, this.undecryptableRelationsCount); - } - }; - if (!this.rootEvent.getRoomId() || !this.rootEvent.getId()) { - throw new Error("Invalid poll start event."); - } - this.roomId = this.rootEvent.getRoomId(); - this.pollEvent = this.rootEvent.unstableExtensibleEvent; - } - get pollId() { - return this.rootEvent.getId(); - } - get endEventId() { - var _a; - return (_a = this.endEvent) === null || _a === void 0 ? void 0 : _a.getId(); - } - get isEnded() { - return !!this.endEvent; - } - get isFetchingResponses() { - return this._isFetchingResponses; - } - get undecryptableRelationsCount() { - return this.undecryptableRelationEventIds.size; - } - getResponses() { - return __awaiter(this, void 0, void 0, function* () { - // if we have already fetched some responses - // just return them - if (this.responses) { - return this.responses; - } - // if there is no fetching in progress - // start fetching - if (!this.isFetchingResponses) { - yield this.fetchResponses(); - } - // return whatever responses we got from the first page - return this.responses; - }); - } - /** - * - * @param event - event with a relation to the rootEvent - * @returns void - */ - onNewRelation(event) { - var _a; - if (polls_1.M_POLL_END.matches(event.getType()) && this.validateEndEvent(event)) { - this.endEvent = event; - this.refilterResponsesOnEnd(); - this.emit(PollEvent.End); - } - // wait for poll responses to be initialised - if (!this.responses) { - return; - } - const pollEndTimestamp = ((_a = this.endEvent) === null || _a === void 0 ? void 0 : _a.getTs()) || Number.MAX_SAFE_INTEGER; - const { responseEvents } = filterResponseRelations([event], pollEndTimestamp); - this.countUndecryptableEvents([event]); - if (responseEvents.length) { - responseEvents.forEach((event) => { - this.responses.addEvent(event); - }); - this.emit(PollEvent.Responses, this.responses); - } - } - fetchResponses() { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - this._isFetchingResponses = true; - // we want: - // - stable and unstable M_POLL_RESPONSE - // - stable and unstable M_POLL_END - // so make one api call and filter by event type client side - const allRelations = yield this.matrixClient.relations(this.roomId, this.rootEvent.getId(), "m.reference", undefined, { - from: this.relationsNextBatch || undefined, - }); - yield Promise.all(allRelations.events.map((event) => this.matrixClient.decryptEventIfNeeded(event))); - const responses = this.responses || - new relations_1.Relations("m.reference", polls_1.M_POLL_RESPONSE.name, this.matrixClient, [polls_1.M_POLL_RESPONSE.altName]); - const pollEndEvent = allRelations.events.find((event) => polls_1.M_POLL_END.matches(event.getType())); - if (this.validateEndEvent(pollEndEvent)) { - this.endEvent = pollEndEvent; - this.refilterResponsesOnEnd(); - this.emit(PollEvent.End); - } - const pollCloseTimestamp = ((_a = this.endEvent) === null || _a === void 0 ? void 0 : _a.getTs()) || Number.MAX_SAFE_INTEGER; - const { responseEvents } = filterResponseRelations(allRelations.events, pollCloseTimestamp); - responseEvents.forEach((event) => { - responses.addEvent(event); - }); - this.relationsNextBatch = (_b = allRelations.nextBatch) !== null && _b !== void 0 ? _b : undefined; - this.responses = responses; - this.countUndecryptableEvents(allRelations.events); - // while there are more pages of relations - // fetch them - if (this.relationsNextBatch) { - // don't await - // we want to return the first page as soon as possible - this.fetchResponses(); - } - else { - // no more pages - this._isFetchingResponses = false; - } - // emit after updating _isFetchingResponses state - this.emit(PollEvent.Responses, this.responses); - }); - } - /** - * Only responses made before the poll ended are valid - * Refilter after an end event is recieved - * To ensure responses are valid - */ - refilterResponsesOnEnd() { - var _a; - if (!this.responses) { - return; - } - const pollEndTimestamp = ((_a = this.endEvent) === null || _a === void 0 ? void 0 : _a.getTs()) || Number.MAX_SAFE_INTEGER; - this.responses.getRelations().forEach((event) => { - var _a; - if (event.getTs() > pollEndTimestamp) { - (_a = this.responses) === null || _a === void 0 ? void 0 : _a.removeEvent(event); - } - }); - this.emit(PollEvent.Responses, this.responses); - } - validateEndEvent(endEvent) { - if (!endEvent) { - return false; - } - /** - * Repeated end events are ignored - - * only the first (valid) closure event by origin_server_ts is counted. - */ - if (this.endEvent && this.endEvent.getTs() < endEvent.getTs()) { - return false; - } - /** - * MSC3381 - * If a m.poll.end event is received from someone other than the poll creator or user with permission to redact - * others' messages in the room, the event must be ignored by clients due to being invalid. - */ - const roomCurrentState = this.room.currentState; - const endEventSender = endEvent.getSender(); - return (!!endEventSender && - (endEventSender === this.rootEvent.getSender() || - roomCurrentState.maySendRedactionForEvent(this.rootEvent, endEventSender))); - } -} -exports.Poll = Poll; - -},{"../@types/polls":310,"./relations":388,"./typed-event-emitter":395}],386:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ReadReceipt = exports.synthesizeReceipt = void 0; -const read_receipts_1 = require("../@types/read_receipts"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -const utils = __importStar(require("../utils")); -const event_1 = require("./event"); -const event_2 = require("../@types/event"); -const utils_1 = require("../utils"); -const room_1 = require("./room"); -function synthesizeReceipt(userId, event, receiptType) { - var _a; - return new event_1.MatrixEvent({ - content: { - [event.getId()]: { - [receiptType]: { - [userId]: { - ts: event.getTs(), - thread_id: (_a = event.threadRootId) !== null && _a !== void 0 ? _a : read_receipts_1.MAIN_ROOM_TIMELINE, - }, - }, - }, - }, - type: event_2.EventType.Receipt, - room_id: event.getRoomId(), - }); -} -exports.synthesizeReceipt = synthesizeReceipt; -const ReceiptPairRealIndex = 0; -const ReceiptPairSyntheticIndex = 1; -class ReadReceipt extends typed_event_emitter_1.TypedEventEmitter { - constructor() { - super(...arguments); - // receipts should clobber based on receipt_type and user_id pairs hence - // the form of this structure. This is sub-optimal for the exposed APIs - // which pass in an event ID and get back some receipts, so we also store - // a pre-cached list for this purpose. - // Map: receipt type → user Id → receipt - this.receipts = new utils_1.MapWithDefault(() => new Map()); - this.receiptCacheByEventId = new Map(); - } - /** - * Gets the latest receipt for a given user in the room - * @param userId - The id of the user for which we want the receipt - * @param ignoreSynthesized - Whether to ignore synthesized receipts or not - * @param receiptType - Optional. The type of the receipt we want to get - * @returns the latest receipts of the chosen type for the chosen user - */ - getReadReceiptForUserId(userId, ignoreSynthesized = false, receiptType = read_receipts_1.ReceiptType.Read) { - var _a, _b; - const [realReceipt, syntheticReceipt] = (_b = (_a = this.receipts.get(receiptType)) === null || _a === void 0 ? void 0 : _a.get(userId)) !== null && _b !== void 0 ? _b : [null, null]; - if (ignoreSynthesized) { - return realReceipt; - } - return syntheticReceipt !== null && syntheticReceipt !== void 0 ? syntheticReceipt : realReceipt; - } - /** - * Get the ID of the event that a given user has read up to, or null if we - * have received no read receipts from them. - * @param userId - The user ID to get read receipt event ID for - * @param ignoreSynthesized - If true, return only receipts that have been - * sent by the server, not implicit ones generated - * by the JS SDK. - * @returns ID of the latest event that the given user has read, or null. - */ - getEventReadUpTo(userId, ignoreSynthesized = false) { - // XXX: This is very very ugly and I hope I won't have to ever add a new - // receipt type here again. IMHO this should be done by the server in - // some more intelligent manner or the client should just use timestamps - var _a, _b, _c, _d, _e, _f, _g; - const timelineSet = this.getUnfilteredTimelineSet(); - const publicReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, read_receipts_1.ReceiptType.Read); - const privateReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, read_receipts_1.ReceiptType.ReadPrivate); - // If we have both, compare them - let comparison; - if ((publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.eventId) && (privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.eventId)) { - comparison = timelineSet.compareEventOrdering(publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.eventId, privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.eventId); - } - // If we didn't get a comparison try to compare the ts of the receipts - if (!comparison && ((_a = publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.data) === null || _a === void 0 ? void 0 : _a.ts) && ((_b = privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.data) === null || _b === void 0 ? void 0 : _b.ts)) { - comparison = ((_c = publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.data) === null || _c === void 0 ? void 0 : _c.ts) - ((_d = privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.data) === null || _d === void 0 ? void 0 : _d.ts); - } - // The public receipt is more likely to drift out of date so the private - // one has precedence - if (!comparison) - return (_f = (_e = privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.eventId) !== null && _e !== void 0 ? _e : publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.eventId) !== null && _f !== void 0 ? _f : null; - // If public read receipt is older, return the private one - return (_g = (comparison < 0 ? privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.eventId : publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.eventId)) !== null && _g !== void 0 ? _g : null; - } - addReceiptToStructure(eventId, receiptType, userId, receipt, synthetic) { - var _a, _b, _c; - const receiptTypesMap = this.receipts.getOrCreate(receiptType); - let pair = receiptTypesMap.get(userId); - if (!pair) { - pair = [null, null]; - receiptTypesMap.set(userId, pair); - } - let existingReceipt = pair[ReceiptPairRealIndex]; - if (synthetic) { - existingReceipt = (_a = pair[ReceiptPairSyntheticIndex]) !== null && _a !== void 0 ? _a : pair[ReceiptPairRealIndex]; - } - if (existingReceipt) { - // we only want to add this receipt if we think it is later than the one we already have. - // This is managed server-side, but because we synthesize RRs locally we have to do it here too. - const ordering = this.getUnfilteredTimelineSet().compareEventOrdering(existingReceipt.eventId, eventId); - if (ordering !== null && ordering >= 0) { - return; - } - } - const wrappedReceipt = { - eventId, - data: receipt, - }; - const realReceipt = synthetic ? pair[ReceiptPairRealIndex] : wrappedReceipt; - const syntheticReceipt = synthetic ? wrappedReceipt : pair[ReceiptPairSyntheticIndex]; - let ordering = null; - if (realReceipt && syntheticReceipt) { - ordering = this.getUnfilteredTimelineSet().compareEventOrdering(realReceipt.eventId, syntheticReceipt.eventId); - } - const preferSynthetic = ordering === null || ordering < 0; - // we don't bother caching just real receipts by event ID as there's nothing that would read it. - // Take the current cached receipt before we overwrite the pair elements. - const cachedReceipt = (_b = pair[ReceiptPairSyntheticIndex]) !== null && _b !== void 0 ? _b : pair[ReceiptPairRealIndex]; - if (synthetic && preferSynthetic) { - pair[ReceiptPairSyntheticIndex] = wrappedReceipt; - } - else if (!synthetic) { - pair[ReceiptPairRealIndex] = wrappedReceipt; - if (!preferSynthetic) { - pair[ReceiptPairSyntheticIndex] = null; - } - } - const newCachedReceipt = (_c = pair[ReceiptPairSyntheticIndex]) !== null && _c !== void 0 ? _c : pair[ReceiptPairRealIndex]; - if (cachedReceipt === newCachedReceipt) - return; - // clean up any previous cache entry - if (cachedReceipt && this.receiptCacheByEventId.get(cachedReceipt.eventId)) { - const previousEventId = cachedReceipt.eventId; - // Remove the receipt we're about to clobber out of existence from the cache - this.receiptCacheByEventId.set(previousEventId, this.receiptCacheByEventId.get(previousEventId).filter((r) => { - return r.type !== receiptType || r.userId !== userId; - })); - if (this.receiptCacheByEventId.get(previousEventId).length < 1) { - this.receiptCacheByEventId.delete(previousEventId); // clean up the cache keys - } - } - // cache the new one - if (!this.receiptCacheByEventId.get(eventId)) { - this.receiptCacheByEventId.set(eventId, []); - } - this.receiptCacheByEventId.get(eventId).push({ - userId: userId, - type: receiptType, - data: receipt, - }); - } - /** - * Get a list of receipts for the given event. - * @param event - the event to get receipts for - * @returns A list of receipts with a userId, type and data keys or - * an empty list. - */ - getReceiptsForEvent(event) { - return this.receiptCacheByEventId.get(event.getId()) || []; - } - /** - * This issue should also be addressed on synapse's side and is tracked as part - * of https://github.com/matrix-org/synapse/issues/14837 - * - * Retrieves the read receipt for the logged in user and checks if it matches - * the last event in the room and whether that event originated from the logged - * in user. - * Under those conditions we can consider the context as read. This is useful - * because we never send read receipts against our own events - * @param userId - the logged in user - */ - fixupNotifications(userId) { - const receipt = this.getReadReceiptForUserId(userId, false); - const lastEvent = this.timeline[this.timeline.length - 1]; - if (lastEvent && (receipt === null || receipt === void 0 ? void 0 : receipt.eventId) === lastEvent.getId() && userId === lastEvent.getSender()) { - this.setUnread(room_1.NotificationCountType.Total, 0); - this.setUnread(room_1.NotificationCountType.Highlight, 0); - } - } - /** - * Add a temporary local-echo receipt to the room to reflect in the - * client the fact that we've sent one. - * @param userId - The user ID if the receipt sender - * @param e - The event that is to be acknowledged - * @param receiptType - The type of receipt - */ - addLocalEchoReceipt(userId, e, receiptType) { - this.addReceipt(synthesizeReceipt(userId, e, receiptType), true); - } - /** - * Get a list of user IDs who have read up to the given event. - * @param event - the event to get read receipts for. - * @returns A list of user IDs. - */ - getUsersReadUpTo(event) { - return this.getReceiptsForEvent(event) - .filter(function (receipt) { - return utils.isSupportedReceiptType(receipt.type); - }) - .map(function (receipt) { - return receipt.userId; - }); - } - /** - * Determines if the given user has read a particular event ID with the known - * history of the room. This is not a definitive check as it relies only on - * what is available to the room at the time of execution. - * @param userId - The user ID to check the read state of. - * @param eventId - The event ID to check if the user read. - * @returns True if the user has read the event, false otherwise. - */ - hasUserReadEvent(userId, eventId) { - var _a, _b; - const readUpToId = this.getEventReadUpTo(userId, false); - if (readUpToId === eventId) - return true; - if (((_a = this.timeline) === null || _a === void 0 ? void 0 : _a.length) && - this.timeline[this.timeline.length - 1].getSender() && - this.timeline[this.timeline.length - 1].getSender() === userId) { - // It doesn't matter where the event is in the timeline, the user has read - // it because they've sent the latest event. - return true; - } - for (let i = ((_b = this.timeline) === null || _b === void 0 ? void 0 : _b.length) - 1; i >= 0; --i) { - const ev = this.timeline[i]; - // If we encounter the target event first, the user hasn't read it - // however if we encounter the readUpToId first then the user has read - // it. These rules apply because we're iterating bottom-up. - if (ev.getId() === eventId) - return false; - if (ev.getId() === readUpToId) - return true; - } - // We don't know if the user has read it, so assume not. - return false; - } -} -exports.ReadReceipt = ReadReceipt; - -},{"../@types/event":306,"../@types/read_receipts":311,"../utils":416,"./event":383,"./room":392,"./typed-event-emitter":395}],387:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RelationsContainer = void 0; -const relations_1 = require("./relations"); -const event_1 = require("./event"); -class RelationsContainer { - constructor(client, room) { - this.client = client; - this.room = room; - // A tree of objects to access a set of related children for an event, as in: - // this.relations.get(parentEventId).get(relationType).get(relationEventType) - this.relations = new Map(); - } - /** - * Get a collection of child events to a given event in this timeline set. - * - * @param eventId - The ID of the event that you'd like to access child events for. - * For example, with annotations, this would be the ID of the event being annotated. - * @param relationType - The type of relationship involved, such as "m.annotation", "m.reference", "m.replace", etc. - * @param eventType - The relation event's type, such as "m.reaction", etc. - * @throws If `eventId, relationType or eventType` - * are not valid. - * - * @returns - * A container for relation events or undefined if there are no relation events for - * the relationType. - */ - getChildEventsForEvent(eventId, relationType, eventType) { - var _a, _b; - return (_b = (_a = this.relations.get(eventId)) === null || _a === void 0 ? void 0 : _a.get(relationType)) === null || _b === void 0 ? void 0 : _b.get(eventType); - } - getAllChildEventsForEvent(parentEventId) { - var _a; - const relationsForEvent = (_a = this.relations.get(parentEventId)) !== null && _a !== void 0 ? _a : new Map(); - const events = []; - for (const relationsRecord of relationsForEvent.values()) { - for (const relations of relationsRecord.values()) { - events.push(...relations.getRelations()); - } - } - return events; - } - /** - * Set an event as the target event if any Relations exist for it already. - * Child events can point to other child events as their parent, so this method may be - * called for events which are also logically child events. - * - * @param event - The event to check as relation target. - */ - aggregateParentEvent(event) { - const relationsForEvent = this.relations.get(event.getId()); - if (!relationsForEvent) - return; - for (const relationsWithRelType of relationsForEvent.values()) { - for (const relationsWithEventType of relationsWithRelType.values()) { - relationsWithEventType.setTargetEvent(event); - } - } - } - /** - * Add relation events to the relevant relation collection. - * - * @param event - The new child event to be aggregated. - * @param timelineSet - The event timeline set within which to search for the related event if any. - */ - aggregateChildEvent(event, timelineSet) { - var _a, _b, _c; - if (event.isRedacted() || event.status === event_1.EventStatus.CANCELLED) { - return; - } - const relation = event.getRelation(); - if (!relation) - return; - const onEventDecrypted = () => { - if (event.isDecryptionFailure()) { - // This could for example happen if the encryption keys are not yet available. - // The event may still be decrypted later. Register the listener again. - event.once(event_1.MatrixEventEvent.Decrypted, onEventDecrypted); - return; - } - this.aggregateChildEvent(event, timelineSet); - }; - // If the event is currently encrypted, wait until it has been decrypted. - if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) { - event.once(event_1.MatrixEventEvent.Decrypted, onEventDecrypted); - return; - } - const { event_id: relatesToEventId, rel_type: relationType } = relation; - const eventType = event.getType(); - let relationsForEvent = this.relations.get(relatesToEventId); - if (!relationsForEvent) { - relationsForEvent = new Map(); - this.relations.set(relatesToEventId, relationsForEvent); - } - let relationsWithRelType = relationsForEvent.get(relationType); - if (!relationsWithRelType) { - relationsWithRelType = new Map(); - relationsForEvent.set(relationType, relationsWithRelType); - } - let relationsWithEventType = relationsWithRelType.get(eventType); - if (!relationsWithEventType) { - relationsWithEventType = new relations_1.Relations(relationType, eventType, this.client); - relationsWithRelType.set(eventType, relationsWithEventType); - const room = (_a = this.room) !== null && _a !== void 0 ? _a : timelineSet === null || timelineSet === void 0 ? void 0 : timelineSet.room; - const relatesToEvent = (_c = (_b = timelineSet === null || timelineSet === void 0 ? void 0 : timelineSet.findEventById(relatesToEventId)) !== null && _b !== void 0 ? _b : room === null || room === void 0 ? void 0 : room.findEventById(relatesToEventId)) !== null && _c !== void 0 ? _c : room === null || room === void 0 ? void 0 : room.getPendingEvent(relatesToEventId); - if (relatesToEvent) { - relationsWithEventType.setTargetEvent(relatesToEvent); - } - } - relationsWithEventType.addEvent(event); - } -} -exports.RelationsContainer = RelationsContainer; - -},{"./event":383,"./relations":388}],388:[function(require,module,exports){ -"use strict"; -/* -Copyright 2019, 2021, 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Relations = exports.RelationsEvent = void 0; -const event_1 = require("./event"); -const logger_1 = require("../logger"); -const event_2 = require("../@types/event"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -const room_1 = require("./room"); -var RelationsEvent; -(function (RelationsEvent) { - RelationsEvent["Add"] = "Relations.add"; - RelationsEvent["Remove"] = "Relations.remove"; - RelationsEvent["Redaction"] = "Relations.redaction"; -})(RelationsEvent = exports.RelationsEvent || (exports.RelationsEvent = {})); -const matchesEventType = (eventType, targetEventType, altTargetEventTypes = []) => [targetEventType, ...altTargetEventTypes].includes(eventType); -/** - * A container for relation events that supports easy access to common ways of - * aggregating such events. Each instance holds events that of a single relation - * type and event type. All of the events also relate to the same original event. - * - * The typical way to get one of these containers is via - * EventTimelineSet#getRelationsForEvent. - */ -class Relations extends typed_event_emitter_1.TypedEventEmitter { - /** - * @param relationType - The type of relation involved, such as "m.annotation", "m.reference", "m.replace", etc. - * @param eventType - The relation event's type, such as "m.reaction", etc. - * @param client - The client which created this instance. For backwards compatibility also accepts a Room. - * @param altEventTypes - alt event types for relation events, for example to support unstable prefixed event types - */ - constructor(relationType, eventType, client, altEventTypes) { - super(); - this.relationType = relationType; - this.eventType = eventType; - this.altEventTypes = altEventTypes; - this.relationEventIds = new Set(); - this.relations = new Set(); - this.annotationsByKey = {}; - this.annotationsBySender = {}; - this.sortedAnnotationsByKey = []; - this.targetEvent = null; - this.creationEmitted = false; - /** - * Listens for event status changes to remove cancelled events. - * - * @param event - The event whose status has changed - * @param status - The new status - */ - this.onEventStatus = (event, status) => { - if (!event.isSending()) { - // Sending is done, so we don't need to listen anymore - event.removeListener(event_1.MatrixEventEvent.Status, this.onEventStatus); - return; - } - if (status !== event_1.EventStatus.CANCELLED) { - return; - } - // Event was cancelled, remove from the collection - event.removeListener(event_1.MatrixEventEvent.Status, this.onEventStatus); - this.removeEvent(event); - }; - /** - * For relations that have been redacted, we want to remove them from - * aggregation data sets and emit an update event. - * - * To do so, we listen for `Event.beforeRedaction`, which happens: - * - after the server accepted the redaction and remote echoed back to us - * - before the original event has been marked redacted in the client - * - * @param redactedEvent - The original relation event that is about to be redacted. - */ - this.onBeforeRedaction = (redactedEvent) => __awaiter(this, void 0, void 0, function* () { - if (!this.relations.has(redactedEvent)) { - return; - } - this.relations.delete(redactedEvent); - if (this.relationType === event_2.RelationType.Annotation) { - // Remove the redacted annotation from aggregation by key - this.removeAnnotationFromAggregation(redactedEvent); - } - else if (this.relationType === event_2.RelationType.Replace && this.targetEvent && !this.targetEvent.isState()) { - const lastReplacement = yield this.getLastReplacement(); - this.targetEvent.makeReplaced(lastReplacement); - } - redactedEvent.removeListener(event_1.MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction); - this.emit(RelationsEvent.Redaction, redactedEvent); - }); - this.client = client instanceof room_1.Room ? client.client : client; - } - /** - * Add relation events to this collection. - * - * @param event - The new relation event to be added. - */ - addEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - if (this.relationEventIds.has(event.getId())) { - return; - } - const relation = event.getRelation(); - if (!relation) { - logger_1.logger.error("Event must have relation info"); - return; - } - const relationType = relation.rel_type; - const eventType = event.getType(); - if (this.relationType !== relationType || !matchesEventType(eventType, this.eventType, this.altEventTypes)) { - logger_1.logger.error("Event relation info doesn't match this container"); - return; - } - // If the event is in the process of being sent, listen for cancellation - // so we can remove the event from the collection. - if (event.isSending()) { - event.on(event_1.MatrixEventEvent.Status, this.onEventStatus); - } - this.relations.add(event); - this.relationEventIds.add(event.getId()); - if (this.relationType === event_2.RelationType.Annotation) { - this.addAnnotationToAggregation(event); - } - else if (this.relationType === event_2.RelationType.Replace && this.targetEvent && !this.targetEvent.isState()) { - const lastReplacement = yield this.getLastReplacement(); - this.targetEvent.makeReplaced(lastReplacement); - } - event.on(event_1.MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction); - this.emit(RelationsEvent.Add, event); - this.maybeEmitCreated(); - }); - } - /** - * Remove relation event from this collection. - * - * @param event - The relation event to remove. - */ - removeEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.relations.has(event)) { - return; - } - this.relations.delete(event); - if (this.relationType === event_2.RelationType.Annotation) { - this.removeAnnotationFromAggregation(event); - } - else if (this.relationType === event_2.RelationType.Replace && this.targetEvent && !this.targetEvent.isState()) { - const lastReplacement = yield this.getLastReplacement(); - this.targetEvent.makeReplaced(lastReplacement); - } - this.emit(RelationsEvent.Remove, event); - }); - } - /** - * Get all relation events in this collection. - * - * These are currently in the order of insertion to this collection, which - * won't match timeline order in the case of scrollback. - * TODO: Tweak `addEvent` to insert correctly for scrollback. - * - * Relation events in insertion order. - */ - getRelations() { - return [...this.relations]; - } - addAnnotationToAggregation(event) { - var _a; - const { key } = (_a = event.getRelation()) !== null && _a !== void 0 ? _a : {}; - if (!key) - return; - let eventsForKey = this.annotationsByKey[key]; - if (!eventsForKey) { - eventsForKey = this.annotationsByKey[key] = new Set(); - this.sortedAnnotationsByKey.push([key, eventsForKey]); - } - // Add the new event to the set for this key - eventsForKey.add(event); - // Re-sort the [key, events] pairs in descending order of event count - this.sortedAnnotationsByKey.sort((a, b) => { - const aEvents = a[1]; - const bEvents = b[1]; - return bEvents.size - aEvents.size; - }); - const sender = event.getSender(); - let eventsFromSender = this.annotationsBySender[sender]; - if (!eventsFromSender) { - eventsFromSender = this.annotationsBySender[sender] = new Set(); - } - // Add the new event to the set for this sender - eventsFromSender.add(event); - } - removeAnnotationFromAggregation(event) { - var _a; - const { key } = (_a = event.getRelation()) !== null && _a !== void 0 ? _a : {}; - if (!key) - return; - const eventsForKey = this.annotationsByKey[key]; - if (eventsForKey) { - eventsForKey.delete(event); - // Re-sort the [key, events] pairs in descending order of event count - this.sortedAnnotationsByKey.sort((a, b) => { - const aEvents = a[1]; - const bEvents = b[1]; - return bEvents.size - aEvents.size; - }); - } - const sender = event.getSender(); - const eventsFromSender = this.annotationsBySender[sender]; - if (eventsFromSender) { - eventsFromSender.delete(event); - } - } - /** - * Get all events in this collection grouped by key and sorted by descending - * event count in each group. - * - * This is currently only supported for the annotation relation type. - * - * An array of [key, events] pairs sorted by descending event count. - * The events are stored in a Set (which preserves insertion order). - */ - getSortedAnnotationsByKey() { - if (this.relationType !== event_2.RelationType.Annotation) { - // Other relation types are not grouped currently. - return null; - } - return this.sortedAnnotationsByKey; - } - /** - * Get all events in this collection grouped by sender. - * - * This is currently only supported for the annotation relation type. - * - * An object with each relation sender as a key and the matching Set of - * events for that sender as a value. - */ - getAnnotationsBySender() { - if (this.relationType !== event_2.RelationType.Annotation) { - // Other relation types are not grouped currently. - return null; - } - return this.annotationsBySender; - } - /** - * Returns the most recent (and allowed) m.replace relation, if any. - * - * This is currently only supported for the m.replace relation type, - * once the target event is known, see `addEvent`. - */ - getLastReplacement() { - return __awaiter(this, void 0, void 0, function* () { - if (this.relationType !== event_2.RelationType.Replace) { - // Aggregating on last only makes sense for this relation type - return null; - } - if (!this.targetEvent) { - // Don't know which replacements to accept yet. - // This method shouldn't be called before the original - // event is known anyway. - return null; - } - // the all-knowning server tells us that the event at some point had - // this timestamp for its replacement, so any following replacement should definitely not be less - const replaceRelation = this.targetEvent.getServerAggregatedRelation(event_2.RelationType.Replace); - const minTs = replaceRelation === null || replaceRelation === void 0 ? void 0 : replaceRelation.origin_server_ts; - const lastReplacement = this.getRelations().reduce((last, event) => { - if (event.getSender() !== this.targetEvent.getSender()) { - return last; - } - if (minTs && minTs > event.getTs()) { - return last; - } - if (last && last.getTs() > event.getTs()) { - return last; - } - return event; - }, null); - if ((lastReplacement === null || lastReplacement === void 0 ? void 0 : lastReplacement.shouldAttemptDecryption()) && this.client.isCryptoEnabled()) { - yield lastReplacement.attemptDecryption(this.client.crypto); - } - else if (lastReplacement === null || lastReplacement === void 0 ? void 0 : lastReplacement.isBeingDecrypted()) { - yield lastReplacement.getDecryptionPromise(); - } - return lastReplacement; - }); - } - /* - * @param targetEvent - the event the relations are related to. - */ - setTargetEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - if (this.targetEvent) { - return; - } - this.targetEvent = event; - if (this.relationType === event_2.RelationType.Replace && !this.targetEvent.isState()) { - const replacement = yield this.getLastReplacement(); - // this is the initial update, so only call it if we already have something - // to not emit Event.replaced needlessly - if (replacement) { - this.targetEvent.makeReplaced(replacement); - } - } - this.maybeEmitCreated(); - }); - } - maybeEmitCreated() { - if (this.creationEmitted) { - return; - } - // Only emit we're "created" once we have a target event instance _and_ - // at least one related event. - if (!this.targetEvent || !this.relations.size) { - return; - } - this.creationEmitted = true; - this.targetEvent.emit(event_1.MatrixEventEvent.RelationsCreated, this.relationType, this.eventType); - } -} -exports.Relations = Relations; - -},{"../@types/event":306,"../logger":374,"./event":383,"./room":392,"./typed-event-emitter":395}],389:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoomMember = exports.RoomMemberEvent = void 0; -const content_repo_1 = require("../content-repo"); -const utils = __importStar(require("../utils")); -const logger_1 = require("../logger"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -const event_1 = require("../@types/event"); -var RoomMemberEvent; -(function (RoomMemberEvent) { - RoomMemberEvent["Membership"] = "RoomMember.membership"; - RoomMemberEvent["Name"] = "RoomMember.name"; - RoomMemberEvent["PowerLevel"] = "RoomMember.powerLevel"; - RoomMemberEvent["Typing"] = "RoomMember.typing"; -})(RoomMemberEvent = exports.RoomMemberEvent || (exports.RoomMemberEvent = {})); -class RoomMember extends typed_event_emitter_1.TypedEventEmitter { - /** - * Construct a new room member. - * - * @param roomId - The room ID of the member. - * @param userId - The user ID of the member. - */ - constructor(roomId, userId) { - super(); - this.roomId = roomId; - this.userId = userId; - this._isOutOfBand = false; - this.modified = -1; - this.requestedProfileInfo = false; // used by sync.ts - // XXX these should be read-only - /** - * True if the room member is currently typing. - */ - this.typing = false; - /** - * The power level for this room member. - */ - this.powerLevel = 0; - /** - * The normalised power level (0-100) for this room member. - */ - this.powerLevelNorm = 0; - /** - * True if the member's name is disambiguated. - */ - this.disambiguate = false; - /** - * The events describing this RoomMember. - */ - this.events = {}; - this.name = userId; - this.rawDisplayName = userId; - this.updateModifiedTime(); - } - /** - * Mark the member as coming from a channel that is not sync - */ - markOutOfBand() { - this._isOutOfBand = true; - } - /** - * @returns does the member come from a channel that is not sync? - * This is used to store the member seperately - * from the sync state so it available across browser sessions. - */ - isOutOfBand() { - return this._isOutOfBand; - } - /** - * Update this room member's membership event. May fire "RoomMember.name" if - * this event updates this member's name. - * @param event - The `m.room.member` event - * @param roomState - Optional. The room state to take into account - * when calculating (e.g. for disambiguating users with the same name). - * - * @remarks - * Fires {@link RoomMemberEvent.Name} - * Fires {@link RoomMemberEvent.Membership} - */ - setMembershipEvent(event, roomState) { - var _a, _b; - const displayName = (_a = event.getDirectionalContent().displayname) !== null && _a !== void 0 ? _a : ""; - if (event.getType() !== event_1.EventType.RoomMember) { - return; - } - this._isOutOfBand = false; - this.events.member = event; - const oldMembership = this.membership; - this.membership = event.getDirectionalContent().membership; - if (this.membership === undefined) { - // logging to diagnose https://github.com/vector-im/element-web/issues/20962 - // (logs event content, although only of membership events) - logger_1.logger.trace(`membership event with membership undefined (forwardLooking: ${event.forwardLooking})!`, event.getContent(), `prevcontent is `, event.getPrevContent()); - } - this.disambiguate = shouldDisambiguate(this.userId, displayName, roomState); - const oldName = this.name; - this.name = calculateDisplayName(this.userId, displayName, this.disambiguate); - // not quite raw: we strip direction override chars so it can safely be inserted into - // blocks of text without breaking the text direction - this.rawDisplayName = utils.removeDirectionOverrideChars((_b = event.getDirectionalContent().displayname) !== null && _b !== void 0 ? _b : ""); - if (!this.rawDisplayName || !utils.removeHiddenChars(this.rawDisplayName)) { - this.rawDisplayName = this.userId; - } - if (oldMembership !== this.membership) { - this.updateModifiedTime(); - this.emit(RoomMemberEvent.Membership, event, this, oldMembership); - } - if (oldName !== this.name) { - this.updateModifiedTime(); - this.emit(RoomMemberEvent.Name, event, this, oldName); - } - } - /** - * Update this room member's power level event. May fire - * "RoomMember.powerLevel" if this event updates this member's power levels. - * @param powerLevelEvent - The `m.room.power_levels` event - * - * @remarks - * Fires {@link RoomMemberEvent.PowerLevel} - */ - setPowerLevelEvent(powerLevelEvent) { - if (powerLevelEvent.getType() !== event_1.EventType.RoomPowerLevels || powerLevelEvent.getStateKey() !== "") { - return; - } - const evContent = powerLevelEvent.getDirectionalContent(); - let maxLevel = evContent.users_default || 0; - const users = evContent.users || {}; - Object.values(users).forEach((lvl) => { - maxLevel = Math.max(maxLevel, lvl); - }); - const oldPowerLevel = this.powerLevel; - const oldPowerLevelNorm = this.powerLevelNorm; - if (users[this.userId] !== undefined && Number.isInteger(users[this.userId])) { - this.powerLevel = users[this.userId]; - } - else if (evContent.users_default !== undefined) { - this.powerLevel = evContent.users_default; - } - else { - this.powerLevel = 0; - } - this.powerLevelNorm = 0; - if (maxLevel > 0) { - this.powerLevelNorm = (this.powerLevel * 100) / maxLevel; - } - // emit for changes in powerLevelNorm as well (since the app will need to - // redraw everyone's level if the max has changed) - if (oldPowerLevel !== this.powerLevel || oldPowerLevelNorm !== this.powerLevelNorm) { - this.updateModifiedTime(); - this.emit(RoomMemberEvent.PowerLevel, powerLevelEvent, this); - } - } - /** - * Update this room member's typing event. May fire "RoomMember.typing" if - * this event changes this member's typing state. - * @param event - The typing event - * - * @remarks - * Fires {@link RoomMemberEvent.Typing} - */ - setTypingEvent(event) { - if (event.getType() !== "m.typing") { - return; - } - const oldTyping = this.typing; - this.typing = false; - const typingList = event.getContent().user_ids; - if (!Array.isArray(typingList)) { - // malformed event :/ bail early. TODO: whine? - return; - } - if (typingList.indexOf(this.userId) !== -1) { - this.typing = true; - } - if (oldTyping !== this.typing) { - this.updateModifiedTime(); - this.emit(RoomMemberEvent.Typing, event, this); - } - } - /** - * Update the last modified time to the current time. - */ - updateModifiedTime() { - this.modified = Date.now(); - } - /** - * Get the timestamp when this RoomMember was last updated. This timestamp is - * updated when properties on this RoomMember are updated. - * It is updated before firing events. - * @returns The timestamp - */ - getLastModifiedTime() { - return this.modified; - } - isKicked() { - return (this.membership === "leave" && - this.events.member !== undefined && - this.events.member.getSender() !== this.events.member.getStateKey()); - } - /** - * If this member was invited with the is_direct flag set, return - * the user that invited this member - * @returns user id of the inviter - */ - getDMInviter() { - // when not available because that room state hasn't been loaded in, - // we don't really know, but more likely to not be a direct chat - if (this.events.member) { - // TODO: persist the is_direct flag on the member as more member events - // come in caused by displayName changes. - // the is_direct flag is set on the invite member event. - // This is copied on the prev_content section of the join member event - // when the invite is accepted. - const memberEvent = this.events.member; - let memberContent = memberEvent.getContent(); - let inviteSender = memberEvent.getSender(); - if (memberContent.membership === "join") { - memberContent = memberEvent.getPrevContent(); - inviteSender = memberEvent.getUnsigned().prev_sender; - } - if (memberContent.membership === "invite" && memberContent.is_direct) { - return inviteSender; - } - } - } - /** - * Get the avatar URL for a room member. - * @param baseUrl - The base homeserver URL See - * {@link MatrixClient#getHomeserverUrl}. - * @param width - The desired width of the thumbnail. - * @param height - The desired height of the thumbnail. - * @param resizeMethod - The thumbnail resize method to use, either - * "crop" or "scale". - * @param allowDefault - (optional) Passing false causes this method to - * return null if the user has no avatar image. Otherwise, a default image URL - * will be returned. Default: true. (Deprecated) - * @param allowDirectLinks - (optional) If true, the avatar URL will be - * returned even if it is a direct hyperlink rather than a matrix content URL. - * If false, any non-matrix content URLs will be ignored. Setting this option to - * true will expose URLs that, if fetched, will leak information about the user - * to anyone who they share a room with. - * @returns the avatar URL or null. - */ - getAvatarUrl(baseUrl, width, height, resizeMethod, allowDefault = true, allowDirectLinks) { - const rawUrl = this.getMxcAvatarUrl(); - if (!rawUrl && !allowDefault) { - return null; - } - const httpUrl = (0, content_repo_1.getHttpUriForMxc)(baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks); - if (httpUrl) { - return httpUrl; - } - return null; - } - /** - * get the mxc avatar url, either from a state event, or from a lazily loaded member - * @returns the mxc avatar url - */ - getMxcAvatarUrl() { - if (this.events.member) { - return this.events.member.getDirectionalContent().avatar_url; - } - else if (this.user) { - return this.user.avatarUrl; - } - } -} -exports.RoomMember = RoomMember; -const MXID_PATTERN = /@.+:.+/; -const LTR_RTL_PATTERN = /[\u200E\u200F\u202A-\u202F]/; -function shouldDisambiguate(selfUserId, displayName, roomState) { - if (!displayName || displayName === selfUserId) - return false; - // First check if the displayname is something we consider truthy - // after stripping it of zero width characters and padding spaces - if (!utils.removeHiddenChars(displayName)) - return false; - if (!roomState) - return false; - // Next check if the name contains something that look like a mxid - // If it does, it may be someone trying to impersonate someone else - // Show full mxid in this case - if (MXID_PATTERN.test(displayName)) - return true; - // Also show mxid if the display name contains any LTR/RTL characters as these - // make it very difficult for us to find similar *looking* display names - // E.g "Mark" could be cloned by writing "kraM" but in RTL. - if (LTR_RTL_PATTERN.test(displayName)) - return true; - // Also show mxid if there are other people with the same or similar - // displayname, after hidden character removal. - const userIds = roomState.getUserIdsWithDisplayName(displayName); - if (userIds.some((u) => u !== selfUserId)) - return true; - return false; -} -function calculateDisplayName(selfUserId, displayName, disambiguate) { - if (!displayName || displayName === selfUserId) - return selfUserId; - if (disambiguate) - return utils.removeDirectionOverrideChars(displayName) + " (" + selfUserId + ")"; - // First check if the displayname is something we consider truthy - // after stripping it of zero width characters and padding spaces - if (!utils.removeHiddenChars(displayName)) - return selfUserId; - // We always strip the direction override characters (LRO and RLO). - // These override the text direction for all subsequent characters - // in the paragraph so if display names contained these, they'd - // need to be wrapped in something to prevent this from leaking out - // (which we can do in HTML but not text) or we'd need to add - // control characters to the string to reset any overrides (eg. - // adding PDF characters at the end). As far as we can see, - // there should be no reason these would be necessary - rtl display - // names should flip into the correct direction automatically based on - // the characters, and you can still embed rtl in ltr or vice versa - // with the embed chars or marker chars. - return utils.removeDirectionOverrideChars(displayName); -} - -},{"../@types/event":306,"../content-repo":323,"../logger":374,"../utils":416,"./typed-event-emitter":395}],390:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoomState = exports.RoomStateEvent = void 0; -const room_member_1 = require("./room-member"); -const logger_1 = require("../logger"); -const utils = __importStar(require("../utils")); -const event_1 = require("../@types/event"); -const event_2 = require("./event"); -const partials_1 = require("../@types/partials"); -const typed_event_emitter_1 = require("./typed-event-emitter"); -const beacon_1 = require("./beacon"); -const ReEmitter_1 = require("../ReEmitter"); -const beacon_2 = require("../@types/beacon"); -// possible statuses for out-of-band member loading -var OobStatus; -(function (OobStatus) { - OobStatus[OobStatus["NotStarted"] = 0] = "NotStarted"; - OobStatus[OobStatus["InProgress"] = 1] = "InProgress"; - OobStatus[OobStatus["Finished"] = 2] = "Finished"; -})(OobStatus || (OobStatus = {})); -var RoomStateEvent; -(function (RoomStateEvent) { - RoomStateEvent["Events"] = "RoomState.events"; - RoomStateEvent["Members"] = "RoomState.members"; - RoomStateEvent["NewMember"] = "RoomState.newMember"; - RoomStateEvent["Update"] = "RoomState.update"; - RoomStateEvent["BeaconLiveness"] = "RoomState.BeaconLiveness"; - RoomStateEvent["Marker"] = "RoomState.Marker"; -})(RoomStateEvent = exports.RoomStateEvent || (exports.RoomStateEvent = {})); -class RoomState extends typed_event_emitter_1.TypedEventEmitter { - /** - * Construct room state. - * - * Room State represents the state of the room at a given point. - * It can be mutated by adding state events to it. - * There are two types of room member associated with a state event: - * normal member objects (accessed via getMember/getMembers) which mutate - * with the state to represent the current state of that room/user, e.g. - * the object returned by `getMember('@bob:example.com')` will mutate to - * get a different display name if Bob later changes his display name - * in the room. - * There are also 'sentinel' members (accessed via getSentinelMember). - * These also represent the state of room members at the point in time - * represented by the RoomState object, but unlike objects from getMember, - * sentinel objects will always represent the room state as at the time - * getSentinelMember was called, so if Bob subsequently changes his display - * name, a room member object previously acquired with getSentinelMember - * will still have his old display name. Calling getSentinelMember again - * after the display name change will return a new RoomMember object - * with Bob's new display name. - * - * @param roomId - Optional. The ID of the room which has this state. - * If none is specified it just tracks paginationTokens, useful for notifTimelineSet - * @param oobMemberFlags - Optional. The state of loading out of bound members. - * As the timeline might get reset while they are loading, this state needs to be inherited - * and shared when the room state is cloned for the new timeline. - * This should only be passed from clone. - */ - constructor(roomId, oobMemberFlags = { status: OobStatus.NotStarted }) { - super(); - this.roomId = roomId; - this.oobMemberFlags = oobMemberFlags; - this.reEmitter = new ReEmitter_1.TypedReEmitter(this); - this.sentinels = {}; // userId: RoomMember - // stores fuzzy matches to a list of userIDs (applies utils.removeHiddenChars to keys) - this.displayNameToUserIds = new Map(); - this.userIdsToDisplayNames = {}; - this.tokenToInvite = {}; // 3pid invite state_key to m.room.member invite - this.joinedMemberCount = null; // cache of the number of joined members - // joined members count from summary api - // once set, we know the server supports the summary api - // and we should only trust that - // we could also only trust that before OOB members - // are loaded but doesn't seem worth the hassle atm - this.summaryJoinedMemberCount = null; - // same for invited member count - this.invitedMemberCount = null; - this.summaryInvitedMemberCount = null; - this.modified = -1; - // XXX: Should be read-only - // The room member dictionary, keyed on the user's ID. - this.members = {}; // userId: RoomMember - // The state events dictionary, keyed on the event type and then the state_key value. - this.events = new Map(); // Map> - // The pagination token for this state. - this.paginationToken = null; - this.beacons = new Map(); - this._liveBeaconIds = []; - this.updateModifiedTime(); - } - /** - * Returns the number of joined members in this room - * This method caches the result. - * @returns The number of members in this room whose membership is 'join' - */ - getJoinedMemberCount() { - if (this.summaryJoinedMemberCount !== null) { - return this.summaryJoinedMemberCount; - } - if (this.joinedMemberCount === null) { - this.joinedMemberCount = this.getMembers().reduce((count, m) => { - return m.membership === "join" ? count + 1 : count; - }, 0); - } - return this.joinedMemberCount; - } - /** - * Set the joined member count explicitly (like from summary part of the sync response) - * @param count - the amount of joined members - */ - setJoinedMemberCount(count) { - this.summaryJoinedMemberCount = count; - } - /** - * Returns the number of invited members in this room - * @returns The number of members in this room whose membership is 'invite' - */ - getInvitedMemberCount() { - if (this.summaryInvitedMemberCount !== null) { - return this.summaryInvitedMemberCount; - } - if (this.invitedMemberCount === null) { - this.invitedMemberCount = this.getMembers().reduce((count, m) => { - return m.membership === "invite" ? count + 1 : count; - }, 0); - } - return this.invitedMemberCount; - } - /** - * Set the amount of invited members in this room - * @param count - the amount of invited members - */ - setInvitedMemberCount(count) { - this.summaryInvitedMemberCount = count; - } - /** - * Get all RoomMembers in this room. - * @returns A list of RoomMembers. - */ - getMembers() { - return Object.values(this.members); - } - /** - * Get all RoomMembers in this room, excluding the user IDs provided. - * @param excludedIds - The user IDs to exclude. - * @returns A list of RoomMembers. - */ - getMembersExcept(excludedIds) { - return this.getMembers().filter((m) => !excludedIds.includes(m.userId)); - } - /** - * Get a room member by their user ID. - * @param userId - The room member's user ID. - * @returns The member or null if they do not exist. - */ - getMember(userId) { - return this.members[userId] || null; - } - /** - * Get a room member whose properties will not change with this room state. You - * typically want this if you want to attach a RoomMember to a MatrixEvent which - * may no longer be represented correctly by Room.currentState or Room.oldState. - * The term 'sentinel' refers to the fact that this RoomMember is an unchanging - * guardian for state at this particular point in time. - * @param userId - The room member's user ID. - * @returns The member or null if they do not exist. - */ - getSentinelMember(userId) { - if (!userId) - return null; - let sentinel = this.sentinels[userId]; - if (sentinel === undefined) { - sentinel = new room_member_1.RoomMember(this.roomId, userId); - const member = this.members[userId]; - if (member === null || member === void 0 ? void 0 : member.events.member) { - sentinel.setMembershipEvent(member.events.member, this); - } - this.sentinels[userId] = sentinel; - } - return sentinel; - } - getStateEvents(eventType, stateKey) { - if (!this.events.has(eventType)) { - // no match - return stateKey === undefined ? [] : null; - } - if (stateKey === undefined) { - // return all values - return Array.from(this.events.get(eventType).values()); - } - const event = this.events.get(eventType).get(stateKey); - return event ? event : null; - } - get hasLiveBeacons() { - var _a; - return !!((_a = this.liveBeaconIds) === null || _a === void 0 ? void 0 : _a.length); - } - get liveBeaconIds() { - return this._liveBeaconIds; - } - /** - * Creates a copy of this room state so that mutations to either won't affect the other. - * @returns the copy of the room state - */ - clone() { - const copy = new RoomState(this.roomId, this.oobMemberFlags); - // Ugly hack: because setStateEvents will mark - // members as susperseding future out of bound members - // if loading is in progress (through oobMemberFlags) - // since these are not new members, we're merely copying them - // set the status to not started - // after copying, we set back the status - const status = this.oobMemberFlags.status; - this.oobMemberFlags.status = OobStatus.NotStarted; - Array.from(this.events.values()).forEach((eventsByStateKey) => { - copy.setStateEvents(Array.from(eventsByStateKey.values())); - }); - // Ugly hack: see above - this.oobMemberFlags.status = status; - if (this.summaryInvitedMemberCount !== null) { - copy.setInvitedMemberCount(this.getInvitedMemberCount()); - } - if (this.summaryJoinedMemberCount !== null) { - copy.setJoinedMemberCount(this.getJoinedMemberCount()); - } - // copy out of band flags if needed - if (this.oobMemberFlags.status == OobStatus.Finished) { - // copy markOutOfBand flags - this.getMembers().forEach((member) => { - var _a; - if (member.isOutOfBand()) { - (_a = copy.getMember(member.userId)) === null || _a === void 0 ? void 0 : _a.markOutOfBand(); - } - }); - } - return copy; - } - /** - * Add previously unknown state events. - * When lazy loading members while back-paginating, - * the relevant room state for the timeline chunk at the end - * of the chunk can be set with this method. - * @param events - state events to prepend - */ - setUnknownStateEvents(events) { - const unknownStateEvents = events.filter((event) => { - return !this.events.has(event.getType()) || !this.events.get(event.getType()).has(event.getStateKey()); - }); - this.setStateEvents(unknownStateEvents); - } - /** - * Add an array of one or more state MatrixEvents, overwriting any existing - * state with the same `{type, stateKey}` tuple. Will fire "RoomState.events" - * for every event added. May fire "RoomState.members" if there are - * `m.room.member` events. May fire "RoomStateEvent.Marker" if there are - * `UNSTABLE_MSC2716_MARKER` events. - * @param stateEvents - a list of state events for this room. - * - * @remarks - * Fires {@link RoomStateEvent.Members} - * Fires {@link RoomStateEvent.NewMember} - * Fires {@link RoomStateEvent.Events} - * Fires {@link RoomStateEvent.Marker} - */ - setStateEvents(stateEvents, markerFoundOptions) { - this.updateModifiedTime(); - // update the core event dict - stateEvents.forEach((event) => { - var _a; - if (event.getRoomId() !== this.roomId || !event.isState()) - return; - if (beacon_2.M_BEACON_INFO.matches(event.getType())) { - this.setBeacon(event); - } - const lastStateEvent = this.getStateEventMatching(event); - this.setStateEvent(event); - if (event.getType() === event_1.EventType.RoomMember) { - this.updateDisplayNameCache(event.getStateKey(), (_a = event.getContent().displayname) !== null && _a !== void 0 ? _a : ""); - this.updateThirdPartyTokenCache(event); - } - this.emit(RoomStateEvent.Events, event, this, lastStateEvent); - }); - this.onBeaconLivenessChange(); - // update higher level data structures. This needs to be done AFTER the - // core event dict as these structures may depend on other state events in - // the given array (e.g. disambiguating display names in one go to do both - // clashing names rather than progressively which only catches 1 of them). - stateEvents.forEach((event) => { - if (event.getRoomId() !== this.roomId || !event.isState()) - return; - if (event.getType() === event_1.EventType.RoomMember) { - const userId = event.getStateKey(); - // leave events apparently elide the displayname or avatar_url, - // so let's fake one up so that we don't leak user ids - // into the timeline - if (event.getContent().membership === "leave" || event.getContent().membership === "ban") { - event.getContent().avatar_url = event.getContent().avatar_url || event.getPrevContent().avatar_url; - event.getContent().displayname = - event.getContent().displayname || event.getPrevContent().displayname; - } - const member = this.getOrCreateMember(userId, event); - member.setMembershipEvent(event, this); - this.updateMember(member); - this.emit(RoomStateEvent.Members, event, this, member); - } - else if (event.getType() === event_1.EventType.RoomPowerLevels) { - // events with unknown state keys should be ignored - // and should not aggregate onto members power levels - if (event.getStateKey() !== "") { - return; - } - const members = Object.values(this.members); - members.forEach((member) => { - // We only propagate `RoomState.members` event if the - // power levels has been changed - // large room suffer from large re-rendering especially when not needed - const oldLastModified = member.getLastModifiedTime(); - member.setPowerLevelEvent(event); - if (oldLastModified !== member.getLastModifiedTime()) { - this.emit(RoomStateEvent.Members, event, this, member); - } - }); - // assume all our sentinels are now out-of-date - this.sentinels = {}; - } - else if (event_1.UNSTABLE_MSC2716_MARKER.matches(event.getType())) { - this.emit(RoomStateEvent.Marker, event, markerFoundOptions); - } - }); - this.emit(RoomStateEvent.Update, this); - } - processBeaconEvents(events, matrixClient) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (!events.length || - // discard locations if we have no beacons - !this.beacons.size) { - return; - } - const beaconByEventIdDict = [...this.beacons.values()].reduce((dict, beacon) => { - dict[beacon.beaconInfoId] = beacon; - return dict; - }, {}); - const processBeaconRelation = (beaconInfoEventId, event) => { - if (!beacon_2.M_BEACON.matches(event.getType())) { - return; - } - const beacon = beaconByEventIdDict[beaconInfoEventId]; - if (beacon) { - beacon.addLocations([event]); - } - }; - for (const event of events) { - const relatedToEventId = (_a = event.getRelation()) === null || _a === void 0 ? void 0 : _a.event_id; - // not related to a beacon we know about; discard - if (!relatedToEventId || !beaconByEventIdDict[relatedToEventId]) - return; - if (!beacon_2.M_BEACON.matches(event.getType()) && !event.isEncrypted()) - return; - try { - yield matrixClient.decryptEventIfNeeded(event); - processBeaconRelation(relatedToEventId, event); - } - catch (_b) { - if (event.isDecryptionFailure()) { - // add an event listener for once the event is decrypted. - event.once(event_2.MatrixEventEvent.Decrypted, () => __awaiter(this, void 0, void 0, function* () { - processBeaconRelation(relatedToEventId, event); - })); - } - } - } - }); - } - /** - * Looks up a member by the given userId, and if it doesn't exist, - * create it and emit the `RoomState.newMember` event. - * This method makes sure the member is added to the members dictionary - * before emitting, as this is done from setStateEvents and setOutOfBandMember. - * @param userId - the id of the user to look up - * @param event - the membership event for the (new) member. Used to emit. - * @returns the member, existing or newly created. - * - * @remarks - * Fires {@link RoomStateEvent.NewMember} - */ - getOrCreateMember(userId, event) { - let member = this.members[userId]; - if (!member) { - member = new room_member_1.RoomMember(this.roomId, userId); - // add member to members before emitting any events, - // as event handlers often lookup the member - this.members[userId] = member; - this.emit(RoomStateEvent.NewMember, event, this, member); - } - return member; - } - setStateEvent(event) { - if (!this.events.has(event.getType())) { - this.events.set(event.getType(), new Map()); - } - this.events.get(event.getType()).set(event.getStateKey(), event); - } - /** - * @experimental - */ - setBeacon(event) { - var _a; - const beaconIdentifier = (0, beacon_1.getBeaconInfoIdentifier)(event); - if (this.beacons.has(beaconIdentifier)) { - const beacon = this.beacons.get(beaconIdentifier); - if (event.isRedacted()) { - if (beacon.beaconInfoId === ((_a = event.getRedactionEvent()) === null || _a === void 0 ? void 0 : _a.redacts)) { - beacon.destroy(); - this.beacons.delete(beaconIdentifier); - } - return; - } - return beacon.update(event); - } - if (event.isRedacted()) { - return; - } - const beacon = new beacon_1.Beacon(event); - this.reEmitter.reEmit(beacon, [ - beacon_1.BeaconEvent.New, - beacon_1.BeaconEvent.Update, - beacon_1.BeaconEvent.Destroy, - beacon_1.BeaconEvent.LivenessChange, - ]); - this.emit(beacon_1.BeaconEvent.New, event, beacon); - beacon.on(beacon_1.BeaconEvent.LivenessChange, this.onBeaconLivenessChange.bind(this)); - beacon.on(beacon_1.BeaconEvent.Destroy, this.onBeaconLivenessChange.bind(this)); - this.beacons.set(beacon.identifier, beacon); - } - /** - * @experimental - * Check liveness of room beacons - * emit RoomStateEvent.BeaconLiveness event - */ - onBeaconLivenessChange() { - this._liveBeaconIds = Array.from(this.beacons.values()) - .filter((beacon) => beacon.isLive) - .map((beacon) => beacon.identifier); - this.emit(RoomStateEvent.BeaconLiveness, this, this.hasLiveBeacons); - } - getStateEventMatching(event) { - var _a, _b; - return (_b = (_a = this.events.get(event.getType())) === null || _a === void 0 ? void 0 : _a.get(event.getStateKey())) !== null && _b !== void 0 ? _b : null; - } - updateMember(member) { - // this member may have a power level already, so set it. - const pwrLvlEvent = this.getStateEvents(event_1.EventType.RoomPowerLevels, ""); - if (pwrLvlEvent) { - member.setPowerLevelEvent(pwrLvlEvent); - } - // blow away the sentinel which is now outdated - delete this.sentinels[member.userId]; - this.members[member.userId] = member; - this.joinedMemberCount = null; - this.invitedMemberCount = null; - } - /** - * Get the out-of-band members loading state, whether loading is needed or not. - * Note that loading might be in progress and hence isn't needed. - * @returns whether or not the members of this room need to be loaded - */ - needsOutOfBandMembers() { - return this.oobMemberFlags.status === OobStatus.NotStarted; - } - /** - * Check if loading of out-of-band-members has completed - * - * @returns true if the full membership list of this room has been loaded. False if it is not started or is in - * progress. - */ - outOfBandMembersReady() { - return this.oobMemberFlags.status === OobStatus.Finished; - } - /** - * Mark this room state as waiting for out-of-band members, - * ensuring it doesn't ask for them to be requested again - * through needsOutOfBandMembers - */ - markOutOfBandMembersStarted() { - if (this.oobMemberFlags.status !== OobStatus.NotStarted) { - return; - } - this.oobMemberFlags.status = OobStatus.InProgress; - } - /** - * Mark this room state as having failed to fetch out-of-band members - */ - markOutOfBandMembersFailed() { - if (this.oobMemberFlags.status !== OobStatus.InProgress) { - return; - } - this.oobMemberFlags.status = OobStatus.NotStarted; - } - /** - * Clears the loaded out-of-band members - */ - clearOutOfBandMembers() { - let count = 0; - Object.keys(this.members).forEach((userId) => { - const member = this.members[userId]; - if (member.isOutOfBand()) { - ++count; - delete this.members[userId]; - } - }); - logger_1.logger.log(`LL: RoomState removed ${count} members...`); - this.oobMemberFlags.status = OobStatus.NotStarted; - } - /** - * Sets the loaded out-of-band members. - * @param stateEvents - array of membership state events - */ - setOutOfBandMembers(stateEvents) { - logger_1.logger.log(`LL: RoomState about to set ${stateEvents.length} OOB members ...`); - if (this.oobMemberFlags.status !== OobStatus.InProgress) { - return; - } - logger_1.logger.log(`LL: RoomState put in finished state ...`); - this.oobMemberFlags.status = OobStatus.Finished; - stateEvents.forEach((e) => this.setOutOfBandMember(e)); - this.emit(RoomStateEvent.Update, this); - } - /** - * Sets a single out of band member, used by both setOutOfBandMembers and clone - * @param stateEvent - membership state event - */ - setOutOfBandMember(stateEvent) { - if (stateEvent.getType() !== event_1.EventType.RoomMember) { - return; - } - const userId = stateEvent.getStateKey(); - const existingMember = this.getMember(userId); - // never replace members received as part of the sync - if (existingMember && !existingMember.isOutOfBand()) { - return; - } - const member = this.getOrCreateMember(userId, stateEvent); - member.setMembershipEvent(stateEvent, this); - // needed to know which members need to be stored seperately - // as they are not part of the sync accumulator - // this is cleared by setMembershipEvent so when it's updated through /sync - member.markOutOfBand(); - this.updateDisplayNameCache(member.userId, member.name); - this.setStateEvent(stateEvent); - this.updateMember(member); - this.emit(RoomStateEvent.Members, stateEvent, this, member); - } - /** - * Set the current typing event for this room. - * @param event - The typing event - */ - setTypingEvent(event) { - Object.values(this.members).forEach(function (member) { - member.setTypingEvent(event); - }); - } - /** - * Get the m.room.member event which has the given third party invite token. - * - * @param token - The token - * @returns The m.room.member event or null - */ - getInviteForThreePidToken(token) { - return this.tokenToInvite[token] || null; - } - /** - * Update the last modified time to the current time. - */ - updateModifiedTime() { - this.modified = Date.now(); - } - /** - * Get the timestamp when this room state was last updated. This timestamp is - * updated when this object has received new state events. - * @returns The timestamp - */ - getLastModifiedTime() { - return this.modified; - } - /** - * Get user IDs with the specified or similar display names. - * @param displayName - The display name to get user IDs from. - * @returns An array of user IDs or an empty array. - */ - getUserIdsWithDisplayName(displayName) { - var _a; - return (_a = this.displayNameToUserIds.get(utils.removeHiddenChars(displayName))) !== null && _a !== void 0 ? _a : []; - } - /** - * Returns true if userId is in room, event is not redacted and either sender of - * mxEvent or has power level sufficient to redact events other than their own. - * @param mxEvent - The event to test permission for - * @param userId - The user ID of the user to test permission for - * @returns true if the given used ID can redact given event - */ - maySendRedactionForEvent(mxEvent, userId) { - const member = this.getMember(userId); - if (!member || member.membership === "leave") - return false; - if (mxEvent.status || mxEvent.isRedacted()) - return false; - // The user may have been the sender, but they can't redact their own message - // if redactions are blocked. - const canRedact = this.maySendEvent(event_1.EventType.RoomRedaction, userId); - if (mxEvent.getSender() === userId) - return canRedact; - return this.hasSufficientPowerLevelFor("redact", member.powerLevel); - } - /** - * Returns true if the given power level is sufficient for action - * @param action - The type of power level to check - * @param powerLevel - The power level of the member - * @returns true if the given power level is sufficient - */ - hasSufficientPowerLevelFor(action, powerLevel) { - const powerLevelsEvent = this.getStateEvents(event_1.EventType.RoomPowerLevels, ""); - let powerLevels = {}; - if (powerLevelsEvent) { - powerLevels = powerLevelsEvent.getContent(); - } - let requiredLevel = 50; - if (utils.isNumber(powerLevels[action])) { - requiredLevel = powerLevels[action]; - } - return powerLevel >= requiredLevel; - } - /** - * Short-form for maySendEvent('m.room.message', userId) - * @param userId - The user ID of the user to test permission for - * @returns true if the given user ID should be permitted to send - * message events into the given room. - */ - maySendMessage(userId) { - return this.maySendEventOfType(event_1.EventType.RoomMessage, userId, false); - } - /** - * Returns true if the given user ID has permission to send a normal - * event of type `eventType` into this room. - * @param eventType - The type of event to test - * @param userId - The user ID of the user to test permission for - * @returns true if the given user ID should be permitted to send - * the given type of event into this room, - * according to the room's state. - */ - maySendEvent(eventType, userId) { - return this.maySendEventOfType(eventType, userId, false); - } - /** - * Returns true if the given MatrixClient has permission to send a state - * event of type `stateEventType` into this room. - * @param stateEventType - The type of state events to test - * @param cli - The client to test permission for - * @returns true if the given client should be permitted to send - * the given type of state event into this room, - * according to the room's state. - */ - mayClientSendStateEvent(stateEventType, cli) { - if (cli.isGuest() || !cli.credentials.userId) { - return false; - } - return this.maySendStateEvent(stateEventType, cli.credentials.userId); - } - /** - * Returns true if the given user ID has permission to send a state - * event of type `stateEventType` into this room. - * @param stateEventType - The type of state events to test - * @param userId - The user ID of the user to test permission for - * @returns true if the given user ID should be permitted to send - * the given type of state event into this room, - * according to the room's state. - */ - maySendStateEvent(stateEventType, userId) { - return this.maySendEventOfType(stateEventType, userId, true); - } - /** - * Returns true if the given user ID has permission to send a normal or state - * event of type `eventType` into this room. - * @param eventType - The type of event to test - * @param userId - The user ID of the user to test permission for - * @param state - If true, tests if the user may send a state - event of this type. Otherwise tests whether - they may send a regular event. - * @returns true if the given user ID should be permitted to send - * the given type of event into this room, - * according to the room's state. - */ - maySendEventOfType(eventType, userId, state) { - const powerLevelsEvent = this.getStateEvents(event_1.EventType.RoomPowerLevels, ""); - let powerLevels; - let eventsLevels = {}; - let stateDefault = 0; - let eventsDefault = 0; - let powerLevel = 0; - if (powerLevelsEvent) { - powerLevels = powerLevelsEvent.getContent(); - eventsLevels = powerLevels.events || {}; - if (Number.isSafeInteger(powerLevels.state_default)) { - stateDefault = powerLevels.state_default; - } - else { - stateDefault = 50; - } - const userPowerLevel = powerLevels.users && powerLevels.users[userId]; - if (Number.isSafeInteger(userPowerLevel)) { - powerLevel = userPowerLevel; - } - else if (Number.isSafeInteger(powerLevels.users_default)) { - powerLevel = powerLevels.users_default; - } - if (Number.isSafeInteger(powerLevels.events_default)) { - eventsDefault = powerLevels.events_default; - } - } - let requiredLevel = state ? stateDefault : eventsDefault; - if (Number.isSafeInteger(eventsLevels[eventType])) { - requiredLevel = eventsLevels[eventType]; - } - return powerLevel >= requiredLevel; - } - /** - * Returns true if the given user ID has permission to trigger notification - * of type `notifLevelKey` - * @param notifLevelKey - The level of notification to test (eg. 'room') - * @param userId - The user ID of the user to test permission for - * @returns true if the given user ID has permission to trigger a - * notification of this type. - */ - mayTriggerNotifOfType(notifLevelKey, userId) { - const member = this.getMember(userId); - if (!member) { - return false; - } - const powerLevelsEvent = this.getStateEvents(event_1.EventType.RoomPowerLevels, ""); - let notifLevel = 50; - if (powerLevelsEvent && - powerLevelsEvent.getContent() && - powerLevelsEvent.getContent().notifications && - utils.isNumber(powerLevelsEvent.getContent().notifications[notifLevelKey])) { - notifLevel = powerLevelsEvent.getContent().notifications[notifLevelKey]; - } - return member.powerLevel >= notifLevel; - } - /** - * Returns the join rule based on the m.room.join_rule state event, defaulting to `invite`. - * @returns the join_rule applied to this room - */ - getJoinRule() { - var _a; - const joinRuleEvent = this.getStateEvents(event_1.EventType.RoomJoinRules, ""); - const joinRuleContent = (_a = joinRuleEvent === null || joinRuleEvent === void 0 ? void 0 : joinRuleEvent.getContent()) !== null && _a !== void 0 ? _a : {}; - return joinRuleContent["join_rule"] || partials_1.JoinRule.Invite; - } - /** - * Returns the history visibility based on the m.room.history_visibility state event, defaulting to `shared`. - * @returns the history_visibility applied to this room - */ - getHistoryVisibility() { - var _a; - const historyVisibilityEvent = this.getStateEvents(event_1.EventType.RoomHistoryVisibility, ""); - const historyVisibilityContent = (_a = historyVisibilityEvent === null || historyVisibilityEvent === void 0 ? void 0 : historyVisibilityEvent.getContent()) !== null && _a !== void 0 ? _a : {}; - return historyVisibilityContent["history_visibility"] || partials_1.HistoryVisibility.Shared; - } - /** - * Returns the guest access based on the m.room.guest_access state event, defaulting to `shared`. - * @returns the guest_access applied to this room - */ - getGuestAccess() { - var _a; - const guestAccessEvent = this.getStateEvents(event_1.EventType.RoomGuestAccess, ""); - const guestAccessContent = (_a = guestAccessEvent === null || guestAccessEvent === void 0 ? void 0 : guestAccessEvent.getContent()) !== null && _a !== void 0 ? _a : {}; - return guestAccessContent["guest_access"] || partials_1.GuestAccess.Forbidden; - } - /** - * Find the predecessor room based on this room state. - * - * @param msc3946ProcessDynamicPredecessor - if true, look for an - * m.room.predecessor state event and use it if found (MSC3946). - * @returns null if this room has no predecessor. Otherwise, returns - * the roomId, last eventId and viaServers of the predecessor room. - * - * If msc3946ProcessDynamicPredecessor is true, use m.predecessor events - * as well as m.room.create events to find predecessors. - * - * Note: if an m.predecessor event is used, eventId may be undefined - * since last_known_event_id is optional. - * - * Note: viaServers may be undefined, and will definitely be undefined if - * this predecessor comes from a RoomCreate event (rather than a - * RoomPredecessor, which has the optional via_servers property). - */ - findPredecessor(msc3946ProcessDynamicPredecessor = false) { - // Note: the tests for this function are against Room.findPredecessor, - // which just calls through to here. - if (msc3946ProcessDynamicPredecessor) { - const predecessorEvent = this.getStateEvents(event_1.EventType.RoomPredecessor, ""); - if (predecessorEvent) { - const content = predecessorEvent.getContent(); - const roomId = content.predecessor_room_id; - let eventId = content.last_known_event_id; - if (typeof eventId !== "string") { - eventId = undefined; - } - let viaServers = content.via_servers; - if (!Array.isArray(viaServers)) { - viaServers = undefined; - } - if (typeof roomId === "string") { - return { roomId, eventId, viaServers }; - } - } - } - const createEvent = this.getStateEvents(event_1.EventType.RoomCreate, ""); - if (createEvent) { - const predecessor = createEvent.getContent()["predecessor"]; - if (predecessor) { - const roomId = predecessor["room_id"]; - if (typeof roomId === "string") { - let eventId = predecessor["event_id"]; - if (typeof eventId !== "string" || eventId === "") { - eventId = undefined; - } - return { roomId, eventId }; - } - } - } - return null; - } - updateThirdPartyTokenCache(memberEvent) { - if (!memberEvent.getContent().third_party_invite) { - return; - } - const token = (memberEvent.getContent().third_party_invite.signed || {}).token; - if (!token) { - return; - } - const threePidInvite = this.getStateEvents(event_1.EventType.RoomThirdPartyInvite, token); - if (!threePidInvite) { - return; - } - this.tokenToInvite[token] = memberEvent; - } - updateDisplayNameCache(userId, displayName) { - var _a; - const oldName = this.userIdsToDisplayNames[userId]; - delete this.userIdsToDisplayNames[userId]; - if (oldName) { - // Remove the old name from the cache. - // We clobber the user_id > name lookup but the name -> [user_id] lookup - // means we need to remove that user ID from that array rather than nuking - // the lot. - const strippedOldName = utils.removeHiddenChars(oldName); - const existingUserIds = this.displayNameToUserIds.get(strippedOldName); - if (existingUserIds) { - // remove this user ID from this array - const filteredUserIDs = existingUserIds.filter((id) => id !== userId); - this.displayNameToUserIds.set(strippedOldName, filteredUserIDs); - } - } - this.userIdsToDisplayNames[userId] = displayName; - const strippedDisplayname = displayName && utils.removeHiddenChars(displayName); - // an empty stripped displayname (undefined/'') will be set to MXID in room-member.js - if (strippedDisplayname) { - const arr = (_a = this.displayNameToUserIds.get(strippedDisplayname)) !== null && _a !== void 0 ? _a : []; - arr.push(userId); - this.displayNameToUserIds.set(strippedDisplayname, arr); - } - } -} -exports.RoomState = RoomState; - -},{"../@types/beacon":305,"../@types/event":306,"../@types/partials":309,"../ReEmitter":317,"../logger":374,"../utils":416,"./beacon":378,"./event":383,"./room-member":389,"./typed-event-emitter":395}],391:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoomSummary = void 0; -/** - * Construct a new Room Summary. A summary can be used for display on a recent - * list, without having to load the entire room list into memory. - * @param roomId - Required. The ID of this room. - * @param info - Optional. The summary info. Additional keys are supported. - */ -class RoomSummary { - constructor(roomId, info) { - this.roomId = roomId; - } -} -exports.RoomSummary = RoomSummary; - -},{}],392:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoomNameType = exports.Room = exports.RoomEvent = exports.NotificationCountType = exports.KNOWN_SAFE_ROOM_VERSION = void 0; -const matrix_events_sdk_1 = require("matrix-events-sdk"); -const event_timeline_set_1 = require("./event-timeline-set"); -const event_timeline_1 = require("./event-timeline"); -const content_repo_1 = require("../content-repo"); -const utils = __importStar(require("../utils")); -const utils_1 = require("../utils"); -const event_1 = require("./event"); -const event_status_1 = require("./event-status"); -const room_member_1 = require("./room-member"); -const room_summary_1 = require("./room-summary"); -const logger_1 = require("../logger"); -const ReEmitter_1 = require("../ReEmitter"); -const event_2 = require("../@types/event"); -const client_1 = require("../client"); -const filter_1 = require("../filter"); -const room_state_1 = require("./room-state"); -const beacon_1 = require("./beacon"); -const thread_1 = require("./thread"); -const read_receipts_1 = require("../@types/read_receipts"); -const relations_container_1 = require("./relations-container"); -const read_receipt_1 = require("./read-receipt"); -const poll_1 = require("./poll"); -// These constants are used as sane defaults when the homeserver doesn't support -// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be -// the same as the common default room version whereas SAFE_ROOM_VERSIONS are the -// room versions which are considered okay for people to run without being asked -// to upgrade (ie: "stable"). Eventually, we should remove these when all homeservers -// return an m.room_versions capability. -exports.KNOWN_SAFE_ROOM_VERSION = "9"; -const SAFE_ROOM_VERSIONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]; -// When inserting a visibility event affecting event `eventId`, we -// need to scan through existing visibility events for `eventId`. -// In theory, this could take an unlimited amount of time if: -// -// - the visibility event was sent by a moderator; and -// - `eventId` already has many visibility changes (usually, it should -// be 2 or less); and -// - for some reason, the visibility changes are received out of order -// (usually, this shouldn't happen at all). -// -// For this reason, we limit the number of events to scan through, -// expecting that a broken visibility change for a single event in -// an extremely uncommon case (possibly a DoS) is a small -// price to pay to keep matrix-js-sdk responsive. -const MAX_NUMBER_OF_VISIBILITY_EVENTS_TO_SCAN_THROUGH = 30; -var NotificationCountType; -(function (NotificationCountType) { - NotificationCountType["Highlight"] = "highlight"; - NotificationCountType["Total"] = "total"; -})(NotificationCountType = exports.NotificationCountType || (exports.NotificationCountType = {})); -var RoomEvent; -(function (RoomEvent) { - RoomEvent["MyMembership"] = "Room.myMembership"; - RoomEvent["Tags"] = "Room.tags"; - RoomEvent["AccountData"] = "Room.accountData"; - RoomEvent["Receipt"] = "Room.receipt"; - RoomEvent["Name"] = "Room.name"; - RoomEvent["Redaction"] = "Room.redaction"; - RoomEvent["RedactionCancelled"] = "Room.redactionCancelled"; - RoomEvent["LocalEchoUpdated"] = "Room.localEchoUpdated"; - RoomEvent["Timeline"] = "Room.timeline"; - RoomEvent["TimelineReset"] = "Room.timelineReset"; - RoomEvent["TimelineRefresh"] = "Room.TimelineRefresh"; - RoomEvent["OldStateUpdated"] = "Room.OldStateUpdated"; - RoomEvent["CurrentStateUpdated"] = "Room.CurrentStateUpdated"; - RoomEvent["HistoryImportedWithinTimeline"] = "Room.historyImportedWithinTimeline"; - RoomEvent["UnreadNotifications"] = "Room.UnreadNotifications"; -})(RoomEvent = exports.RoomEvent || (exports.RoomEvent = {})); -class Room extends read_receipt_1.ReadReceipt { - /** - * Construct a new Room. - * - *

For a room, we store an ordered sequence of timelines, which may or may not - * be continuous. Each timeline lists a series of events, as well as tracking - * the room state at the start and the end of the timeline. It also tracks - * forward and backward pagination tokens, as well as containing links to the - * next timeline in the sequence. - * - *

There is one special timeline - the 'live' timeline, which represents the - * timeline to which events are being added in real-time as they are received - * from the /sync API. Note that you should not retain references to this - * timeline - even if it is the current timeline right now, it may not remain - * so if the server gives us a timeline gap in /sync. - * - *

In order that we can find events from their ids later, we also maintain a - * map from event_id to timeline and index. - * - * @param roomId - Required. The ID of this room. - * @param client - Required. The client, used to lazy load members. - * @param myUserId - Required. The ID of the syncing user. - * @param opts - Configuration options - */ - constructor(roomId, client, myUserId, opts = {}) { - super(); - this.roomId = roomId; - this.client = client; - this.myUserId = myUserId; - this.opts = opts; - this.txnToEvent = new Map(); // Pending in-flight requests { string: MatrixEvent } - this.notificationCounts = {}; - this.threadNotifications = new Map(); - this.cachedThreadReadReceipts = new Map(); - // Useful to know at what point the current user has started using threads in this room - this.oldestThreadedReceiptTs = Infinity; - /** - * A record of the latest unthread receipts per user - * This is useful in determining whether a user has read a thread or not - */ - this.unthreadedReceipts = new Map(); - this.polls = new Map(); - this.threadsTimelineSets = []; - // any filtered timeline sets we're maintaining for this room - this.filteredTimelineSets = {}; // filter_id: timelineSet - this.timelineNeedsRefresh = false; - this.summaryHeroes = null; - // flags to stop logspam about missing m.room.create events - this.getTypeWarning = false; - this.getVersionWarning = false; - /** - * Dict of room tags; the keys are the tag name and the values - * are any metadata associated with the tag - e.g. `{ "fav" : { order: 1 } }` - */ - this.tags = {}; // $tagName: { $metadata: $value } - /** - * accountData Dict of per-room account_data events; the keys are the - * event type and the values are the events. - */ - this.accountData = new Map(); // $eventType: $event - /** - * The room summary. - */ - this.summary = null; - this.relations = new relations_container_1.RelationsContainer(this.client, this); - /** - * A collection of events known by the client - * This is not a comprehensive list of the threads that exist in this room - */ - this.threads = new Map(); - /** - * A mapping of eventId to all visibility changes to apply - * to the event, by chronological order, as per - * https://github.com/matrix-org/matrix-doc/pull/3531 - * - * # Invariants - * - * - within each list, all events are classed by - * chronological order; - * - all events are events such that - * `asVisibilityEvent()` returns a non-null `IVisibilityChange`; - * - within each list with key `eventId`, all events - * are in relation to `eventId`. - * - * @experimental - */ - this.visibilityEvents = new Map(); - this.threadTimelineSetsPromise = null; - this.threadsReady = false; - this.updateThreadRootEvents = (thread, toStartOfTimeline, recreateEvent) => { - var _a, _b; - if (thread.length) { - this.updateThreadRootEvent((_a = this.threadsTimelineSets) === null || _a === void 0 ? void 0 : _a[0], thread, toStartOfTimeline, recreateEvent); - if (thread.hasCurrentUserParticipated) { - this.updateThreadRootEvent((_b = this.threadsTimelineSets) === null || _b === void 0 ? void 0 : _b[1], thread, toStartOfTimeline, recreateEvent); - } - } - }; - this.updateThreadRootEvent = (timelineSet, thread, toStartOfTimeline, recreateEvent) => { - if (timelineSet && thread.rootEvent) { - if (recreateEvent) { - timelineSet.removeEvent(thread.id); - } - if (thread_1.Thread.hasServerSideSupport) { - timelineSet.addLiveEvent(thread.rootEvent, { - duplicateStrategy: event_timeline_set_1.DuplicateStrategy.Replace, - fromCache: false, - roomState: this.currentState, - }); - } - else { - timelineSet.addEventToTimeline(thread.rootEvent, timelineSet.getLiveTimeline(), { toStartOfTimeline }); - } - } - }; - this.applyRedaction = (event) => { - if (event.isRedaction()) { - const redactId = event.event.redacts; - // if we know about this event, redact its contents now. - const redactedEvent = redactId ? this.findEventById(redactId) : undefined; - if (redactedEvent) { - redactedEvent.makeRedacted(event); - // If this is in the current state, replace it with the redacted version - if (redactedEvent.isState()) { - const currentStateEvent = this.currentState.getStateEvents(redactedEvent.getType(), redactedEvent.getStateKey()); - if ((currentStateEvent === null || currentStateEvent === void 0 ? void 0 : currentStateEvent.getId()) === redactedEvent.getId()) { - this.currentState.setStateEvents([redactedEvent]); - } - } - this.emit(RoomEvent.Redaction, event, this); - // TODO: we stash user displaynames (among other things) in - // RoomMember objects which are then attached to other events - // (in the sender and target fields). We should get those - // RoomMember objects to update themselves when the events that - // they are based on are changed. - // Remove any visibility change on this event. - this.visibilityEvents.delete(redactId); - // If this event is a visibility change event, remove it from the - // list of visibility changes and update any event affected by it. - if (redactedEvent.isVisibilityEvent()) { - this.redactVisibilityChangeEvent(event); - } - } - // FIXME: apply redactions to notification list - // NB: We continue to add the redaction event to the timeline so - // clients can say "so and so redacted an event" if they wish to. Also - // this may be needed to trigger an update. - } - }; - // In some cases, we add listeners for every displayed Matrix event, so it's - // common to have quite a few more than the default limit. - this.setMaxListeners(100); - this.reEmitter = new ReEmitter_1.TypedReEmitter(this); - opts.pendingEventOrdering = opts.pendingEventOrdering || client_1.PendingEventOrdering.Chronological; - this.name = roomId; - this.normalizedName = roomId; - // all our per-room timeline sets. the first one is the unfiltered ones; - // the subsequent ones are the filtered ones in no particular order. - this.timelineSets = [new event_timeline_set_1.EventTimelineSet(this, opts)]; - this.reEmitter.reEmit(this.getUnfilteredTimelineSet(), [RoomEvent.Timeline, RoomEvent.TimelineReset]); - this.fixUpLegacyTimelineFields(); - if (this.opts.pendingEventOrdering === client_1.PendingEventOrdering.Detached) { - this.pendingEventList = []; - this.client.store.getPendingEvents(this.roomId).then((events) => { - const mapper = this.client.getEventMapper({ - toDevice: false, - decrypt: false, - }); - events.forEach((serializedEvent) => __awaiter(this, void 0, void 0, function* () { - const event = mapper(serializedEvent); - yield client.decryptEventIfNeeded(event); - event.setStatus(event_status_1.EventStatus.NOT_SENT); - this.addPendingEvent(event, event.getTxnId()); - })); - }); - } - // awaited by getEncryptionTargetMembers while room members are loading - if (!this.opts.lazyLoadMembers) { - this.membersPromise = Promise.resolve(false); - } - else { - this.membersPromise = undefined; - } - } - createThreadsTimelineSets() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (this.threadTimelineSetsPromise) { - return this.threadTimelineSetsPromise; - } - if ((_a = this.client) === null || _a === void 0 ? void 0 : _a.supportsThreads()) { - try { - this.threadTimelineSetsPromise = Promise.all([ - this.createThreadTimelineSet(), - this.createThreadTimelineSet(thread_1.ThreadFilterType.My), - ]); - const timelineSets = yield this.threadTimelineSetsPromise; - this.threadsTimelineSets.push(...timelineSets); - return timelineSets; - } - catch (e) { - this.threadTimelineSetsPromise = null; - return null; - } - } - return null; - }); - } - /** - * Bulk decrypt critical events in a room - * - * Critical events represents the minimal set of events to decrypt - * for a typical UI to function properly - * - * - Last event of every room (to generate likely message preview) - * - All events up to the read receipt (to calculate an accurate notification count) - * - * @returns Signals when all events have been decrypted - */ - decryptCriticalEvents() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.client.isCryptoEnabled()) - return; - const readReceiptEventId = this.getEventReadUpTo(this.client.getUserId(), true); - const events = this.getLiveTimeline().getEvents(); - const readReceiptTimelineIndex = events.findIndex((matrixEvent) => { - return matrixEvent.event.event_id === readReceiptEventId; - }); - const decryptionPromises = events - .slice(readReceiptTimelineIndex) - .reverse() - .map((event) => this.client.decryptEventIfNeeded(event, { isRetry: true })); - yield Promise.allSettled(decryptionPromises); - }); - } - /** - * Bulk decrypt events in a room - * - * @returns Signals when all events have been decrypted - */ - decryptAllEvents() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.client.isCryptoEnabled()) - return; - const decryptionPromises = this.getUnfilteredTimelineSet() - .getLiveTimeline() - .getEvents() - .slice(0) // copy before reversing - .reverse() - .map((event) => this.client.decryptEventIfNeeded(event, { isRetry: true })); - yield Promise.allSettled(decryptionPromises); - }); - } - /** - * Gets the creator of the room - * @returns The creator of the room, or null if it could not be determined - */ - getCreator() { - var _a; - const createEvent = this.currentState.getStateEvents(event_2.EventType.RoomCreate, ""); - return (_a = createEvent === null || createEvent === void 0 ? void 0 : createEvent.getContent()["creator"]) !== null && _a !== void 0 ? _a : null; - } - /** - * Gets the version of the room - * @returns The version of the room, or null if it could not be determined - */ - getVersion() { - var _a; - const createEvent = this.currentState.getStateEvents(event_2.EventType.RoomCreate, ""); - if (!createEvent) { - if (!this.getVersionWarning) { - logger_1.logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event"); - this.getVersionWarning = true; - } - return "1"; - } - return (_a = createEvent.getContent()["room_version"]) !== null && _a !== void 0 ? _a : "1"; - } - /** - * Determines whether this room needs to be upgraded to a new version - * @returns What version the room should be upgraded to, or null if - * the room does not require upgrading at this time. - * @deprecated Use #getRecommendedVersion() instead - */ - shouldUpgradeToVersion() { - // TODO: Remove this function. - // This makes assumptions about which versions are safe, and can easily - // be wrong. Instead, people are encouraged to use getRecommendedVersion - // which determines a safer value. This function doesn't use that function - // because this is not async-capable, and to avoid breaking the contract - // we're deprecating this. - if (!SAFE_ROOM_VERSIONS.includes(this.getVersion())) { - return exports.KNOWN_SAFE_ROOM_VERSION; - } - return null; - } - /** - * Determines the recommended room version for the room. This returns an - * object with 3 properties: `version` as the new version the - * room should be upgraded to (may be the same as the current version); - * `needsUpgrade` to indicate if the room actually can be - * upgraded (ie: does the current version not match?); and `urgent` - * to indicate if the new version patches a vulnerability in a previous - * version. - * @returns - * Resolves to the version the room should be upgraded to. - */ - getRecommendedVersion() { - return __awaiter(this, void 0, void 0, function* () { - const capabilities = yield this.client.getCapabilities(); - let versionCap = capabilities["m.room_versions"]; - if (!versionCap) { - versionCap = { - default: exports.KNOWN_SAFE_ROOM_VERSION, - available: {}, - }; - for (const safeVer of SAFE_ROOM_VERSIONS) { - versionCap.available[safeVer] = client_1.RoomVersionStability.Stable; - } - } - let result = this.checkVersionAgainstCapability(versionCap); - if (result.urgent && result.needsUpgrade) { - // Something doesn't feel right: we shouldn't need to update - // because the version we're on should be in the protocol's - // namespace. This usually means that the server was updated - // before the client was, making us think the newest possible - // room version is not stable. As a solution, we'll refresh - // the capability we're using to determine this. - logger_1.logger.warn("Refreshing room version capability because the server looks " + - "to be supporting a newer room version we don't know about."); - const caps = yield this.client.getCapabilities(true); - versionCap = caps["m.room_versions"]; - if (!versionCap) { - logger_1.logger.warn("No room version capability - assuming upgrade required."); - return result; - } - else { - result = this.checkVersionAgainstCapability(versionCap); - } - } - return result; - }); - } - checkVersionAgainstCapability(versionCap) { - const currentVersion = this.getVersion(); - logger_1.logger.log(`[${this.roomId}] Current version: ${currentVersion}`); - logger_1.logger.log(`[${this.roomId}] Version capability: `, versionCap); - const result = { - version: currentVersion, - needsUpgrade: false, - urgent: false, - }; - // If the room is on the default version then nothing needs to change - if (currentVersion === versionCap.default) - return result; - const stableVersions = Object.keys(versionCap.available).filter((v) => versionCap.available[v] === "stable"); - // Check if the room is on an unstable version. We determine urgency based - // off the version being in the Matrix spec namespace or not (if the version - // is in the current namespace and unstable, the room is probably vulnerable). - if (!stableVersions.includes(currentVersion)) { - result.version = versionCap.default; - result.needsUpgrade = true; - result.urgent = !!this.getVersion().match(/^[0-9]+[0-9.]*$/g); - if (result.urgent) { - logger_1.logger.warn(`URGENT upgrade required on ${this.roomId}`); - } - else { - logger_1.logger.warn(`Non-urgent upgrade required on ${this.roomId}`); - } - return result; - } - // The room is on a stable, but non-default, version by this point. - // No upgrade needed. - return result; - } - /** - * Determines whether the given user is permitted to perform a room upgrade - * @param userId - The ID of the user to test against - * @returns True if the given user is permitted to upgrade the room - */ - userMayUpgradeRoom(userId) { - return this.currentState.maySendStateEvent(event_2.EventType.RoomTombstone, userId); - } - /** - * Get the list of pending sent events for this room - * - * @returns A list of the sent events - * waiting for remote echo. - * - * @throws If `opts.pendingEventOrdering` was not 'detached' - */ - getPendingEvents() { - if (!this.pendingEventList) { - throw new Error("Cannot call getPendingEvents with pendingEventOrdering == " + this.opts.pendingEventOrdering); - } - return this.pendingEventList; - } - /** - * Removes a pending event for this room - * - * @returns True if an element was removed. - */ - removePendingEvent(eventId) { - if (!this.pendingEventList) { - throw new Error("Cannot call removePendingEvent with pendingEventOrdering == " + this.opts.pendingEventOrdering); - } - const removed = utils.removeElement(this.pendingEventList, function (ev) { - return ev.getId() == eventId; - }, false); - this.savePendingEvents(); - return removed; - } - /** - * Check whether the pending event list contains a given event by ID. - * If pending event ordering is not "detached" then this returns false. - * - * @param eventId - The event ID to check for. - */ - hasPendingEvent(eventId) { - var _a, _b; - return (_b = (_a = this.pendingEventList) === null || _a === void 0 ? void 0 : _a.some((event) => event.getId() === eventId)) !== null && _b !== void 0 ? _b : false; - } - /** - * Get a specific event from the pending event list, if configured, null otherwise. - * - * @param eventId - The event ID to check for. - */ - getPendingEvent(eventId) { - var _a, _b; - return (_b = (_a = this.pendingEventList) === null || _a === void 0 ? void 0 : _a.find((event) => event.getId() === eventId)) !== null && _b !== void 0 ? _b : null; - } - /** - * Get the live unfiltered timeline for this room. - * - * @returns live timeline - */ - getLiveTimeline() { - return this.getUnfilteredTimelineSet().getLiveTimeline(); - } - /** - * Get the timestamp of the last message in the room - * - * @returns the timestamp of the last message in the room - */ - getLastActiveTimestamp() { - const timeline = this.getLiveTimeline(); - const events = timeline.getEvents(); - if (events.length) { - const lastEvent = events[events.length - 1]; - return lastEvent.getTs(); - } - else { - return Number.MIN_SAFE_INTEGER; - } - } - /** - * @returns the membership type (join | leave | invite) for the logged in user - */ - getMyMembership() { - var _a; - return (_a = this.selfMembership) !== null && _a !== void 0 ? _a : "leave"; - } - /** - * If this room is a DM we're invited to, - * try to find out who invited us - * @returns user id of the inviter - */ - getDMInviter() { - var _a; - const me = this.getMember(this.myUserId); - if (me) { - return me.getDMInviter(); - } - if (this.selfMembership === "invite") { - // fall back to summary information - const memberCount = this.getInvitedAndJoinedMemberCount(); - if (memberCount === 2) { - return (_a = this.summaryHeroes) === null || _a === void 0 ? void 0 : _a[0]; - } - } - } - /** - * Assuming this room is a DM room, tries to guess with which user. - * @returns user id of the other member (could be syncing user) - */ - guessDMUserId() { - const me = this.getMember(this.myUserId); - if (me) { - const inviterId = me.getDMInviter(); - if (inviterId) { - return inviterId; - } - } - // Remember, we're assuming this room is a DM, so returning the first member we find should be fine - if (Array.isArray(this.summaryHeroes) && this.summaryHeroes.length) { - return this.summaryHeroes[0]; - } - const members = this.currentState.getMembers(); - const anyMember = members.find((m) => m.userId !== this.myUserId); - if (anyMember) { - return anyMember.userId; - } - // it really seems like I'm the only user in the room - // so I probably created a room with just me in it - // and marked it as a DM. Ok then - return this.myUserId; - } - getAvatarFallbackMember() { - const memberCount = this.getInvitedAndJoinedMemberCount(); - if (memberCount > 2) { - return; - } - const hasHeroes = Array.isArray(this.summaryHeroes) && this.summaryHeroes.length; - if (hasHeroes) { - const availableMember = this.summaryHeroes.map((userId) => { - return this.getMember(userId); - }).find((member) => !!member); - if (availableMember) { - return availableMember; - } - } - const members = this.currentState.getMembers(); - // could be different than memberCount - // as this includes left members - if (members.length <= 2) { - const availableMember = members.find((m) => { - return m.userId !== this.myUserId; - }); - if (availableMember) { - return availableMember; - } - } - // if all else fails, try falling back to a user, - // and create a one-off member for it - if (hasHeroes) { - const availableUser = this.summaryHeroes.map((userId) => { - return this.client.getUser(userId); - }).find((user) => !!user); - if (availableUser) { - const member = new room_member_1.RoomMember(this.roomId, availableUser.userId); - member.user = availableUser; - return member; - } - } - } - /** - * Sets the membership this room was received as during sync - * @param membership - join | leave | invite - */ - updateMyMembership(membership) { - const prevMembership = this.selfMembership; - this.selfMembership = membership; - if (prevMembership !== membership) { - if (membership === "leave") { - this.cleanupAfterLeaving(); - } - this.emit(RoomEvent.MyMembership, this, membership, prevMembership); - } - } - loadMembersFromServer() { - return __awaiter(this, void 0, void 0, function* () { - const lastSyncToken = this.client.store.getSyncToken(); - const response = yield this.client.members(this.roomId, undefined, "leave", lastSyncToken !== null && lastSyncToken !== void 0 ? lastSyncToken : undefined); - return response.chunk; - }); - } - loadMembers() { - return __awaiter(this, void 0, void 0, function* () { - // were the members loaded from the server? - let fromServer = false; - let rawMembersEvents = yield this.client.store.getOutOfBandMembers(this.roomId); - // If the room is encrypted, we always fetch members from the server at - // least once, in case the latest state wasn't persisted properly. Note - // that this function is only called once (unless loading the members - // fails), since loadMembersIfNeeded always returns this.membersPromise - // if set, which will be the result of the first (successful) call. - if (rawMembersEvents === null || (this.client.isCryptoEnabled() && this.client.isRoomEncrypted(this.roomId))) { - fromServer = true; - rawMembersEvents = yield this.loadMembersFromServer(); - logger_1.logger.log(`LL: got ${rawMembersEvents.length} ` + `members from server for room ${this.roomId}`); - } - const memberEvents = rawMembersEvents.filter(utils_1.noUnsafeEventProps).map(this.client.getEventMapper()); - return { memberEvents, fromServer }; - }); - } - /** - * Check if loading of out-of-band-members has completed - * - * @returns true if the full membership list of this room has been loaded (including if lazy-loading is disabled). - * False if the load is not started or is in progress. - */ - membersLoaded() { - if (!this.opts.lazyLoadMembers) { - return true; - } - return this.currentState.outOfBandMembersReady(); - } - /** - * Preloads the member list in case lazy loading - * of memberships is in use. Can be called multiple times, - * it will only preload once. - * @returns when preloading is done and - * accessing the members on the room will take - * all members in the room into account - */ - loadMembersIfNeeded() { - if (this.membersPromise) { - return this.membersPromise; - } - // mark the state so that incoming messages while - // the request is in flight get marked as superseding - // the OOB members - this.currentState.markOutOfBandMembersStarted(); - const inMemoryUpdate = this.loadMembers() - .then((result) => { - this.currentState.setOutOfBandMembers(result.memberEvents); - return result.fromServer; - }) - .catch((err) => { - // allow retries on fail - this.membersPromise = undefined; - this.currentState.markOutOfBandMembersFailed(); - throw err; - }); - // update members in storage, but don't wait for it - inMemoryUpdate - .then((fromServer) => { - if (fromServer) { - const oobMembers = this.currentState - .getMembers() - .filter((m) => m.isOutOfBand()) - .map((m) => { var _a; return (_a = m.events.member) === null || _a === void 0 ? void 0 : _a.event; }); - logger_1.logger.log(`LL: telling store to write ${oobMembers.length}` + ` members for room ${this.roomId}`); - const store = this.client.store; - return (store - .setOutOfBandMembers(this.roomId, oobMembers) - // swallow any IDB error as we don't want to fail - // because of this - .catch((err) => { - logger_1.logger.log("LL: storing OOB room members failed, oh well", err); - })); - } - }) - .catch((err) => { - // as this is not awaited anywhere, - // at least show the error in the console - logger_1.logger.error(err); - }); - this.membersPromise = inMemoryUpdate; - return this.membersPromise; - } - /** - * Removes the lazily loaded members from storage if needed - */ - clearLoadedMembersIfNeeded() { - return __awaiter(this, void 0, void 0, function* () { - if (this.opts.lazyLoadMembers && this.membersPromise) { - yield this.loadMembersIfNeeded(); - yield this.client.store.clearOutOfBandMembers(this.roomId); - this.currentState.clearOutOfBandMembers(); - this.membersPromise = undefined; - } - }); - } - /** - * called when sync receives this room in the leave section - * to do cleanup after leaving a room. Possibly called multiple times. - */ - cleanupAfterLeaving() { - this.clearLoadedMembersIfNeeded().catch((err) => { - logger_1.logger.error(`error after clearing loaded members from ` + `room ${this.roomId} after leaving`); - logger_1.logger.log(err); - }); - } - /** - * Empty out the current live timeline and re-request it. This is used when - * historical messages are imported into the room via MSC2716 `/batch_send` - * because the client may already have that section of the timeline loaded. - * We need to force the client to throw away their current timeline so that - * when they back paginate over the area again with the historical messages - * in between, it grabs the newly imported messages. We can listen for - * `UNSTABLE_MSC2716_MARKER`, in order to tell when historical messages are ready - * to be discovered in the room and the timeline needs a refresh. The SDK - * emits a `RoomEvent.HistoryImportedWithinTimeline` event when we detect a - * valid marker and can check the needs refresh status via - * `room.getTimelineNeedsRefresh()`. - */ - refreshLiveTimeline() { - return __awaiter(this, void 0, void 0, function* () { - const liveTimelineBefore = this.getLiveTimeline(); - const forwardPaginationToken = liveTimelineBefore.getPaginationToken(event_timeline_1.EventTimeline.FORWARDS); - const backwardPaginationToken = liveTimelineBefore.getPaginationToken(event_timeline_1.EventTimeline.BACKWARDS); - const eventsBefore = liveTimelineBefore.getEvents(); - const mostRecentEventInTimeline = eventsBefore[eventsBefore.length - 1]; - logger_1.logger.log(`[refreshLiveTimeline for ${this.roomId}] at ` + - `mostRecentEventInTimeline=${mostRecentEventInTimeline && mostRecentEventInTimeline.getId()} ` + - `liveTimelineBefore=${liveTimelineBefore.toString()} ` + - `forwardPaginationToken=${forwardPaginationToken} ` + - `backwardPaginationToken=${backwardPaginationToken}`); - // Get the main TimelineSet - const timelineSet = this.getUnfilteredTimelineSet(); - let newTimeline; - // If there isn't any event in the timeline, let's go fetch the latest - // event and construct a timeline from it. - // - // This should only really happen if the user ran into an error - // with refreshing the timeline before which left them in a blank - // timeline from `resetLiveTimeline`. - if (!mostRecentEventInTimeline) { - newTimeline = yield this.client.getLatestTimeline(timelineSet); - } - else { - // Empty out all of `this.timelineSets`. But we also need to keep the - // same `timelineSet` references around so the React code updates - // properly and doesn't ignore the room events we emit because it checks - // that the `timelineSet` references are the same. We need the - // `timelineSet` empty so that the `client.getEventTimeline(...)` call - // later, will call `/context` and create a new timeline instead of - // returning the same one. - this.resetLiveTimeline(null, null); - // Make the UI timeline show the new blank live timeline we just - // reset so that if the network fails below it's showing the - // accurate state of what we're working with instead of the - // disconnected one in the TimelineWindow which is just hanging - // around by reference. - this.emit(RoomEvent.TimelineRefresh, this, timelineSet); - // Use `client.getEventTimeline(...)` to construct a new timeline from a - // `/context` response state and events for the most recent event before - // we reset everything. The `timelineSet` we pass in needs to be empty - // in order for this function to call `/context` and generate a new - // timeline. - newTimeline = yield this.client.getEventTimeline(timelineSet, mostRecentEventInTimeline.getId()); - } - // If a racing `/sync` beat us to creating a new timeline, use that - // instead because it's the latest in the room and any new messages in - // the scrollback will include the history. - const liveTimeline = timelineSet.getLiveTimeline(); - if (!liveTimeline || - (liveTimeline.getPaginationToken(event_timeline_1.Direction.Forward) === null && - liveTimeline.getPaginationToken(event_timeline_1.Direction.Backward) === null && - liveTimeline.getEvents().length === 0)) { - logger_1.logger.log(`[refreshLiveTimeline for ${this.roomId}] using our new live timeline`); - // Set the pagination token back to the live sync token (`null`) instead - // of using the `/context` historical token (ex. `t12-13_0_0_0_0_0_0_0_0`) - // so that it matches the next response from `/sync` and we can properly - // continue the timeline. - newTimeline.setPaginationToken(forwardPaginationToken, event_timeline_1.EventTimeline.FORWARDS); - // Set our new fresh timeline as the live timeline to continue syncing - // forwards and back paginating from. - timelineSet.setLiveTimeline(newTimeline); - // Fixup `this.oldstate` so that `scrollback` has the pagination tokens - // available - this.fixUpLegacyTimelineFields(); - } - else { - logger_1.logger.log(`[refreshLiveTimeline for ${this.roomId}] \`/sync\` or some other request beat us to creating a new ` + - `live timeline after we reset it. We'll use that instead since any events in the scrollback from ` + - `this timeline will include the history.`); - } - // The timeline has now been refreshed ✅ - this.setTimelineNeedsRefresh(false); - // Emit an event which clients can react to and re-load the timeline - // from the SDK - this.emit(RoomEvent.TimelineRefresh, this, timelineSet); - }); - } - /** - * Reset the live timeline of all timelineSets, and start new ones. - * - *

This is used when /sync returns a 'limited' timeline. - * - * @param backPaginationToken - token for back-paginating the new timeline - * @param forwardPaginationToken - token for forward-paginating the old live timeline, - * if absent or null, all timelines are reset, removing old ones (including the previous live - * timeline which would otherwise be unable to paginate forwards without this token). - * Removing just the old live timeline whilst preserving previous ones is not supported. - */ - resetLiveTimeline(backPaginationToken, forwardPaginationToken) { - for (const timelineSet of this.timelineSets) { - timelineSet.resetLiveTimeline(backPaginationToken !== null && backPaginationToken !== void 0 ? backPaginationToken : undefined, forwardPaginationToken !== null && forwardPaginationToken !== void 0 ? forwardPaginationToken : undefined); - } - for (const thread of this.threads.values()) { - thread.resetLiveTimeline(backPaginationToken, forwardPaginationToken); - } - this.fixUpLegacyTimelineFields(); - } - /** - * Fix up this.timeline, this.oldState and this.currentState - * - * @internal - */ - fixUpLegacyTimelineFields() { - const previousOldState = this.oldState; - const previousCurrentState = this.currentState; - // maintain this.timeline as a reference to the live timeline, - // and this.oldState and this.currentState as references to the - // state at the start and end of that timeline. These are more - // for backwards-compatibility than anything else. - this.timeline = this.getLiveTimeline().getEvents(); - this.oldState = this.getLiveTimeline().getState(event_timeline_1.EventTimeline.BACKWARDS); - this.currentState = this.getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS); - // Let people know to register new listeners for the new state - // references. The reference won't necessarily change every time so only - // emit when we see a change. - if (previousOldState !== this.oldState) { - this.emit(RoomEvent.OldStateUpdated, this, previousOldState, this.oldState); - } - if (previousCurrentState !== this.currentState) { - this.emit(RoomEvent.CurrentStateUpdated, this, previousCurrentState, this.currentState); - // Re-emit various events on the current room state - // TODO: If currentState really only exists for backwards - // compatibility, shouldn't we be doing this some other way? - this.reEmitter.stopReEmitting(previousCurrentState, [ - room_state_1.RoomStateEvent.Events, - room_state_1.RoomStateEvent.Members, - room_state_1.RoomStateEvent.NewMember, - room_state_1.RoomStateEvent.Update, - room_state_1.RoomStateEvent.Marker, - beacon_1.BeaconEvent.New, - beacon_1.BeaconEvent.Update, - beacon_1.BeaconEvent.Destroy, - beacon_1.BeaconEvent.LivenessChange, - ]); - this.reEmitter.reEmit(this.currentState, [ - room_state_1.RoomStateEvent.Events, - room_state_1.RoomStateEvent.Members, - room_state_1.RoomStateEvent.NewMember, - room_state_1.RoomStateEvent.Update, - room_state_1.RoomStateEvent.Marker, - beacon_1.BeaconEvent.New, - beacon_1.BeaconEvent.Update, - beacon_1.BeaconEvent.Destroy, - beacon_1.BeaconEvent.LivenessChange, - ]); - } - } - /** - * Returns whether there are any devices in the room that are unverified - * - * Note: Callers should first check if crypto is enabled on this device. If it is - * disabled, then we aren't tracking room devices at all, so we can't answer this, and an - * error will be thrown. - * - * @returns the result - */ - hasUnverifiedDevices() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.client.isRoomEncrypted(this.roomId)) { - return false; - } - const e2eMembers = yield this.getEncryptionTargetMembers(); - for (const member of e2eMembers) { - const devices = this.client.getStoredDevicesForUser(member.userId); - if (devices.some((device) => device.isUnverified())) { - return true; - } - } - return false; - }); - } - /** - * Return the timeline sets for this room. - * @returns array of timeline sets for this room - */ - getTimelineSets() { - return this.timelineSets; - } - /** - * Helper to return the main unfiltered timeline set for this room - * @returns room's unfiltered timeline set - */ - getUnfilteredTimelineSet() { - return this.timelineSets[0]; - } - /** - * Get the timeline which contains the given event from the unfiltered set, if any - * - * @param eventId - event ID to look for - * @returns timeline containing - * the given event, or null if unknown - */ - getTimelineForEvent(eventId) { - const event = this.findEventById(eventId); - const thread = this.findThreadForEvent(event); - if (thread) { - return thread.timelineSet.getTimelineForEvent(eventId); - } - else { - return this.getUnfilteredTimelineSet().getTimelineForEvent(eventId); - } - } - /** - * Add a new timeline to this room's unfiltered timeline set - * - * @returns newly-created timeline - */ - addTimeline() { - return this.getUnfilteredTimelineSet().addTimeline(); - } - /** - * Whether the timeline needs to be refreshed in order to pull in new - * historical messages that were imported. - * @param value - The value to set - */ - setTimelineNeedsRefresh(value) { - this.timelineNeedsRefresh = value; - } - /** - * Whether the timeline needs to be refreshed in order to pull in new - * historical messages that were imported. - * @returns . - */ - getTimelineNeedsRefresh() { - return this.timelineNeedsRefresh; - } - /** - * Get an event which is stored in our unfiltered timeline set, or in a thread - * - * @param eventId - event ID to look for - * @returns the given event, or undefined if unknown - */ - findEventById(eventId) { - let event = this.getUnfilteredTimelineSet().findEventById(eventId); - if (!event) { - const threads = this.getThreads(); - for (let i = 0; i < threads.length; i++) { - const thread = threads[i]; - event = thread.findEventById(eventId); - if (event) { - return event; - } - } - } - return event; - } - /** - * Get one of the notification counts for this room - * @param type - The type of notification count to get. default: 'total' - * @returns The notification count, or undefined if there is no count - * for this type. - */ - getUnreadNotificationCount(type = NotificationCountType.Total) { - var _a; - let count = this.getRoomUnreadNotificationCount(type); - for (const threadNotification of this.threadNotifications.values()) { - count += (_a = threadNotification[type]) !== null && _a !== void 0 ? _a : 0; - } - return count; - } - /** - * Get the notification for the event context (room or thread timeline) - */ - getUnreadCountForEventContext(type = NotificationCountType.Total, event) { - var _a; - const isThreadEvent = !!event.threadRootId && !event.isThreadRoot; - return ((_a = (isThreadEvent - ? this.getThreadUnreadNotificationCount(event.threadRootId, type) - : this.getRoomUnreadNotificationCount(type))) !== null && _a !== void 0 ? _a : 0); - } - /** - * Get one of the notification counts for this room - * @param type - The type of notification count to get. default: 'total' - * @returns The notification count, or undefined if there is no count - * for this type. - */ - getRoomUnreadNotificationCount(type = NotificationCountType.Total) { - var _a; - return (_a = this.notificationCounts[type]) !== null && _a !== void 0 ? _a : 0; - } - /** - * Get one of the notification counts for a thread - * @param threadId - the root event ID - * @param type - The type of notification count to get. default: 'total' - * @returns The notification count, or undefined if there is no count - * for this type. - */ - getThreadUnreadNotificationCount(threadId, type = NotificationCountType.Total) { - var _a, _b; - return (_b = (_a = this.threadNotifications.get(threadId)) === null || _a === void 0 ? void 0 : _a[type]) !== null && _b !== void 0 ? _b : 0; - } - /** - * Checks if the current room has unread thread notifications - * @returns - */ - hasThreadUnreadNotification() { - var _a, _b; - for (const notification of this.threadNotifications.values()) { - if (((_a = notification.highlight) !== null && _a !== void 0 ? _a : 0) > 0 || ((_b = notification.total) !== null && _b !== void 0 ? _b : 0) > 0) { - return true; - } - } - return false; - } - /** - * Swet one of the notification count for a thread - * @param threadId - the root event ID - * @param type - The type of notification count to get. default: 'total' - * @returns - */ - setThreadUnreadNotificationCount(threadId, type, count) { - var _a, _b; - const notification = Object.assign({ highlight: (_a = this.threadNotifications.get(threadId)) === null || _a === void 0 ? void 0 : _a.highlight, total: (_b = this.threadNotifications.get(threadId)) === null || _b === void 0 ? void 0 : _b.total }, { - [type]: count, - }); - this.threadNotifications.set(threadId, notification); - this.emit(RoomEvent.UnreadNotifications, notification, threadId); - } - /** - * @returns the notification count type for all the threads in the room - */ - get threadsAggregateNotificationType() { - var _a, _b; - let type = null; - for (const threadNotification of this.threadNotifications.values()) { - if (((_a = threadNotification.highlight) !== null && _a !== void 0 ? _a : 0) > 0) { - return NotificationCountType.Highlight; - } - else if (((_b = threadNotification.total) !== null && _b !== void 0 ? _b : 0) > 0 && !type) { - type = NotificationCountType.Total; - } - } - return type; - } - /** - * Resets the thread notifications for this room - */ - resetThreadUnreadNotificationCount(notificationsToKeep) { - if (notificationsToKeep) { - for (const [threadId] of this.threadNotifications) { - if (!notificationsToKeep.includes(threadId)) { - this.threadNotifications.delete(threadId); - } - } - } - else { - this.threadNotifications.clear(); - } - this.emit(RoomEvent.UnreadNotifications); - } - /** - * Set one of the notification counts for this room - * @param type - The type of notification count to set. - * @param count - The new count - */ - setUnreadNotificationCount(type, count) { - this.notificationCounts[type] = count; - this.emit(RoomEvent.UnreadNotifications, this.notificationCounts); - } - setUnread(type, count) { - return this.setUnreadNotificationCount(type, count); - } - setSummary(summary) { - const heroes = summary["m.heroes"]; - const joinedCount = summary["m.joined_member_count"]; - const invitedCount = summary["m.invited_member_count"]; - if (Number.isInteger(joinedCount)) { - this.currentState.setJoinedMemberCount(joinedCount); - } - if (Number.isInteger(invitedCount)) { - this.currentState.setInvitedMemberCount(invitedCount); - } - if (Array.isArray(heroes)) { - // be cautious about trusting server values, - // and make sure heroes doesn't contain our own id - // just to be sure - this.summaryHeroes = heroes.filter((userId) => { - return userId !== this.myUserId; - }); - } - } - /** - * Whether to send encrypted messages to devices within this room. - * @param value - true to blacklist unverified devices, null - * to use the global value for this room. - */ - setBlacklistUnverifiedDevices(value) { - this.blacklistUnverifiedDevices = value; - } - /** - * Whether to send encrypted messages to devices within this room. - * @returns true if blacklisting unverified devices, null - * if the global value should be used for this room. - */ - getBlacklistUnverifiedDevices() { - if (this.blacklistUnverifiedDevices === undefined) - return null; - return this.blacklistUnverifiedDevices; - } - /** - * Get the avatar URL for a room if one was set. - * @param baseUrl - The homeserver base URL. See - * {@link MatrixClient#getHomeserverUrl}. - * @param width - The desired width of the thumbnail. - * @param height - The desired height of the thumbnail. - * @param resizeMethod - The thumbnail resize method to use, either - * "crop" or "scale". - * @param allowDefault - True to allow an identicon for this room if an - * avatar URL wasn't explicitly set. Default: true. (Deprecated) - * @returns the avatar URL or null. - */ - getAvatarUrl(baseUrl, width, height, resizeMethod, allowDefault = true) { - const roomAvatarEvent = this.currentState.getStateEvents(event_2.EventType.RoomAvatar, ""); - if (!roomAvatarEvent && !allowDefault) { - return null; - } - const mainUrl = roomAvatarEvent ? roomAvatarEvent.getContent().url : null; - if (mainUrl) { - return (0, content_repo_1.getHttpUriForMxc)(baseUrl, mainUrl, width, height, resizeMethod); - } - return null; - } - /** - * Get the mxc avatar url for the room, if one was set. - * @returns the mxc avatar url or falsy - */ - getMxcAvatarUrl() { - var _a, _b; - return ((_b = (_a = this.currentState.getStateEvents(event_2.EventType.RoomAvatar, "")) === null || _a === void 0 ? void 0 : _a.getContent()) === null || _b === void 0 ? void 0 : _b.url) || null; - } - /** - * Get this room's canonical alias - * The alias returned by this function may not necessarily - * still point to this room. - * @returns The room's canonical alias, or null if there is none - */ - getCanonicalAlias() { - const canonicalAlias = this.currentState.getStateEvents(event_2.EventType.RoomCanonicalAlias, ""); - if (canonicalAlias) { - return canonicalAlias.getContent().alias || null; - } - return null; - } - /** - * Get this room's alternative aliases - * @returns The room's alternative aliases, or an empty array - */ - getAltAliases() { - const canonicalAlias = this.currentState.getStateEvents(event_2.EventType.RoomCanonicalAlias, ""); - if (canonicalAlias) { - return canonicalAlias.getContent().alt_aliases || []; - } - return []; - } - /** - * Add events to a timeline - * - *

Will fire "Room.timeline" for each event added. - * - * @param events - A list of events to add. - * - * @param toStartOfTimeline - True to add these events to the start - * (oldest) instead of the end (newest) of the timeline. If true, the oldest - * event will be the last element of 'events'. - * - * @param timeline - timeline to - * add events to. - * - * @param paginationToken - token for the next batch of events - * - * @remarks - * Fires {@link RoomEvent.Timeline} - */ - addEventsToTimeline(events, toStartOfTimeline, timeline, paginationToken) { - timeline.getTimelineSet().addEventsToTimeline(events, toStartOfTimeline, timeline, paginationToken); - } - /** - * Get the instance of the thread associated with the current event - * @param eventId - the ID of the current event - * @returns a thread instance if known - */ - getThread(eventId) { - var _a; - return (_a = this.threads.get(eventId)) !== null && _a !== void 0 ? _a : null; - } - /** - * Get all the known threads in the room - */ - getThreads() { - return Array.from(this.threads.values()); - } - /** - * Get a member from the current room state. - * @param userId - The user ID of the member. - * @returns The member or `null`. - */ - getMember(userId) { - return this.currentState.getMember(userId); - } - /** - * Get all currently loaded members from the current - * room state. - * @returns Room members - */ - getMembers() { - return this.currentState.getMembers(); - } - /** - * Get a list of members whose membership state is "join". - * @returns A list of currently joined members. - */ - getJoinedMembers() { - return this.getMembersWithMembership("join"); - } - /** - * Returns the number of joined members in this room - * This method caches the result. - * This is a wrapper around the method of the same name in roomState, returning - * its result for the room's current state. - * @returns The number of members in this room whose membership is 'join' - */ - getJoinedMemberCount() { - return this.currentState.getJoinedMemberCount(); - } - /** - * Returns the number of invited members in this room - * @returns The number of members in this room whose membership is 'invite' - */ - getInvitedMemberCount() { - return this.currentState.getInvitedMemberCount(); - } - /** - * Returns the number of invited + joined members in this room - * @returns The number of members in this room whose membership is 'invite' or 'join' - */ - getInvitedAndJoinedMemberCount() { - return this.getInvitedMemberCount() + this.getJoinedMemberCount(); - } - /** - * Get a list of members with given membership state. - * @param membership - The membership state. - * @returns A list of members with the given membership state. - */ - getMembersWithMembership(membership) { - return this.currentState.getMembers().filter(function (m) { - return m.membership === membership; - }); - } - /** - * Get a list of members we should be encrypting for in this room - * @returns A list of members who - * we should encrypt messages for in this room. - */ - getEncryptionTargetMembers() { - return __awaiter(this, void 0, void 0, function* () { - yield this.loadMembersIfNeeded(); - let members = this.getMembersWithMembership("join"); - if (this.shouldEncryptForInvitedMembers()) { - members = members.concat(this.getMembersWithMembership("invite")); - } - return members; - }); - } - /** - * Determine whether we should encrypt messages for invited users in this room - * @returns if we should encrypt messages for invited users - */ - shouldEncryptForInvitedMembers() { - var _a; - const ev = this.currentState.getStateEvents(event_2.EventType.RoomHistoryVisibility, ""); - return ((_a = ev === null || ev === void 0 ? void 0 : ev.getContent()) === null || _a === void 0 ? void 0 : _a.history_visibility) !== "joined"; - } - /** - * Get the default room name (i.e. what a given user would see if the - * room had no m.room.name) - * @param userId - The userId from whose perspective we want - * to calculate the default name - * @returns The default room name - */ - getDefaultRoomName(userId) { - return this.calculateRoomName(userId, true); - } - /** - * Check if the given user_id has the given membership state. - * @param userId - The user ID to check. - * @param membership - The membership e.g. `'join'` - * @returns True if this user_id has the given membership state. - */ - hasMembershipState(userId, membership) { - const member = this.getMember(userId); - if (!member) { - return false; - } - return member.membership === membership; - } - /** - * Add a timelineSet for this room with the given filter - * @param filter - The filter to be applied to this timelineSet - * @param opts - Configuration options - * @returns The timelineSet - */ - getOrCreateFilteredTimelineSet(filter, { prepopulateTimeline = true, useSyncEvents = true, pendingEvents = true } = {}) { - if (this.filteredTimelineSets[filter.filterId]) { - return this.filteredTimelineSets[filter.filterId]; - } - const opts = Object.assign({ filter, pendingEvents }, this.opts); - const timelineSet = new event_timeline_set_1.EventTimelineSet(this, opts); - this.reEmitter.reEmit(timelineSet, [RoomEvent.Timeline, RoomEvent.TimelineReset]); - if (useSyncEvents) { - this.filteredTimelineSets[filter.filterId] = timelineSet; - this.timelineSets.push(timelineSet); - } - const unfilteredLiveTimeline = this.getLiveTimeline(); - // Not all filter are possible to replicate client-side only - // When that's the case we do not want to prepopulate from the live timeline - // as we would get incorrect results compared to what the server would send back - if (prepopulateTimeline) { - // populate up the new timelineSet with filtered events from our live - // unfiltered timeline. - // - // XXX: This is risky as our timeline - // may have grown huge and so take a long time to filter. - // see https://github.com/vector-im/vector-web/issues/2109 - unfilteredLiveTimeline.getEvents().forEach(function (event) { - timelineSet.addLiveEvent(event); - }); - // find the earliest unfiltered timeline - let timeline = unfilteredLiveTimeline; - while (timeline.getNeighbouringTimeline(event_timeline_1.EventTimeline.BACKWARDS)) { - timeline = timeline.getNeighbouringTimeline(event_timeline_1.EventTimeline.BACKWARDS); - } - timelineSet - .getLiveTimeline() - .setPaginationToken(timeline.getPaginationToken(event_timeline_1.EventTimeline.BACKWARDS), event_timeline_1.EventTimeline.BACKWARDS); - } - else if (useSyncEvents) { - const livePaginationToken = unfilteredLiveTimeline.getPaginationToken(event_timeline_1.Direction.Forward); - timelineSet.getLiveTimeline().setPaginationToken(livePaginationToken, event_timeline_1.Direction.Backward); - } - // alternatively, we could try to do something like this to try and re-paginate - // in the filtered events from nothing, but Mark says it's an abuse of the API - // to do so: - // - // timelineSet.resetLiveTimeline( - // unfilteredLiveTimeline.getPaginationToken(EventTimeline.FORWARDS) - // ); - return timelineSet; - } - getThreadListFilter(filterType = thread_1.ThreadFilterType.All) { - return __awaiter(this, void 0, void 0, function* () { - const myUserId = this.client.getUserId(); - const filter = new filter_1.Filter(myUserId); - const definition = { - room: { - timeline: { - [thread_1.FILTER_RELATED_BY_REL_TYPES.name]: [thread_1.THREAD_RELATION_TYPE.name], - }, - }, - }; - if (filterType === thread_1.ThreadFilterType.My) { - definition.room.timeline[thread_1.FILTER_RELATED_BY_SENDERS.name] = [myUserId]; - } - filter.setDefinition(definition); - const filterId = yield this.client.getOrCreateFilter(`THREAD_PANEL_${this.roomId}_${filterType}`, filter); - filter.filterId = filterId; - return filter; - }); - } - createThreadTimelineSet(filterType) { - return __awaiter(this, void 0, void 0, function* () { - let timelineSet; - if (thread_1.Thread.hasServerSideListSupport) { - timelineSet = new event_timeline_set_1.EventTimelineSet(this, Object.assign(Object.assign({}, this.opts), { pendingEvents: false }), undefined, undefined, filterType !== null && filterType !== void 0 ? filterType : thread_1.ThreadFilterType.All); - this.reEmitter.reEmit(timelineSet, [RoomEvent.Timeline, RoomEvent.TimelineReset]); - } - else if (thread_1.Thread.hasServerSideSupport) { - const filter = yield this.getThreadListFilter(filterType); - timelineSet = this.getOrCreateFilteredTimelineSet(filter, { - prepopulateTimeline: false, - useSyncEvents: false, - pendingEvents: false, - }); - } - else { - timelineSet = new event_timeline_set_1.EventTimelineSet(this, { - pendingEvents: false, - }); - Array.from(this.threads).forEach(([, thread]) => { - if (thread.length === 0) - return; - const currentUserParticipated = thread.timeline.some((event) => { - return event.getSender() === this.client.getUserId(); - }); - if (filterType !== thread_1.ThreadFilterType.My || currentUserParticipated) { - timelineSet.getLiveTimeline().addEvent(thread.rootEvent, { - toStartOfTimeline: false, - }); - } - }); - } - return timelineSet; - }); - } - /** - * Takes the given thread root events and creates threads for them. - */ - processThreadRoots(events, toStartOfTimeline) { - for (const rootEvent of events) { - event_timeline_1.EventTimeline.setEventMetadata(rootEvent, this.currentState, toStartOfTimeline); - if (!this.getThread(rootEvent.getId())) { - this.createThread(rootEvent.getId(), rootEvent, [], toStartOfTimeline); - } - } - } - /** - * Fetch the bare minimum of room threads required for the thread list to work reliably. - * With server support that means fetching one page. - * Without server support that means fetching as much at once as the server allows us to. - */ - fetchRoomThreads() { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - if (this.threadsReady || !this.client.supportsThreads()) { - return; - } - if (thread_1.Thread.hasServerSideListSupport) { - yield Promise.all([ - this.fetchRoomThreadList(thread_1.ThreadFilterType.All), - this.fetchRoomThreadList(thread_1.ThreadFilterType.My), - ]); - } - else { - const allThreadsFilter = yield this.getThreadListFilter(); - const { chunk: events } = yield this.client.createMessagesRequest(this.roomId, "", Number.MAX_SAFE_INTEGER, event_timeline_1.Direction.Backward, allThreadsFilter); - if (!events.length) - return; - // Sorted by last_reply origin_server_ts - const threadRoots = events.map(this.client.getEventMapper()).sort((eventA, eventB) => { - /** - * `origin_server_ts` in a decentralised world is far from ideal - * but for lack of any better, we will have to use this - * Long term the sorting should be handled by homeservers and this - * is only meant as a short term patch - */ - const threadAMetadata = eventA.getServerAggregatedRelation(thread_1.THREAD_RELATION_TYPE.name); - const threadBMetadata = eventB.getServerAggregatedRelation(thread_1.THREAD_RELATION_TYPE.name); - return threadAMetadata.latest_event.origin_server_ts - threadBMetadata.latest_event.origin_server_ts; - }); - let latestMyThreadsRootEvent; - const roomState = this.getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS); - for (const rootEvent of threadRoots) { - const opts = { - duplicateStrategy: event_timeline_set_1.DuplicateStrategy.Ignore, - fromCache: false, - roomState, - }; - (_a = this.threadsTimelineSets[0]) === null || _a === void 0 ? void 0 : _a.addLiveEvent(rootEvent, opts); - const threadRelationship = rootEvent.getServerAggregatedRelation(thread_1.THREAD_RELATION_TYPE.name); - if (threadRelationship === null || threadRelationship === void 0 ? void 0 : threadRelationship.current_user_participated) { - (_b = this.threadsTimelineSets[1]) === null || _b === void 0 ? void 0 : _b.addLiveEvent(rootEvent, opts); - latestMyThreadsRootEvent = rootEvent; - } - } - this.processThreadRoots(threadRoots, true); - this.client.decryptEventIfNeeded(threadRoots[threadRoots.length - 1]); - if (latestMyThreadsRootEvent) { - this.client.decryptEventIfNeeded(latestMyThreadsRootEvent); - } - } - this.on(thread_1.ThreadEvent.NewReply, this.onThreadNewReply); - this.on(thread_1.ThreadEvent.Delete, this.onThreadDelete); - this.threadsReady = true; - }); - } - processPollEvents(events) { - return __awaiter(this, void 0, void 0, function* () { - const processPollStartEvent = (event) => { - if (!matrix_events_sdk_1.M_POLL_START.matches(event.getType())) - return; - try { - const poll = new poll_1.Poll(event, this.client, this); - this.polls.set(event.getId(), poll); - this.emit(poll_1.PollEvent.New, poll); - } - catch (_a) { } - // poll creation can fail for malformed poll start events - }; - const processPollRelationEvent = (event) => { - const relationEventId = event.relationEventId; - if (relationEventId && this.polls.has(relationEventId)) { - const poll = this.polls.get(relationEventId); - poll === null || poll === void 0 ? void 0 : poll.onNewRelation(event); - } - }; - const processPollEvent = (event) => { - processPollStartEvent(event); - processPollRelationEvent(event); - }; - for (const event of events) { - try { - yield this.client.decryptEventIfNeeded(event); - processPollEvent(event); - } - catch (_a) { } - } - }); - } - /** - * Fetch a single page of threadlist messages for the specific thread filter - * @internal - */ - fetchRoomThreadList(filter) { - return __awaiter(this, void 0, void 0, function* () { - const timelineSet = filter === thread_1.ThreadFilterType.My ? this.threadsTimelineSets[1] : this.threadsTimelineSets[0]; - const { chunk: events, end } = yield this.client.createThreadListMessagesRequest(this.roomId, null, undefined, event_timeline_1.Direction.Backward, timelineSet.threadListType, timelineSet.getFilter()); - timelineSet.getLiveTimeline().setPaginationToken(end !== null && end !== void 0 ? end : null, event_timeline_1.Direction.Backward); - if (!events.length) - return; - const matrixEvents = events.map(this.client.getEventMapper()); - this.processThreadRoots(matrixEvents, true); - const roomState = this.getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS); - for (const rootEvent of matrixEvents) { - timelineSet.addLiveEvent(rootEvent, { - duplicateStrategy: event_timeline_set_1.DuplicateStrategy.Replace, - fromCache: false, - roomState, - }); - } - }); - } - onThreadNewReply(thread) { - this.updateThreadRootEvents(thread, false, true); - } - onThreadDelete(thread) { - var _a; - this.threads.delete(thread.id); - const timeline = this.getTimelineForEvent(thread.id); - const roomEvent = (_a = timeline === null || timeline === void 0 ? void 0 : timeline.getEvents()) === null || _a === void 0 ? void 0 : _a.find((it) => it.getId() === thread.id); - if (roomEvent) { - thread.clearEventMetadata(roomEvent); - } - else { - logger_1.logger.debug("onThreadDelete: Could not find root event in room timeline"); - } - for (const timelineSet of this.threadsTimelineSets) { - timelineSet.removeEvent(thread.id); - } - } - /** - * Forget the timelineSet for this room with the given filter - * - * @param filter - the filter whose timelineSet is to be forgotten - */ - removeFilteredTimelineSet(filter) { - const timelineSet = this.filteredTimelineSets[filter.filterId]; - delete this.filteredTimelineSets[filter.filterId]; - const i = this.timelineSets.indexOf(timelineSet); - if (i > -1) { - this.timelineSets.splice(i, 1); - } - } - eventShouldLiveIn(event, events, roots) { - var _a, _b; - if (!((_a = this.client) === null || _a === void 0 ? void 0 : _a.supportsThreads())) { - return { - shouldLiveInRoom: true, - shouldLiveInThread: false, - }; - } - // A thread root is always shown in both timelines - if (event.isThreadRoot || (roots === null || roots === void 0 ? void 0 : roots.has(event.getId()))) { - return { - shouldLiveInRoom: true, - shouldLiveInThread: true, - threadId: event.getId(), - }; - } - // A thread relation is always only shown in a thread - if (event.isRelation(thread_1.THREAD_RELATION_TYPE.name)) { - return { - shouldLiveInRoom: false, - shouldLiveInThread: true, - threadId: event.threadRootId, - }; - } - const parentEventId = event.getAssociatedId(); - let parentEvent; - if (parentEventId) { - parentEvent = (_b = this.findEventById(parentEventId)) !== null && _b !== void 0 ? _b : events === null || events === void 0 ? void 0 : events.find((e) => e.getId() === parentEventId); - } - // Treat relations and redactions as extensions of their parents so evaluate parentEvent instead - if (parentEvent && (event.isRelation() || event.isRedaction())) { - return this.eventShouldLiveIn(parentEvent, events, roots); - } - // Edge case where we know the event is a relation but don't have the parentEvent - if (roots === null || roots === void 0 ? void 0 : roots.has(event.relationEventId)) { - return { - shouldLiveInRoom: true, - shouldLiveInThread: true, - threadId: event.relationEventId, - }; - } - // We've exhausted all scenarios, can safely assume that this event should live in the room timeline only - return { - shouldLiveInRoom: true, - shouldLiveInThread: false, - }; - } - findThreadForEvent(event) { - if (!event) - return null; - const { threadId } = this.eventShouldLiveIn(event); - return threadId ? this.getThread(threadId) : null; - } - addThreadedEvents(threadId, events, toStartOfTimeline = false) { - var _a; - let thread = this.getThread(threadId); - if (!thread) { - const rootEvent = (_a = this.findEventById(threadId)) !== null && _a !== void 0 ? _a : events.find((e) => e.getId() === threadId); - thread = this.createThread(threadId, rootEvent, events, toStartOfTimeline); - } - thread.addEvents(events, toStartOfTimeline); - } - /** - * Adds events to a thread's timeline. Will fire "Thread.update" - */ - processThreadedEvents(events, toStartOfTimeline) { - var _a; - events.forEach(this.applyRedaction); - const eventsByThread = {}; - for (const event of events) { - const { threadId, shouldLiveInThread } = this.eventShouldLiveIn(event); - if (shouldLiveInThread && !eventsByThread[threadId]) { - eventsByThread[threadId] = []; - } - (_a = eventsByThread[threadId]) === null || _a === void 0 ? void 0 : _a.push(event); - } - Object.entries(eventsByThread).map(([threadId, threadEvents]) => this.addThreadedEvents(threadId, threadEvents, toStartOfTimeline)); - } - createThread(threadId, rootEvent, events = [], toStartOfTimeline) { - var _a, _b, _c; - if (this.threads.has(threadId)) { - return this.threads.get(threadId); - } - if (rootEvent) { - const relatedEvents = this.relations.getAllChildEventsForEvent(rootEvent.getId()); - if (relatedEvents === null || relatedEvents === void 0 ? void 0 : relatedEvents.length) { - // Include all relations of the root event, given it'll be visible in both timelines, - // except `m.replace` as that will already be applied atop the event using `MatrixEvent::makeReplaced` - events = events.concat(relatedEvents.filter((e) => !e.isRelation(event_2.RelationType.Replace))); - } - } - const thread = new thread_1.Thread(threadId, rootEvent, { - room: this, - client: this.client, - pendingEventOrdering: this.opts.pendingEventOrdering, - receipts: (_a = this.cachedThreadReadReceipts.get(threadId)) !== null && _a !== void 0 ? _a : [], - }); - // All read receipts should now come down from sync, we do not need to keep - // a reference to the cached receipts anymore. - this.cachedThreadReadReceipts.delete(threadId); - // If we managed to create a thread and figure out its `id` then we can use it - // This has to happen before thread.addEvents, because that adds events to the eventtimeline, and the - // eventtimeline sometimes looks up thread information via the room. - this.threads.set(thread.id, thread); - // This is necessary to be able to jump to events in threads: - // If we jump to an event in a thread where neither the event, nor the root, - // nor any thread event are loaded yet, we'll load the event as well as the thread root, create the thread, - // and pass the event through this. - thread.addEvents(events, false); - this.reEmitter.reEmit(thread, [ - thread_1.ThreadEvent.Delete, - thread_1.ThreadEvent.Update, - thread_1.ThreadEvent.NewReply, - RoomEvent.Timeline, - RoomEvent.TimelineReset, - ]); - const isNewer = ((_b = this.lastThread) === null || _b === void 0 ? void 0 : _b.rootEvent) && - (rootEvent === null || rootEvent === void 0 ? void 0 : rootEvent.localTimestamp) && - ((_c = this.lastThread.rootEvent) === null || _c === void 0 ? void 0 : _c.localTimestamp) < (rootEvent === null || rootEvent === void 0 ? void 0 : rootEvent.localTimestamp); - if (!this.lastThread || isNewer) { - this.lastThread = thread; - } - if (this.threadsReady) { - this.updateThreadRootEvents(thread, toStartOfTimeline, false); - } - this.emit(thread_1.ThreadEvent.New, thread, toStartOfTimeline); - return thread; - } - processLiveEvent(event) { - this.applyRedaction(event); - // Implement MSC3531: hiding messages. - if (event.isVisibilityEvent()) { - // This event changes the visibility of another event, record - // the visibility change, inform clients if necessary. - this.applyNewVisibilityEvent(event); - } - // If any pending visibility change is waiting for this (older) event, - this.applyPendingVisibilityEvents(event); - // Sliding Sync modifications: - // The proxy cannot guarantee every sent event will have a transaction_id field, so we need - // to check the event ID against the list of pending events if there is no transaction ID - // field. Only do this for events sent by us though as it's potentially expensive to loop - // the pending events map. - const txnId = event.getUnsigned().transaction_id; - if (!txnId && event.getSender() === this.myUserId) { - // check the txn map for a matching event ID - for (const [tid, localEvent] of this.txnToEvent) { - if (localEvent.getId() === event.getId()) { - logger_1.logger.debug("processLiveEvent: found sent event without txn ID: ", tid, event.getId()); - // update the unsigned field so we can re-use the same codepaths - const unsigned = event.getUnsigned(); - unsigned.transaction_id = tid; - event.setUnsigned(unsigned); - break; - } - } - } - } - /** - * Add an event to the end of this room's live timelines. Will fire - * "Room.timeline". - * - * @param event - Event to be added - * @param addLiveEventOptions - addLiveEvent options - * @internal - * - * @remarks - * Fires {@link RoomEvent.Timeline} - */ - addLiveEvent(event, addLiveEventOptions) { - const { duplicateStrategy, timelineWasEmpty, fromCache } = addLiveEventOptions; - // add to our timeline sets - for (const timelineSet of this.timelineSets) { - timelineSet.addLiveEvent(event, { - duplicateStrategy, - fromCache, - timelineWasEmpty, - }); - } - // synthesize and inject implicit read receipts - // Done after adding the event because otherwise the app would get a read receipt - // pointing to an event that wasn't yet in the timeline - // Don't synthesize RR for m.room.redaction as this causes the RR to go missing. - if (event.sender && event.getType() !== event_2.EventType.RoomRedaction) { - this.addReceipt((0, read_receipt_1.synthesizeReceipt)(event.sender.userId, event, read_receipts_1.ReceiptType.Read), true); - // Any live events from a user could be taken as implicit - // presence information: evidence that they are currently active. - // ...except in a world where we use 'user.currentlyActive' to reduce - // presence spam, this isn't very useful - we'll get a transition when - // they are no longer currently active anyway. So don't bother to - // reset the lastActiveAgo and lastPresenceTs from the RoomState's user. - } - } - /** - * Add a pending outgoing event to this room. - * - *

The event is added to either the pendingEventList, or the live timeline, - * depending on the setting of opts.pendingEventOrdering. - * - *

This is an internal method, intended for use by MatrixClient. - * - * @param event - The event to add. - * - * @param txnId - Transaction id for this outgoing event - * - * @throws if the event doesn't have status SENDING, or we aren't given a - * unique transaction id. - * - * @remarks - * Fires {@link RoomEvent.LocalEchoUpdated} - */ - addPendingEvent(event, txnId) { - if (event.status !== event_status_1.EventStatus.SENDING && event.status !== event_status_1.EventStatus.NOT_SENT) { - throw new Error("addPendingEvent called on an event with status " + event.status); - } - if (this.txnToEvent.get(txnId)) { - throw new Error("addPendingEvent called on an event with known txnId " + txnId); - } - // call setEventMetadata to set up event.sender etc - // as event is shared over all timelineSets, we set up its metadata based - // on the unfiltered timelineSet. - event_timeline_1.EventTimeline.setEventMetadata(event, this.getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS), false); - this.txnToEvent.set(txnId, event); - if (this.pendingEventList) { - if (this.pendingEventList.some((e) => e.status === event_status_1.EventStatus.NOT_SENT)) { - logger_1.logger.warn("Setting event as NOT_SENT due to messages in the same state"); - event.setStatus(event_status_1.EventStatus.NOT_SENT); - } - this.pendingEventList.push(event); - this.savePendingEvents(); - if (event.isRelation()) { - // For pending events, add them to the relations collection immediately. - // (The alternate case below already covers this as part of adding to - // the timeline set.) - this.aggregateNonLiveRelation(event); - } - if (event.isRedaction()) { - const redactId = event.event.redacts; - let redactedEvent = this.pendingEventList.find((e) => e.getId() === redactId); - if (!redactedEvent && redactId) { - redactedEvent = this.findEventById(redactId); - } - if (redactedEvent) { - redactedEvent.markLocallyRedacted(event); - this.emit(RoomEvent.Redaction, event, this); - } - } - } - else { - for (const timelineSet of this.timelineSets) { - if (timelineSet.getFilter()) { - if (timelineSet.getFilter().filterRoomTimeline([event]).length) { - timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), { - toStartOfTimeline: false, - }); - } - } - else { - timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), { - toStartOfTimeline: false, - }); - } - } - } - this.emit(RoomEvent.LocalEchoUpdated, event, this); - } - /** - * Persists all pending events to local storage - * - * If the current room is encrypted only encrypted events will be persisted - * all messages that are not yet encrypted will be discarded - * - * This is because the flow of EVENT_STATUS transition is - * `queued => sending => encrypting => sending => sent` - * - * Steps 3 and 4 are skipped for unencrypted room. - * It is better to discard an unencrypted message rather than persisting - * it locally for everyone to read - */ - savePendingEvents() { - if (this.pendingEventList) { - const pendingEvents = this.pendingEventList - .map((event) => { - return Object.assign(Object.assign({}, event.event), { txn_id: event.getTxnId() }); - }) - .filter((event) => { - // Filter out the unencrypted messages if the room is encrypted - const isEventEncrypted = event.type === event_2.EventType.RoomMessageEncrypted; - const isRoomEncrypted = this.client.isRoomEncrypted(this.roomId); - return isEventEncrypted || !isRoomEncrypted; - }); - this.client.store.setPendingEvents(this.roomId, pendingEvents); - } - } - /** - * Used to aggregate the local echo for a relation, and also - * for re-applying a relation after it's redaction has been cancelled, - * as the local echo for the redaction of the relation would have - * un-aggregated the relation. Note that this is different from regular messages, - * which are just kept detached for their local echo. - * - * Also note that live events are aggregated in the live EventTimelineSet. - * @param event - the relation event that needs to be aggregated. - */ - aggregateNonLiveRelation(event) { - this.relations.aggregateChildEvent(event); - } - getEventForTxnId(txnId) { - return this.txnToEvent.get(txnId); - } - /** - * Deal with the echo of a message we sent. - * - *

We move the event to the live timeline if it isn't there already, and - * update it. - * - * @param remoteEvent - The event received from - * /sync - * @param localEvent - The local echo, which - * should be either in the pendingEventList or the timeline. - * - * @internal - * - * @remarks - * Fires {@link RoomEvent.LocalEchoUpdated} - */ - handleRemoteEcho(remoteEvent, localEvent) { - const oldEventId = localEvent.getId(); - const newEventId = remoteEvent.getId(); - const oldStatus = localEvent.status; - logger_1.logger.debug(`Got remote echo for event ${oldEventId} -> ${newEventId} old status ${oldStatus}`); - // no longer pending - this.txnToEvent.delete(remoteEvent.getUnsigned().transaction_id); - // if it's in the pending list, remove it - if (this.pendingEventList) { - this.removePendingEvent(oldEventId); - } - // replace the event source (this will preserve the plaintext payload if - // any, which is good, because we don't want to try decoding it again). - localEvent.handleRemoteEcho(remoteEvent.event); - const { shouldLiveInRoom, threadId } = this.eventShouldLiveIn(remoteEvent); - const thread = threadId ? this.getThread(threadId) : null; - thread === null || thread === void 0 ? void 0 : thread.setEventMetadata(localEvent); - thread === null || thread === void 0 ? void 0 : thread.timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId); - if (shouldLiveInRoom) { - for (const timelineSet of this.timelineSets) { - // if it's already in the timeline, update the timeline map. If it's not, add it. - timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId); - } - } - this.emit(RoomEvent.LocalEchoUpdated, localEvent, this, oldEventId, oldStatus); - } - /** - * Update the status / event id on a pending event, to reflect its transmission - * progress. - * - *

This is an internal method. - * - * @param event - local echo event - * @param newStatus - status to assign - * @param newEventId - new event id to assign. Ignored unless newStatus == EventStatus.SENT. - * - * @remarks - * Fires {@link RoomEvent.LocalEchoUpdated} - */ - updatePendingEvent(event, newStatus, newEventId) { - logger_1.logger.log(`setting pendingEvent status to ${newStatus} in ${event.getRoomId()} ` + - `event ID ${event.getId()} -> ${newEventId}`); - // if the message was sent, we expect an event id - if (newStatus == event_status_1.EventStatus.SENT && !newEventId) { - throw new Error("updatePendingEvent called with status=SENT, but no new event id"); - } - // SENT races against /sync, so we have to special-case it. - if (newStatus == event_status_1.EventStatus.SENT) { - const timeline = this.getTimelineForEvent(newEventId); - if (timeline) { - // we've already received the event via the event stream. - // nothing more to do here, assuming the transaction ID was correctly matched. - // Let's check that. - const remoteEvent = this.findEventById(newEventId); - const remoteTxnId = remoteEvent === null || remoteEvent === void 0 ? void 0 : remoteEvent.getUnsigned().transaction_id; - if (!remoteTxnId && remoteEvent) { - // This code path is mostly relevant for the Sliding Sync proxy. - // The remote event did not contain a transaction ID, so we did not handle - // the remote echo yet. Handle it now. - const unsigned = remoteEvent.getUnsigned(); - unsigned.transaction_id = event.getTxnId(); - remoteEvent.setUnsigned(unsigned); - // the remote event is _already_ in the timeline, so we need to remove it so - // we can convert the local event into the final event. - this.removeEvent(remoteEvent.getId()); - this.handleRemoteEcho(remoteEvent, event); - } - return; - } - } - const oldStatus = event.status; - const oldEventId = event.getId(); - if (!oldStatus) { - throw new Error("updatePendingEventStatus called on an event which is not a local echo."); - } - const allowed = ALLOWED_TRANSITIONS[oldStatus]; - if (!(allowed === null || allowed === void 0 ? void 0 : allowed.includes(newStatus))) { - throw new Error(`Invalid EventStatus transition ${oldStatus}->${newStatus}`); - } - event.setStatus(newStatus); - if (newStatus == event_status_1.EventStatus.SENT) { - // update the event id - event.replaceLocalEventId(newEventId); - const { shouldLiveInRoom, threadId } = this.eventShouldLiveIn(event); - const thread = threadId ? this.getThread(threadId) : undefined; - thread === null || thread === void 0 ? void 0 : thread.setEventMetadata(event); - thread === null || thread === void 0 ? void 0 : thread.timelineSet.replaceEventId(oldEventId, newEventId); - if (shouldLiveInRoom) { - // if the event was already in the timeline (which will be the case if - // opts.pendingEventOrdering==chronological), we need to update the - // timeline map. - for (const timelineSet of this.timelineSets) { - timelineSet.replaceEventId(oldEventId, newEventId); - } - } - } - else if (newStatus == event_status_1.EventStatus.CANCELLED) { - // remove it from the pending event list, or the timeline. - if (this.pendingEventList) { - const removedEvent = this.getPendingEvent(oldEventId); - this.removePendingEvent(oldEventId); - if (removedEvent === null || removedEvent === void 0 ? void 0 : removedEvent.isRedaction()) { - this.revertRedactionLocalEcho(removedEvent); - } - } - this.removeEvent(oldEventId); - } - this.savePendingEvents(); - this.emit(RoomEvent.LocalEchoUpdated, event, this, oldEventId, oldStatus); - } - revertRedactionLocalEcho(redactionEvent) { - const redactId = redactionEvent.event.redacts; - if (!redactId) { - return; - } - const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId); - if (redactedEvent) { - redactedEvent.unmarkLocallyRedacted(); - // re-render after undoing redaction - this.emit(RoomEvent.RedactionCancelled, redactionEvent, this); - // reapply relation now redaction failed - if (redactedEvent.isRelation()) { - this.aggregateNonLiveRelation(redactedEvent); - } - } - } - addLiveEvents(events, duplicateStrategyOrOpts, fromCache = false) { - var _a; - let duplicateStrategy = duplicateStrategyOrOpts; - let timelineWasEmpty = false; - if (typeof duplicateStrategyOrOpts === "object") { - ({ - duplicateStrategy, - fromCache = false, - /* roomState, (not used here) */ - timelineWasEmpty, - } = duplicateStrategyOrOpts); - } - else if (duplicateStrategyOrOpts !== undefined) { - // Deprecation warning - // FIXME: Remove after 2023-06-01 (technical debt) - logger_1.logger.warn("Overload deprecated: " + - "`Room.addLiveEvents(events, duplicateStrategy?, fromCache?)` " + - "is deprecated in favor of the overload with `Room.addLiveEvents(events, IAddLiveEventOptions)`"); - } - if (duplicateStrategy && ["replace", "ignore"].indexOf(duplicateStrategy) === -1) { - throw new Error("duplicateStrategy MUST be either 'replace' or 'ignore'"); - } - // sanity check that the live timeline is still live - for (let i = 0; i < this.timelineSets.length; i++) { - const liveTimeline = this.timelineSets[i].getLiveTimeline(); - if (liveTimeline.getPaginationToken(event_timeline_1.EventTimeline.FORWARDS)) { - throw new Error("live timeline " + - i + - " is no longer live - it has a pagination token " + - "(" + - liveTimeline.getPaginationToken(event_timeline_1.EventTimeline.FORWARDS) + - ")"); - } - if (liveTimeline.getNeighbouringTimeline(event_timeline_1.EventTimeline.FORWARDS)) { - throw new Error(`live timeline ${i} is no longer live - it has a neighbouring timeline`); - } - } - const threadRoots = this.findThreadRoots(events); - const eventsByThread = {}; - const options = { - duplicateStrategy, - fromCache, - timelineWasEmpty, - }; - for (const event of events) { - // TODO: We should have a filter to say "only add state event types X Y Z to the timeline". - this.processLiveEvent(event); - if (event.getUnsigned().transaction_id) { - const existingEvent = this.txnToEvent.get(event.getUnsigned().transaction_id); - if (existingEvent) { - // remote echo of an event we sent earlier - this.handleRemoteEcho(event, existingEvent); - continue; // we can skip adding the event to the timeline sets, it is already there - } - } - const { shouldLiveInRoom, shouldLiveInThread, threadId } = this.eventShouldLiveIn(event, events, threadRoots); - if (shouldLiveInThread && !eventsByThread[threadId !== null && threadId !== void 0 ? threadId : ""]) { - eventsByThread[threadId !== null && threadId !== void 0 ? threadId : ""] = []; - } - (_a = eventsByThread[threadId !== null && threadId !== void 0 ? threadId : ""]) === null || _a === void 0 ? void 0 : _a.push(event); - if (shouldLiveInRoom) { - this.addLiveEvent(event, options); - } - } - Object.entries(eventsByThread).forEach(([threadId, threadEvents]) => { - this.addThreadedEvents(threadId, threadEvents, false); - }); - } - partitionThreadedEvents(events) { - // Indices to the events array, for readability - const ROOM = 0; - const THREAD = 1; - if (this.client.supportsThreads()) { - const threadRoots = this.findThreadRoots(events); - return events.reduce((memo, event) => { - const { shouldLiveInRoom, shouldLiveInThread, threadId } = this.eventShouldLiveIn(event, events, threadRoots); - if (shouldLiveInRoom) { - memo[ROOM].push(event); - } - if (shouldLiveInThread) { - event.setThreadId(threadId !== null && threadId !== void 0 ? threadId : ""); - memo[THREAD].push(event); - } - return memo; - }, [[], []]); - } - else { - // When `experimentalThreadSupport` is disabled treat all events as timelineEvents - return [events, []]; - } - } - /** - * Given some events, find the IDs of all the thread roots that are referred to by them. - */ - findThreadRoots(events) { - var _a; - const threadRoots = new Set(); - for (const event of events) { - if (event.isRelation(thread_1.THREAD_RELATION_TYPE.name)) { - threadRoots.add((_a = event.relationEventId) !== null && _a !== void 0 ? _a : ""); - } - } - return threadRoots; - } - /** - * Add a receipt event to the room. - * @param event - The m.receipt event. - * @param synthetic - True if this event is implicit. - */ - addReceipt(event, synthetic = false) { - const content = event.getContent(); - Object.keys(content).forEach((eventId) => { - Object.keys(content[eventId]).forEach((receiptType) => { - Object.keys(content[eventId][receiptType]).forEach((userId) => { - var _a, _b, _c, _d; - const receipt = content[eventId][receiptType][userId]; - const receiptForMainTimeline = !receipt.thread_id || receipt.thread_id === read_receipts_1.MAIN_ROOM_TIMELINE; - const receiptDestination = receiptForMainTimeline - ? this - : this.threads.get((_a = receipt.thread_id) !== null && _a !== void 0 ? _a : ""); - if (receiptDestination) { - receiptDestination.addReceiptToStructure(eventId, receiptType, userId, receipt, synthetic); - // If the read receipt sent for the logged in user matches - // the last event of the live timeline, then we know for a fact - // that the user has read that message. - // We can mark the room as read and not wait for the local echo - // from synapse - // This needs to be done after the initial sync as we do not want this - // logic to run whilst the room is being initialised - if (this.client.isInitialSyncComplete() && userId === this.client.getUserId()) { - const lastEvent = receiptDestination.timeline[receiptDestination.timeline.length - 1]; - if (lastEvent && eventId === lastEvent.getId() && userId === lastEvent.getSender()) { - receiptDestination.setUnread(NotificationCountType.Total, 0); - receiptDestination.setUnread(NotificationCountType.Highlight, 0); - } - } - } - else { - // The thread does not exist locally, keep the read receipt - // in a cache locally, and re-apply the `addReceipt` logic - // when the thread is created - this.cachedThreadReadReceipts.set(receipt.thread_id, [ - ...((_b = this.cachedThreadReadReceipts.get(receipt.thread_id)) !== null && _b !== void 0 ? _b : []), - { eventId, receiptType, userId, receipt, synthetic }, - ]); - } - const me = this.client.getUserId(); - // Track the time of the current user's oldest threaded receipt in the room. - if (userId === me && !receiptForMainTimeline && receipt.ts < this.oldestThreadedReceiptTs) { - this.oldestThreadedReceiptTs = receipt.ts; - } - // Track each user's unthreaded read receipt. - if (!receipt.thread_id && receipt.ts > ((_d = (_c = this.unthreadedReceipts.get(userId)) === null || _c === void 0 ? void 0 : _c.ts) !== null && _d !== void 0 ? _d : 0)) { - this.unthreadedReceipts.set(userId, receipt); - } - }); - }); - }); - // send events after we've regenerated the structure & cache, otherwise things that - // listened for the event would read stale data. - this.emit(RoomEvent.Receipt, event, this); - } - /** - * Adds/handles ephemeral events such as typing notifications and read receipts. - * @param events - A list of events to process - */ - addEphemeralEvents(events) { - for (const event of events) { - if (event.getType() === event_2.EventType.Typing) { - this.currentState.setTypingEvent(event); - } - else if (event.getType() === event_2.EventType.Receipt) { - this.addReceipt(event); - } // else ignore - life is too short for us to care about these events - } - } - /** - * Removes events from this room. - * @param eventIds - A list of eventIds to remove. - */ - removeEvents(eventIds) { - for (const eventId of eventIds) { - this.removeEvent(eventId); - } - } - /** - * Removes a single event from this room. - * - * @param eventId - The id of the event to remove - * - * @returns true if the event was removed from any of the room's timeline sets - */ - removeEvent(eventId) { - let removedAny = false; - for (const timelineSet of this.timelineSets) { - const removed = timelineSet.removeEvent(eventId); - if (removed) { - if (removed.isRedaction()) { - this.revertRedactionLocalEcho(removed); - } - removedAny = true; - } - } - return removedAny; - } - /** - * Recalculate various aspects of the room, including the room name and - * room summary. Call this any time the room's current state is modified. - * May fire "Room.name" if the room name is updated. - * - * @remarks - * Fires {@link RoomEvent.Name} - */ - recalculate() { - // set fake stripped state events if this is an invite room so logic remains - // consistent elsewhere. - const membershipEvent = this.currentState.getStateEvents(event_2.EventType.RoomMember, this.myUserId); - if (membershipEvent) { - const membership = membershipEvent.getContent().membership; - this.updateMyMembership(membership); - if (membership === "invite") { - const strippedStateEvents = membershipEvent.getUnsigned().invite_room_state || []; - strippedStateEvents.forEach((strippedEvent) => { - const existingEvent = this.currentState.getStateEvents(strippedEvent.type, strippedEvent.state_key); - if (!existingEvent) { - // set the fake stripped event instead - this.currentState.setStateEvents([ - new event_1.MatrixEvent({ - type: strippedEvent.type, - state_key: strippedEvent.state_key, - content: strippedEvent.content, - event_id: "$fake" + Date.now(), - room_id: this.roomId, - user_id: this.myUserId, // technically a lie - }), - ]); - } - }); - } - } - const oldName = this.name; - this.name = this.calculateRoomName(this.myUserId); - this.normalizedName = (0, utils_1.normalize)(this.name); - this.summary = new room_summary_1.RoomSummary(this.roomId, { - title: this.name, - }); - if (oldName !== this.name) { - this.emit(RoomEvent.Name, this); - } - } - /** - * Update the room-tag event for the room. The previous one is overwritten. - * @param event - the m.tag event - */ - addTags(event) { - // event content looks like: - // content: { - // tags: { - // $tagName: { $metadata: $value }, - // $tagName: { $metadata: $value }, - // } - // } - // XXX: do we need to deep copy here? - this.tags = event.getContent().tags || {}; - // XXX: we could do a deep-comparison to see if the tags have really - // changed - but do we want to bother? - this.emit(RoomEvent.Tags, event, this); - } - /** - * Update the account_data events for this room, overwriting events of the same type. - * @param events - an array of account_data events to add - */ - addAccountData(events) { - for (const event of events) { - if (event.getType() === "m.tag") { - this.addTags(event); - } - const eventType = event.getType(); - const lastEvent = this.accountData.get(eventType); - this.accountData.set(eventType, event); - this.emit(RoomEvent.AccountData, event, this, lastEvent); - } - } - /** - * Access account_data event of given event type for this room - * @param type - the type of account_data event to be accessed - * @returns the account_data event in question - */ - getAccountData(type) { - return this.accountData.get(type); - } - /** - * Returns whether the syncing user has permission to send a message in the room - * @returns true if the user should be permitted to send - * message events into the room. - */ - maySendMessage() { - return (this.getMyMembership() === "join" && - (this.client.isRoomEncrypted(this.roomId) - ? this.currentState.maySendEvent(event_2.EventType.RoomMessageEncrypted, this.myUserId) - : this.currentState.maySendEvent(event_2.EventType.RoomMessage, this.myUserId))); - } - /** - * Returns whether the given user has permissions to issue an invite for this room. - * @param userId - the ID of the Matrix user to check permissions for - * @returns true if the user should be permitted to issue invites for this room. - */ - canInvite(userId) { - let canInvite = this.getMyMembership() === "join"; - const powerLevelsEvent = this.currentState.getStateEvents(event_2.EventType.RoomPowerLevels, ""); - const powerLevels = powerLevelsEvent && powerLevelsEvent.getContent(); - const me = this.getMember(userId); - if (powerLevels && me && powerLevels.invite > me.powerLevel) { - canInvite = false; - } - return canInvite; - } - /** - * Returns the join rule based on the m.room.join_rule state event, defaulting to `invite`. - * @returns the join_rule applied to this room - */ - getJoinRule() { - return this.currentState.getJoinRule(); - } - /** - * Returns the history visibility based on the m.room.history_visibility state event, defaulting to `shared`. - * @returns the history_visibility applied to this room - */ - getHistoryVisibility() { - return this.currentState.getHistoryVisibility(); - } - /** - * Returns the history visibility based on the m.room.history_visibility state event, defaulting to `shared`. - * @returns the history_visibility applied to this room - */ - getGuestAccess() { - return this.currentState.getGuestAccess(); - } - /** - * Returns the type of the room from the `m.room.create` event content or undefined if none is set - * @returns the type of the room. - */ - getType() { - const createEvent = this.currentState.getStateEvents(event_2.EventType.RoomCreate, ""); - if (!createEvent) { - if (!this.getTypeWarning) { - logger_1.logger.warn("[getType] Room " + this.roomId + " does not have an m.room.create event"); - this.getTypeWarning = true; - } - return undefined; - } - return createEvent.getContent()[event_2.RoomCreateTypeField]; - } - /** - * Returns whether the room is a space-room as defined by MSC1772. - * @returns true if the room's type is RoomType.Space - */ - isSpaceRoom() { - return this.getType() === event_2.RoomType.Space; - } - /** - * Returns whether the room is a call-room as defined by MSC3417. - * @returns true if the room's type is RoomType.UnstableCall - */ - isCallRoom() { - return this.getType() === event_2.RoomType.UnstableCall; - } - /** - * Returns whether the room is a video room. - * @returns true if the room's type is RoomType.ElementVideo - */ - isElementVideoRoom() { - return this.getType() === event_2.RoomType.ElementVideo; - } - /** - * Find the predecessor of this room. - * - * @param msc3946ProcessDynamicPredecessor - if true, look for an - * m.room.predecessor state event and use it if found (MSC3946). - * @returns null if this room has no predecessor. Otherwise, returns - * the roomId, last eventId and viaServers of the predecessor room. - * - * If msc3946ProcessDynamicPredecessor is true, use m.predecessor events - * as well as m.room.create events to find predecessors. - * - * Note: if an m.predecessor event is used, eventId may be undefined - * since last_known_event_id is optional. - * - * Note: viaServers may be undefined, and will definitely be undefined if - * this predecessor comes from a RoomCreate event (rather than a - * RoomPredecessor, which has the optional via_servers property). - */ - findPredecessor(msc3946ProcessDynamicPredecessor = false) { - const currentState = this.getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS); - if (!currentState) { - return null; - } - return currentState.findPredecessor(msc3946ProcessDynamicPredecessor); - } - roomNameGenerator(state) { - if (this.client.roomNameGenerator) { - const name = this.client.roomNameGenerator(this.roomId, state); - if (name !== null) { - return name; - } - } - switch (state.type) { - case RoomNameType.Actual: - return state.name; - case RoomNameType.Generated: - switch (state.subtype) { - case "Inviting": - return `Inviting ${memberNamesToRoomName(state.names, state.count)}`; - default: - return memberNamesToRoomName(state.names, state.count); - } - case RoomNameType.EmptyRoom: - if (state.oldName) { - return `Empty room (was ${state.oldName})`; - } - else { - return "Empty room"; - } - } - } - /** - * This is an internal method. Calculates the name of the room from the current - * room state. - * @param userId - The client's user ID. Used to filter room members - * correctly. - * @param ignoreRoomNameEvent - Return the implicit room name that we'd see if there - * was no m.room.name event. - * @returns The calculated room name. - */ - calculateRoomName(userId, ignoreRoomNameEvent = false) { - if (!ignoreRoomNameEvent) { - // check for an alias, if any. for now, assume first alias is the - // official one. - const mRoomName = this.currentState.getStateEvents(event_2.EventType.RoomName, ""); - if (mRoomName === null || mRoomName === void 0 ? void 0 : mRoomName.getContent().name) { - return this.roomNameGenerator({ - type: RoomNameType.Actual, - name: mRoomName.getContent().name, - }); - } - } - const alias = this.getCanonicalAlias(); - if (alias) { - return this.roomNameGenerator({ - type: RoomNameType.Actual, - name: alias, - }); - } - const joinedMemberCount = this.currentState.getJoinedMemberCount(); - const invitedMemberCount = this.currentState.getInvitedMemberCount(); - // -1 because these numbers include the syncing user - let inviteJoinCount = joinedMemberCount + invitedMemberCount - 1; - // get service members (e.g. helper bots) for exclusion - let excludedUserIds = []; - const mFunctionalMembers = this.currentState.getStateEvents(event_2.UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, ""); - if (Array.isArray(mFunctionalMembers === null || mFunctionalMembers === void 0 ? void 0 : mFunctionalMembers.getContent().service_members)) { - excludedUserIds = mFunctionalMembers.getContent().service_members; - } - // get members that are NOT ourselves and are actually in the room. - let otherNames = []; - if (this.summaryHeroes) { - // if we have a summary, the member state events should be in the room state - this.summaryHeroes.forEach((userId) => { - // filter service members - if (excludedUserIds.includes(userId)) { - inviteJoinCount--; - return; - } - const member = this.getMember(userId); - otherNames.push(member ? member.name : userId); - }); - } - else { - let otherMembers = this.currentState.getMembers().filter((m) => { - return m.userId !== userId && (m.membership === "invite" || m.membership === "join"); - }); - otherMembers = otherMembers.filter(({ userId }) => { - // filter service members - if (excludedUserIds.includes(userId)) { - inviteJoinCount--; - return false; - } - return true; - }); - // make sure members have stable order - otherMembers.sort((a, b) => utils.compare(a.userId, b.userId)); - // only 5 first members, immitate summaryHeroes - otherMembers = otherMembers.slice(0, 5); - otherNames = otherMembers.map((m) => m.name); - } - if (inviteJoinCount) { - return this.roomNameGenerator({ - type: RoomNameType.Generated, - names: otherNames, - count: inviteJoinCount, - }); - } - const myMembership = this.getMyMembership(); - // if I have created a room and invited people through - // 3rd party invites - if (myMembership == "join") { - const thirdPartyInvites = this.currentState.getStateEvents(event_2.EventType.RoomThirdPartyInvite); - if (thirdPartyInvites === null || thirdPartyInvites === void 0 ? void 0 : thirdPartyInvites.length) { - const thirdPartyNames = thirdPartyInvites.map((i) => { - return i.getContent().display_name; - }); - return this.roomNameGenerator({ - type: RoomNameType.Generated, - subtype: "Inviting", - names: thirdPartyNames, - count: thirdPartyNames.length + 1, - }); - } - } - // let's try to figure out who was here before - let leftNames = otherNames; - // if we didn't have heroes, try finding them in the room state - if (!leftNames.length) { - leftNames = this.currentState - .getMembers() - .filter((m) => { - return m.userId !== userId && m.membership !== "invite" && m.membership !== "join"; - }) - .map((m) => m.name); - } - let oldName; - if (leftNames.length) { - oldName = this.roomNameGenerator({ - type: RoomNameType.Generated, - names: leftNames, - count: leftNames.length + 1, - }); - } - return this.roomNameGenerator({ - type: RoomNameType.EmptyRoom, - oldName, - }); - } - /** - * When we receive a new visibility change event: - * - * - store this visibility change alongside the timeline, in case we - * later need to apply it to an event that we haven't received yet; - * - if we have already received the event whose visibility has changed, - * patch it to reflect the visibility change and inform listeners. - */ - applyNewVisibilityEvent(event) { - const visibilityChange = event.asVisibilityChange(); - if (!visibilityChange) { - // The event is ill-formed. - return; - } - // Ignore visibility change events that are not emitted by moderators. - const userId = event.getSender(); - if (!userId) { - return; - } - const isPowerSufficient = (event_2.EVENT_VISIBILITY_CHANGE_TYPE.name && - this.currentState.maySendStateEvent(event_2.EVENT_VISIBILITY_CHANGE_TYPE.name, userId)) || - (event_2.EVENT_VISIBILITY_CHANGE_TYPE.altName && - this.currentState.maySendStateEvent(event_2.EVENT_VISIBILITY_CHANGE_TYPE.altName, userId)); - if (!isPowerSufficient) { - // Powerlevel is insufficient. - return; - } - // Record this change in visibility. - // If the event is not in our timeline and we only receive it later, - // we may need to apply the visibility change at a later date. - const visibilityEventsOnOriginalEvent = this.visibilityEvents.get(visibilityChange.eventId); - if (visibilityEventsOnOriginalEvent) { - // It would be tempting to simply erase the latest visibility change - // but we need to record all of the changes in case the latest change - // is ever redacted. - // - // In practice, linear scans through `visibilityEvents` should be fast. - // However, to protect against a potential DoS attack, we limit the - // number of iterations in this loop. - let index = visibilityEventsOnOriginalEvent.length - 1; - const min = Math.max(0, visibilityEventsOnOriginalEvent.length - MAX_NUMBER_OF_VISIBILITY_EVENTS_TO_SCAN_THROUGH); - for (; index >= min; --index) { - const target = visibilityEventsOnOriginalEvent[index]; - if (target.getTs() < event.getTs()) { - break; - } - } - if (index === -1) { - visibilityEventsOnOriginalEvent.unshift(event); - } - else { - visibilityEventsOnOriginalEvent.splice(index + 1, 0, event); - } - } - else { - this.visibilityEvents.set(visibilityChange.eventId, [event]); - } - // Finally, let's check if the event is already in our timeline. - // If so, we need to patch it and inform listeners. - const originalEvent = this.findEventById(visibilityChange.eventId); - if (!originalEvent) { - return; - } - originalEvent.applyVisibilityEvent(visibilityChange); - } - redactVisibilityChangeEvent(event) { - // Sanity checks. - if (!event.isVisibilityEvent) { - throw new Error("expected a visibility change event"); - } - const relation = event.getRelation(); - const originalEventId = relation === null || relation === void 0 ? void 0 : relation.event_id; - const visibilityEventsOnOriginalEvent = this.visibilityEvents.get(originalEventId); - if (!visibilityEventsOnOriginalEvent) { - // No visibility changes on the original event. - // In particular, this change event was not recorded, - // most likely because it was ill-formed. - return; - } - const index = visibilityEventsOnOriginalEvent.findIndex((change) => change.getId() === event.getId()); - if (index === -1) { - // This change event was not recorded, most likely because - // it was ill-formed. - return; - } - // Remove visibility change. - visibilityEventsOnOriginalEvent.splice(index, 1); - // If we removed the latest visibility change event, propagate changes. - if (index === visibilityEventsOnOriginalEvent.length) { - const originalEvent = this.findEventById(originalEventId); - if (!originalEvent) { - return; - } - if (index === 0) { - // We have just removed the only visibility change event. - this.visibilityEvents.delete(originalEventId); - originalEvent.applyVisibilityEvent(); - } - else { - const newEvent = visibilityEventsOnOriginalEvent[visibilityEventsOnOriginalEvent.length - 1]; - const newVisibility = newEvent.asVisibilityChange(); - if (!newVisibility) { - // Event is ill-formed. - // This breaks our invariant. - throw new Error("at this stage, visibility changes should be well-formed"); - } - originalEvent.applyVisibilityEvent(newVisibility); - } - } - } - /** - * When we receive an event whose visibility has been altered by - * a (more recent) visibility change event, patch the event in - * place so that clients now not to display it. - * - * @param event - Any matrix event. If this event has at least one a - * pending visibility change event, apply the latest visibility - * change event. - */ - applyPendingVisibilityEvents(event) { - const visibilityEvents = this.visibilityEvents.get(event.getId()); - if (!visibilityEvents || visibilityEvents.length == 0) { - // No pending visibility change in store. - return; - } - const visibilityEvent = visibilityEvents[visibilityEvents.length - 1]; - const visibilityChange = visibilityEvent.asVisibilityChange(); - if (!visibilityChange) { - return; - } - if (visibilityChange.visible) { - // Events are visible by default, no need to apply a visibility change. - // Note that we need to keep the visibility changes in `visibilityEvents`, - // in case we later fetch an older visibility change event that is superseded - // by `visibilityChange`. - } - if (visibilityEvent.getTs() < event.getTs()) { - // Something is wrong, the visibility change cannot happen before the - // event. Presumably an ill-formed event. - return; - } - event.applyVisibilityEvent(visibilityChange); - } - /** - * Find when a client has gained thread capabilities by inspecting the oldest - * threaded receipt - * @returns the timestamp of the oldest threaded receipt - */ - getOldestThreadedReceiptTs() { - return this.oldestThreadedReceiptTs; - } - /** - * Returns the most recent unthreaded receipt for a given user - * @param userId - the MxID of the User - * @returns an unthreaded Receipt. Can be undefined if receipts have been disabled - * or a user chooses to use private read receipts (or we have simply not received - * a receipt from this user yet). - */ - getLastUnthreadedReceiptFor(userId) { - return this.unthreadedReceipts.get(userId); - } - /** - * This issue should also be addressed on synapse's side and is tracked as part - * of https://github.com/matrix-org/synapse/issues/14837 - * - * - * We consider a room fully read if the current user has sent - * the last event in the live timeline of that context and if the read receipt - * we have on record matches. - * This also detects all unread threads and applies the same logic to those - * contexts - */ - fixupNotifications(userId) { - super.fixupNotifications(userId); - const unreadThreads = this.getThreads().filter((thread) => this.getThreadUnreadNotificationCount(thread.id, NotificationCountType.Total) > 0); - for (const thread of unreadThreads) { - thread.fixupNotifications(userId); - } - } -} -exports.Room = Room; -// a map from current event status to a list of allowed next statuses -const ALLOWED_TRANSITIONS = { - [event_status_1.EventStatus.ENCRYPTING]: [event_status_1.EventStatus.SENDING, event_status_1.EventStatus.NOT_SENT, event_status_1.EventStatus.CANCELLED], - [event_status_1.EventStatus.SENDING]: [event_status_1.EventStatus.ENCRYPTING, event_status_1.EventStatus.QUEUED, event_status_1.EventStatus.NOT_SENT, event_status_1.EventStatus.SENT], - [event_status_1.EventStatus.QUEUED]: [event_status_1.EventStatus.SENDING, event_status_1.EventStatus.NOT_SENT, event_status_1.EventStatus.CANCELLED], - [event_status_1.EventStatus.SENT]: [], - [event_status_1.EventStatus.NOT_SENT]: [event_status_1.EventStatus.SENDING, event_status_1.EventStatus.QUEUED, event_status_1.EventStatus.CANCELLED], - [event_status_1.EventStatus.CANCELLED]: [], -}; -var RoomNameType; -(function (RoomNameType) { - RoomNameType[RoomNameType["EmptyRoom"] = 0] = "EmptyRoom"; - RoomNameType[RoomNameType["Generated"] = 1] = "Generated"; - RoomNameType[RoomNameType["Actual"] = 2] = "Actual"; -})(RoomNameType = exports.RoomNameType || (exports.RoomNameType = {})); -// Can be overriden by IMatrixClientCreateOpts::memberNamesToRoomNameFn -function memberNamesToRoomName(names, count) { - const countWithoutMe = count - 1; - if (!names.length) { - return "Empty room"; - } - else if (names.length === 1 && countWithoutMe <= 1) { - return names[0]; - } - else if (names.length === 2 && countWithoutMe <= 2) { - return `${names[0]} and ${names[1]}`; - } - else { - const plural = countWithoutMe > 1; - if (plural) { - return `${names[0]} and ${countWithoutMe} others`; - } - else { - return `${names[0]} and 1 other`; - } - } -} - -},{"../@types/event":306,"../@types/read_receipts":311,"../ReEmitter":317,"../client":321,"../content-repo":323,"../filter":364,"../logger":374,"../utils":416,"./beacon":378,"./event":383,"./event-status":380,"./event-timeline":382,"./event-timeline-set":381,"./poll":385,"./read-receipt":386,"./relations-container":387,"./room-member":389,"./room-state":390,"./room-summary":391,"./thread":394,"matrix-events-sdk":167}],393:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SearchResult = void 0; -const event_context_1 = require("./event-context"); -class SearchResult { - /** - * Create a SearchResponse from the response to /search - */ - static fromJson(jsonObj, eventMapper) { - const jsonContext = jsonObj.context || {}; - let eventsBefore = (jsonContext.events_before || []).map(eventMapper); - let eventsAfter = (jsonContext.events_after || []).map(eventMapper); - const context = new event_context_1.EventContext(eventMapper(jsonObj.result)); - // Filter out any contextual events which do not correspond to the same timeline (thread or room) - const threadRootId = context.ourEvent.threadRootId; - eventsBefore = eventsBefore.filter((e) => e.threadRootId === threadRootId); - eventsAfter = eventsAfter.filter((e) => e.threadRootId === threadRootId); - context.setPaginateToken(jsonContext.start, true); - context.addEvents(eventsBefore, true); - context.addEvents(eventsAfter, false); - context.setPaginateToken(jsonContext.end, false); - return new SearchResult(jsonObj.rank, context); - } - /** - * Construct a new SearchResult - * - * @param rank - where this SearchResult ranks in the results - * @param context - the matching event and its - * context - */ - constructor(rank, context) { - this.rank = rank; - this.context = context; - } -} -exports.SearchResult = SearchResult; - -},{"./event-context":379}],394:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.threadFilterTypeToFilter = exports.ThreadFilterType = exports.THREAD_RELATION_TYPE = exports.FILTER_RELATED_BY_REL_TYPES = exports.FILTER_RELATED_BY_SENDERS = exports.Thread = exports.determineFeatureSupport = exports.FeatureSupport = exports.ThreadEvent = void 0; -const client_1 = require("../client"); -const ReEmitter_1 = require("../ReEmitter"); -const event_1 = require("../@types/event"); -const event_2 = require("./event"); -const event_timeline_1 = require("./event-timeline"); -const event_timeline_set_1 = require("./event-timeline-set"); -const room_1 = require("./room"); -const NamespacedValue_1 = require("../NamespacedValue"); -const logger_1 = require("../logger"); -const read_receipt_1 = require("./read-receipt"); -const read_receipts_1 = require("../@types/read_receipts"); -var ThreadEvent; -(function (ThreadEvent) { - ThreadEvent["New"] = "Thread.new"; - ThreadEvent["Update"] = "Thread.update"; - ThreadEvent["NewReply"] = "Thread.newReply"; - ThreadEvent["ViewThread"] = "Thread.viewThread"; - ThreadEvent["Delete"] = "Thread.delete"; -})(ThreadEvent = exports.ThreadEvent || (exports.ThreadEvent = {})); -var FeatureSupport; -(function (FeatureSupport) { - FeatureSupport[FeatureSupport["None"] = 0] = "None"; - FeatureSupport[FeatureSupport["Experimental"] = 1] = "Experimental"; - FeatureSupport[FeatureSupport["Stable"] = 2] = "Stable"; -})(FeatureSupport = exports.FeatureSupport || (exports.FeatureSupport = {})); -function determineFeatureSupport(stable, unstable) { - if (stable) { - return FeatureSupport.Stable; - } - else if (unstable) { - return FeatureSupport.Experimental; - } - else { - return FeatureSupport.None; - } -} -exports.determineFeatureSupport = determineFeatureSupport; -class Thread extends read_receipt_1.ReadReceipt { - constructor(id, rootEvent, opts) { - var _a; - super(); - this.id = id; - this.rootEvent = rootEvent; - this.timeline = []; - this._currentUserParticipated = false; - this.replyCount = 0; - this.pendingReplyCount = 0; - this.initialEventsFetched = !Thread.hasServerSideSupport; - /** - * An array of events to add to the timeline once the thread has been initialised - * with server suppport. - */ - this.replayEvents = []; - this.onBeforeRedaction = (event, redaction) => { - if ((event === null || event === void 0 ? void 0 : event.isRelation(exports.THREAD_RELATION_TYPE.name)) && - this.room.eventShouldLiveIn(event).threadId === this.id && - event.getId() !== this.id && // the root event isn't counted in the length so ignore this redaction - !redaction.status // only respect it when it succeeds - ) { - this.replyCount--; - this.updatePendingReplyCount(); - this.emit(ThreadEvent.Update, this); - } - }; - this.onRedaction = (event) => __awaiter(this, void 0, void 0, function* () { - if (event.threadRootId !== this.id) - return; // ignore redactions for other timelines - if (this.replyCount <= 0) { - for (const threadEvent of this.timeline) { - this.clearEventMetadata(threadEvent); - } - this.lastEvent = this.rootEvent; - this._currentUserParticipated = false; - this.emit(ThreadEvent.Delete, this); - } - else { - yield this.updateThreadMetadata(); - } - }); - this.onTimelineEvent = (event, room, toStartOfTimeline) => { - // Add a synthesized receipt when paginating forward in the timeline - if (!toStartOfTimeline) { - room.addLocalEchoReceipt(event.getSender(), event, read_receipts_1.ReceiptType.Read); - } - this.onEcho(event, toStartOfTimeline !== null && toStartOfTimeline !== void 0 ? toStartOfTimeline : false); - }; - this.onLocalEcho = (event) => { - this.onEcho(event, false); - }; - this.onEcho = (event, toStartOfTimeline) => __awaiter(this, void 0, void 0, function* () { - if (event.threadRootId !== this.id) - return; // ignore echoes for other timelines - if (this.lastEvent === event) - return; // ignore duplicate events - yield this.updateThreadMetadata(); - if (!event.isRelation(exports.THREAD_RELATION_TYPE.name)) - return; // don't send a new reply event for reactions or edits - if (toStartOfTimeline) - return; // ignore messages added to the start of the timeline - this.emit(ThreadEvent.NewReply, this, event); - }); - if (!(opts === null || opts === void 0 ? void 0 : opts.room)) { - // Logging/debugging for https://github.com/vector-im/element-web/issues/22141 - // Hope is that we end up with a more obvious stack trace. - throw new Error("element-web#22141: A thread requires a room in order to function"); - } - this.room = opts.room; - this.client = opts.client; - this.pendingEventOrdering = (_a = opts.pendingEventOrdering) !== null && _a !== void 0 ? _a : client_1.PendingEventOrdering.Chronological; - this.timelineSet = new event_timeline_set_1.EventTimelineSet(this.room, { - timelineSupport: true, - pendingEvents: true, - }, this.client, this); - this.reEmitter = new ReEmitter_1.TypedReEmitter(this); - this.reEmitter.reEmit(this.timelineSet, [room_1.RoomEvent.Timeline, room_1.RoomEvent.TimelineReset]); - this.room.on(event_2.MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction); - this.room.on(room_1.RoomEvent.Redaction, this.onRedaction); - this.room.on(room_1.RoomEvent.LocalEchoUpdated, this.onLocalEcho); - this.timelineSet.on(room_1.RoomEvent.Timeline, this.onTimelineEvent); - this.processReceipts(opts.receipts); - // even if this thread is thought to be originating from this client, we initialise it as we may be in a - // gappy sync and a thread around this event may already exist. - this.updateThreadMetadata(); - this.setEventMetadata(this.rootEvent); - } - fetchRootEvent() { - return __awaiter(this, void 0, void 0, function* () { - this.rootEvent = this.room.findEventById(this.id); - // If the rootEvent does not exist in the local stores, then fetch it from the server. - try { - const eventData = yield this.client.fetchRoomEvent(this.roomId, this.id); - const mapper = this.client.getEventMapper(); - this.rootEvent = mapper(eventData); // will merge with existing event object if such is known - } - catch (e) { - logger_1.logger.error("Failed to fetch thread root to construct thread with", e); - } - yield this.processEvent(this.rootEvent); - }); - } - static setServerSideSupport(status) { - Thread.hasServerSideSupport = status; - if (status !== FeatureSupport.Stable) { - exports.FILTER_RELATED_BY_SENDERS.setPreferUnstable(true); - exports.FILTER_RELATED_BY_REL_TYPES.setPreferUnstable(true); - exports.THREAD_RELATION_TYPE.setPreferUnstable(true); - } - } - static setServerSideListSupport(status) { - Thread.hasServerSideListSupport = status; - } - static setServerSideFwdPaginationSupport(status) { - Thread.hasServerSideFwdPaginationSupport = status; - } - get roomState() { - return this.room.getLiveTimeline().getState(event_timeline_1.EventTimeline.FORWARDS); - } - addEventToTimeline(event, toStartOfTimeline) { - if (!this.findEventById(event.getId())) { - this.timelineSet.addEventToTimeline(event, this.liveTimeline, { - toStartOfTimeline, - fromCache: false, - roomState: this.roomState, - }); - this.timeline = this.events; - } - } - addEvents(events, toStartOfTimeline) { - events.forEach((ev) => this.addEvent(ev, toStartOfTimeline, false)); - this.updateThreadMetadata(); - } - /** - * Add an event to the thread and updates - * the tail/root references if needed - * Will fire "Thread.update" - * @param event - The event to add - * @param toStartOfTimeline - whether the event is being added - * to the start (and not the end) of the timeline. - * @param emit - whether to emit the Update event if the thread was updated or not. - */ - addEvent(event, toStartOfTimeline, emit = true) { - var _a, _b, _c; - return __awaiter(this, void 0, void 0, function* () { - this.setEventMetadata(event); - const lastReply = this.lastReply(); - const isNewestReply = !lastReply || event.localTimestamp >= lastReply.localTimestamp; - // Add all incoming events to the thread's timeline set when there's no server support - if (!Thread.hasServerSideSupport) { - // all the relevant membership info to hydrate events with a sender - // is held in the main room timeline - // We want to fetch the room state from there and pass it down to this thread - // timeline set to let it reconcile an event with its relevant RoomMember - this.addEventToTimeline(event, toStartOfTimeline); - this.client.decryptEventIfNeeded(event, {}); - } - else if (!toStartOfTimeline && this.initialEventsFetched && isNewestReply) { - this.addEventToTimeline(event, false); - this.fetchEditsWhereNeeded(event); - } - else if (event.isRelation(event_1.RelationType.Annotation) || event.isRelation(event_1.RelationType.Replace)) { - if (!this.initialEventsFetched) { - /** - * A thread can be fully discovered via a single sync response - * And when that's the case we still ask the server to do an initialisation - * as it's the safest to ensure we have everything. - * However when we are in that scenario we might loose annotation or edits - * - * This fix keeps a reference to those events and replay them once the thread - * has been initialised properly. - */ - (_a = this.replayEvents) === null || _a === void 0 ? void 0 : _a.push(event); - } - else { - this.addEventToTimeline(event, toStartOfTimeline); - } - // Apply annotations and replace relations to the relations of the timeline only - (_b = this.timelineSet.relations) === null || _b === void 0 ? void 0 : _b.aggregateParentEvent(event); - (_c = this.timelineSet.relations) === null || _c === void 0 ? void 0 : _c.aggregateChildEvent(event, this.timelineSet); - return; - } - // If no thread support exists we want to count all thread relation - // added as a reply. We can't rely on the bundled relationships count - if ((!Thread.hasServerSideSupport || !this.rootEvent) && event.isRelation(exports.THREAD_RELATION_TYPE.name)) { - this.replyCount++; - } - if (emit) { - this.emit(ThreadEvent.NewReply, this, event); - this.updateThreadMetadata(); - } - }); - } - processEvent(event) { - return __awaiter(this, void 0, void 0, function* () { - if (event) { - this.setEventMetadata(event); - yield this.fetchEditsWhereNeeded(event); - } - this.timeline = this.events; - }); - } - /** - * Processes the receipts that were caught during initial sync - * When clients become aware of a thread, they try to retrieve those read receipts - * and apply them to the current thread - * @param receipts - A collection of the receipts cached from initial sync - */ - processReceipts(receipts = []) { - for (const { eventId, receiptType, userId, receipt, synthetic } of receipts) { - this.addReceiptToStructure(eventId, receiptType, userId, receipt, synthetic); - } - } - getRootEventBundledRelationship(rootEvent = this.rootEvent) { - return rootEvent === null || rootEvent === void 0 ? void 0 : rootEvent.getServerAggregatedRelation(exports.THREAD_RELATION_TYPE.name); - } - processRootEvent() { - return __awaiter(this, void 0, void 0, function* () { - const bundledRelationship = this.getRootEventBundledRelationship(); - if (Thread.hasServerSideSupport && bundledRelationship) { - this.replyCount = bundledRelationship.count; - this._currentUserParticipated = !!bundledRelationship.current_user_participated; - const mapper = this.client.getEventMapper(); - // re-insert roomId - this.lastEvent = mapper(Object.assign(Object.assign({}, bundledRelationship.latest_event), { room_id: this.roomId })); - this.updatePendingReplyCount(); - yield this.processEvent(this.lastEvent); - } - }); - } - updatePendingReplyCount() { - const unfilteredPendingEvents = this.pendingEventOrdering === client_1.PendingEventOrdering.Detached ? this.room.getPendingEvents() : this.events; - const pendingEvents = unfilteredPendingEvents.filter((ev) => { - var _a; - return ev.threadRootId === this.id && - ev.isRelation(exports.THREAD_RELATION_TYPE.name) && - ev.status !== null && - ev.getId() !== ((_a = this.lastEvent) === null || _a === void 0 ? void 0 : _a.getId()); - }); - this.lastPendingEvent = pendingEvents.length ? pendingEvents[pendingEvents.length - 1] : undefined; - this.pendingReplyCount = pendingEvents.length; - } - /** - * Reset the live timeline of all timelineSets, and start new ones. - * - *

This is used when /sync returns a 'limited' timeline. 'Limited' means that there's a gap between the messages - * /sync returned, and the last known message in our timeline. In such a case, our live timeline isn't live anymore - * and has to be replaced by a new one. To make sure we can continue paginating our timelines correctly, we have to - * set new pagination tokens on the old and the new timeline. - * - * @param backPaginationToken - token for back-paginating the new timeline - * @param forwardPaginationToken - token for forward-paginating the old live timeline, - * if absent or null, all timelines are reset, removing old ones (including the previous live - * timeline which would otherwise be unable to paginate forwards without this token). - * Removing just the old live timeline whilst preserving previous ones is not supported. - */ - resetLiveTimeline(backPaginationToken, forwardPaginationToken) { - return __awaiter(this, void 0, void 0, function* () { - const oldLive = this.liveTimeline; - this.timelineSet.resetLiveTimeline(backPaginationToken !== null && backPaginationToken !== void 0 ? backPaginationToken : undefined, forwardPaginationToken !== null && forwardPaginationToken !== void 0 ? forwardPaginationToken : undefined); - const newLive = this.liveTimeline; - // FIXME: Remove the following as soon as https://github.com/matrix-org/synapse/issues/14830 is resolved. - // - // The pagination API for thread timelines currently can't handle the type of pagination tokens returned by sync - // - // To make this work anyway, we'll have to transform them into one of the types that the API can handle. - // One option is passing the tokens to /messages, which can handle sync tokens, and returns the right format. - // /messages does not return new tokens on requests with a limit of 0. - // This means our timelines might overlap a slight bit, but that's not an issue, as we deduplicate messages - // anyway. - let newBackward; - let oldForward; - if (backPaginationToken) { - const res = yield this.client.createMessagesRequest(this.roomId, backPaginationToken, 1, event_timeline_1.Direction.Forward); - newBackward = res.end; - } - if (forwardPaginationToken) { - const res = yield this.client.createMessagesRequest(this.roomId, forwardPaginationToken, 1, event_timeline_1.Direction.Backward); - oldForward = res.start; - } - // Only replace the token if we don't have paginated away from this position already. This situation doesn't - // occur today, but if the above issue is resolved, we'd have to go down this path. - if (forwardPaginationToken && oldLive.getPaginationToken(event_timeline_1.Direction.Forward) === forwardPaginationToken) { - oldLive.setPaginationToken(oldForward !== null && oldForward !== void 0 ? oldForward : null, event_timeline_1.Direction.Forward); - } - if (backPaginationToken && newLive.getPaginationToken(event_timeline_1.Direction.Backward) === backPaginationToken) { - newLive.setPaginationToken(newBackward !== null && newBackward !== void 0 ? newBackward : null, event_timeline_1.Direction.Backward); - } - }); - } - updateThreadMetadata() { - return __awaiter(this, void 0, void 0, function* () { - this.updatePendingReplyCount(); - if (Thread.hasServerSideSupport) { - // Ensure we show *something* as soon as possible, we'll update it as soon as we get better data, but we - // don't want the thread preview to be empty if we can avoid it - if (!this.initialEventsFetched) { - yield this.processRootEvent(); - } - yield this.fetchRootEvent(); - } - yield this.processRootEvent(); - if (!this.initialEventsFetched) { - this.initialEventsFetched = true; - // fetch initial event to allow proper pagination - try { - // if the thread has regular events, this will just load the last reply. - // if the thread is newly created, this will load the root event. - if (this.replyCount === 0 && this.rootEvent) { - this.timelineSet.addEventsToTimeline([this.rootEvent], true, this.liveTimeline, null); - this.liveTimeline.setPaginationToken(null, event_timeline_1.Direction.Backward); - } - else { - yield this.client.paginateEventTimeline(this.liveTimeline, { - backwards: true, - limit: Math.max(1, this.length), - }); - } - for (const event of this.replayEvents) { - this.addEvent(event, false); - } - this.replayEvents = null; - // just to make sure that, if we've created a timeline window for this thread before the thread itself - // existed (e.g. when creating a new thread), we'll make sure the panel is force refreshed correctly. - this.emit(room_1.RoomEvent.TimelineReset, this.room, this.timelineSet, true); - } - catch (e) { - logger_1.logger.error("Failed to load start of newly created thread: ", e); - this.initialEventsFetched = false; - } - } - this.emit(ThreadEvent.Update, this); - }); - } - // XXX: Workaround for https://github.com/matrix-org/matrix-spec-proposals/pull/2676/files#r827240084 - fetchEditsWhereNeeded(...events) { - return __awaiter(this, void 0, void 0, function* () { - return Promise.all(events - .filter((e) => e.isEncrypted()) - .map((event) => { - if (event.isRelation()) - return; // skip - relations don't get edits - return this.client - .relations(this.roomId, event.getId(), event_1.RelationType.Replace, event.getType(), { - limit: 1, - }) - .then((relations) => { - if (relations.events.length) { - event.makeReplaced(relations.events[0]); - } - }) - .catch((e) => { - logger_1.logger.error("Failed to load edits for encrypted thread event", e); - }); - })); - }); - } - setEventMetadata(event) { - if (event) { - event_timeline_1.EventTimeline.setEventMetadata(event, this.roomState, false); - event.setThread(this); - } - } - clearEventMetadata(event) { - var _a, _b, _c; - if (event) { - event.setThread(undefined); - (_c = (_b = (_a = event.event) === null || _a === void 0 ? void 0 : _a.unsigned) === null || _b === void 0 ? void 0 : _b["m.relations"]) === null || _c === void 0 ? true : delete _c[exports.THREAD_RELATION_TYPE.name]; - } - } - /** - * Finds an event by ID in the current thread - */ - findEventById(eventId) { - return this.timelineSet.findEventById(eventId); - } - /** - * Return last reply to the thread, if known. - */ - lastReply(matches = () => true) { - for (let i = this.timeline.length - 1; i >= 0; i--) { - const event = this.timeline[i]; - if (matches(event)) { - return event; - } - } - return null; - } - get roomId() { - return this.room.roomId; - } - /** - * The number of messages in the thread - * Only count rel_type=m.thread as we want to - * exclude annotations from that number - */ - get length() { - return this.replyCount + this.pendingReplyCount; - } - /** - * A getter for the last event of the thread. - * This might be a synthesized event, if so, it will not emit any events to listeners. - */ - get replyToEvent() { - var _a, _b; - return (_b = (_a = this.lastPendingEvent) !== null && _a !== void 0 ? _a : this.lastEvent) !== null && _b !== void 0 ? _b : this.lastReply(); - } - get events() { - return this.liveTimeline.getEvents(); - } - has(eventId) { - return this.timelineSet.findEventById(eventId) instanceof event_2.MatrixEvent; - } - get hasCurrentUserParticipated() { - return this._currentUserParticipated; - } - get liveTimeline() { - return this.timelineSet.getLiveTimeline(); - } - getUnfilteredTimelineSet() { - return this.timelineSet; - } - addReceipt(event, synthetic) { - throw new Error("Unsupported function on the thread model"); - } - /** - * Get the ID of the event that a given user has read up to within this thread, - * or null if we have received no read receipt (at all) from them. - * @param userId - The user ID to get read receipt event ID for - * @param ignoreSynthesized - If true, return only receipts that have been - * sent by the server, not implicit ones generated - * by the JS SDK. - * @returns ID of the latest event that the given user has read, or null. - */ - getEventReadUpTo(userId, ignoreSynthesized) { - var _a, _b; - const isCurrentUser = userId === this.client.getUserId(); - const lastReply = this.timeline[this.timeline.length - 1]; - if (isCurrentUser && lastReply) { - // If the last activity in a thread is prior to the first threaded read receipt - // sent in the room (suggesting that it was sent before the user started - // using a client that supported threaded read receipts), we want to - // consider this thread as read. - const beforeFirstThreadedReceipt = lastReply.getTs() < this.room.getOldestThreadedReceiptTs(); - const lastReplyId = lastReply.getId(); - // Some unsent events do not have an ID, we do not want to consider them read - if (beforeFirstThreadedReceipt && lastReplyId) { - return lastReplyId; - } - } - const readUpToId = super.getEventReadUpTo(userId, ignoreSynthesized); - // Check whether the unthreaded read receipt for that user is more recent - // than the read receipt inside that thread. - if (lastReply) { - const unthreadedReceipt = this.room.getLastUnthreadedReceiptFor(userId); - if (!unthreadedReceipt) { - return readUpToId; - } - for (let i = ((_a = this.timeline) === null || _a === void 0 ? void 0 : _a.length) - 1; i >= 0; --i) { - const ev = this.timeline[i]; - // If we encounter the `readUpToId` we do not need to look further - // there is no "more recent" unthreaded read receipt - if (ev.getId() === readUpToId) - return readUpToId; - // Inspecting events from most recent to oldest, we're checking - // whether an unthreaded read receipt is more recent that the current event. - // We usually prefer relying on the order of the DAG but in this scenario - // it is not possible and we have to rely on timestamp - if (ev.getTs() < unthreadedReceipt.ts) - return (_b = ev.getId()) !== null && _b !== void 0 ? _b : readUpToId; - } - } - return readUpToId; - } - /** - * Determine if the given user has read a particular event. - * - * It is invalid to call this method with an event that is not part of this thread. - * - * This is not a definitive check as it only checks the events that have been - * loaded client-side at the time of execution. - * @param userId - The user ID to check the read state of. - * @param eventId - The event ID to check if the user read. - * @returns True if the user has read the event, false otherwise. - */ - hasUserReadEvent(userId, eventId) { - var _a, _b, _c, _d, _e, _f; - if (userId === this.client.getUserId()) { - // Consider an event read if it's part of a thread that is before the - // first threaded receipt sent in that room. It is likely that it is - // part of a thread that was created before MSC3771 was implemented. - // Or before the last unthreaded receipt for the logged in user - const beforeFirstThreadedReceipt = ((_b = (_a = this.lastReply()) === null || _a === void 0 ? void 0 : _a.getTs()) !== null && _b !== void 0 ? _b : 0) < this.room.getOldestThreadedReceiptTs(); - const unthreadedReceiptTs = (_d = (_c = this.room.getLastUnthreadedReceiptFor(userId)) === null || _c === void 0 ? void 0 : _c.ts) !== null && _d !== void 0 ? _d : 0; - const beforeLastUnthreadedReceipt = ((_f = (_e = this === null || this === void 0 ? void 0 : this.lastReply()) === null || _e === void 0 ? void 0 : _e.getTs()) !== null && _f !== void 0 ? _f : 0) < unthreadedReceiptTs; - if (beforeFirstThreadedReceipt || beforeLastUnthreadedReceipt) { - return true; - } - } - return super.hasUserReadEvent(userId, eventId); - } - setUnread(type, count) { - return this.room.setThreadUnreadNotificationCount(this.id, type, count); - } -} -exports.Thread = Thread; -Thread.hasServerSideSupport = FeatureSupport.None; -Thread.hasServerSideListSupport = FeatureSupport.None; -Thread.hasServerSideFwdPaginationSupport = FeatureSupport.None; -exports.FILTER_RELATED_BY_SENDERS = new NamespacedValue_1.ServerControlledNamespacedValue("related_by_senders", "io.element.relation_senders"); -exports.FILTER_RELATED_BY_REL_TYPES = new NamespacedValue_1.ServerControlledNamespacedValue("related_by_rel_types", "io.element.relation_types"); -exports.THREAD_RELATION_TYPE = new NamespacedValue_1.ServerControlledNamespacedValue("m.thread", "io.element.thread"); -var ThreadFilterType; -(function (ThreadFilterType) { - ThreadFilterType[ThreadFilterType["My"] = 0] = "My"; - ThreadFilterType[ThreadFilterType["All"] = 1] = "All"; -})(ThreadFilterType = exports.ThreadFilterType || (exports.ThreadFilterType = {})); -function threadFilterTypeToFilter(type) { - switch (type) { - case ThreadFilterType.My: - return "participated"; - default: - return "all"; - } -} -exports.threadFilterTypeToFilter = threadFilterTypeToFilter; - -},{"../@types/event":306,"../@types/read_receipts":311,"../NamespacedValue":316,"../ReEmitter":317,"../client":321,"../logger":374,"./event":383,"./event-timeline":382,"./event-timeline-set":381,"./read-receipt":386,"./room":392}],395:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TypedEventEmitter = exports.EventEmitterEvents = void 0; -// eslint-disable-next-line no-restricted-imports -const events_1 = require("events"); -var EventEmitterEvents; -(function (EventEmitterEvents) { - EventEmitterEvents["NewListener"] = "newListener"; - EventEmitterEvents["RemoveListener"] = "removeListener"; - EventEmitterEvents["Error"] = "error"; -})(EventEmitterEvents = exports.EventEmitterEvents || (exports.EventEmitterEvents = {})); -/** - * Typed Event Emitter class which can act as a Base Model for all our model - * and communication events. - * This makes it much easier for us to distinguish between events, as we now need - * to properly type this, so that our events are not stringly-based and prone - * to silly typos. - */ -class TypedEventEmitter extends events_1.EventEmitter { - addListener(event, listener) { - return super.addListener(event, listener); - } - emit(event, ...args) { - return super.emit(event, ...args); - } - eventNames() { - return super.eventNames(); - } - listenerCount(event) { - return super.listenerCount(event); - } - listeners(event) { - return super.listeners(event); - } - off(event, listener) { - return super.off(event, listener); - } - on(event, listener) { - return super.on(event, listener); - } - once(event, listener) { - return super.once(event, listener); - } - prependListener(event, listener) { - return super.prependListener(event, listener); - } - prependOnceListener(event, listener) { - return super.prependOnceListener(event, listener); - } - removeAllListeners(event) { - return super.removeAllListeners(event); - } - removeListener(event, listener) { - return super.removeListener(event, listener); - } - rawListeners(event) { - return super.rawListeners(event); - } -} -exports.TypedEventEmitter = TypedEventEmitter; - -},{"events":105}],396:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.User = exports.UserEvent = void 0; -const typed_event_emitter_1 = require("./typed-event-emitter"); -var UserEvent; -(function (UserEvent) { - UserEvent["DisplayName"] = "User.displayName"; - UserEvent["AvatarUrl"] = "User.avatarUrl"; - UserEvent["Presence"] = "User.presence"; - UserEvent["CurrentlyActive"] = "User.currentlyActive"; - UserEvent["LastPresenceTs"] = "User.lastPresenceTs"; -})(UserEvent = exports.UserEvent || (exports.UserEvent = {})); -class User extends typed_event_emitter_1.TypedEventEmitter { - /** - * Construct a new User. A User must have an ID and can optionally have extra information associated with it. - * @param userId - Required. The ID of this user. - */ - constructor(userId) { - super(); - this.userId = userId; - this.modified = -1; - /** - * The presence enum if known. - * @privateRemarks - * Should be read-only - */ - this.presence = "offline"; - /** - * Timestamp (ms since the epoch) for when we last received presence data for this user. - * We can subtract lastActiveAgo from this to approximate an absolute value for when a user was last active. - * @privateRemarks - * Should be read-only - */ - this.lastActiveAgo = 0; - /** - * The time elapsed in ms since the user interacted proactively with the server, - * or we saw a message from the user - * @privateRemarks - * Should be read-only - */ - this.lastPresenceTs = 0; - /** - * Whether we should consider lastActiveAgo to be an approximation - * and that the user should be seen as active 'now' - * @privateRemarks - * Should be read-only - */ - this.currentlyActive = false; - /** - * The events describing this user. - * @privateRemarks - * Should be read-only - */ - this.events = {}; - this.displayName = userId; - this.rawDisplayName = userId; - this.updateModifiedTime(); - } - /** - * Update this User with the given presence event. May fire "User.presence", - * "User.avatarUrl" and/or "User.displayName" if this event updates this user's - * properties. - * @param event - The `m.presence` event. - * - * @remarks - * Fires {@link UserEvent.Presence} - * Fires {@link UserEvent.DisplayName} - * Fires {@link UserEvent.AvatarUrl} - */ - setPresenceEvent(event) { - if (event.getType() !== "m.presence") { - return; - } - const firstFire = this.events.presence === null; - this.events.presence = event; - const eventsToFire = []; - if (event.getContent().presence !== this.presence || firstFire) { - eventsToFire.push(UserEvent.Presence); - } - if (event.getContent().avatar_url && event.getContent().avatar_url !== this.avatarUrl) { - eventsToFire.push(UserEvent.AvatarUrl); - } - if (event.getContent().displayname && event.getContent().displayname !== this.displayName) { - eventsToFire.push(UserEvent.DisplayName); - } - if (event.getContent().currently_active !== undefined && - event.getContent().currently_active !== this.currentlyActive) { - eventsToFire.push(UserEvent.CurrentlyActive); - } - this.presence = event.getContent().presence; - eventsToFire.push(UserEvent.LastPresenceTs); - if (event.getContent().status_msg) { - this.presenceStatusMsg = event.getContent().status_msg; - } - if (event.getContent().displayname) { - this.displayName = event.getContent().displayname; - } - if (event.getContent().avatar_url) { - this.avatarUrl = event.getContent().avatar_url; - } - this.lastActiveAgo = event.getContent().last_active_ago; - this.lastPresenceTs = Date.now(); - this.currentlyActive = event.getContent().currently_active; - this.updateModifiedTime(); - for (const eventToFire of eventsToFire) { - this.emit(eventToFire, event, this); - } - } - /** - * Manually set this user's display name. No event is emitted in response to this - * as there is no underlying MatrixEvent to emit with. - * @param name - The new display name. - */ - setDisplayName(name) { - const oldName = this.displayName; - this.displayName = name; - if (name !== oldName) { - this.updateModifiedTime(); - } - } - /** - * Manually set this user's non-disambiguated display name. No event is emitted - * in response to this as there is no underlying MatrixEvent to emit with. - * @param name - The new display name. - */ - setRawDisplayName(name) { - this.rawDisplayName = name; - } - /** - * Manually set this user's avatar URL. No event is emitted in response to this - * as there is no underlying MatrixEvent to emit with. - * @param url - The new avatar URL. - */ - setAvatarUrl(url) { - const oldUrl = this.avatarUrl; - this.avatarUrl = url; - if (url !== oldUrl) { - this.updateModifiedTime(); - } - } - /** - * Update the last modified time to the current time. - */ - updateModifiedTime() { - this.modified = Date.now(); - } - /** - * Get the timestamp when this User was last updated. This timestamp is - * updated when this User receives a new Presence event which has updated a - * property on this object. It is updated before firing events. - * @returns The timestamp - */ - getLastModifiedTime() { - return this.modified; - } - /** - * Get the absolute timestamp when this User was last known active on the server. - * It is *NOT* accurate if this.currentlyActive is true. - * @returns The timestamp - */ - getLastActiveTs() { - return this.lastPresenceTs - this.lastActiveAgo; - } -} -exports.User = User; - -},{"./typed-event-emitter":395}],397:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PushProcessor = void 0; -const utils_1 = require("./utils"); -const logger_1 = require("./logger"); -const PushRules_1 = require("./@types/PushRules"); -const event_1 = require("./@types/event"); -const RULEKINDS_IN_ORDER = [ - PushRules_1.PushRuleKind.Override, - PushRules_1.PushRuleKind.ContentSpecific, - PushRules_1.PushRuleKind.RoomSpecific, - PushRules_1.PushRuleKind.SenderSpecific, - PushRules_1.PushRuleKind.Underride, -]; -// The default override rules to apply to the push rules that arrive from the server. -// We do this for two reasons: -// 1. Synapse is unlikely to send us the push rule in an incremental sync - see -// https://github.com/matrix-org/synapse/pull/4867#issuecomment-481446072 for -// more details. -// 2. We often want to start using push rules ahead of the server supporting them, -// and so we can put them here. -const DEFAULT_OVERRIDE_RULES = [ - { - // For homeservers which don't support MSC2153 yet - rule_id: ".m.rule.reaction", - default: true, - enabled: true, - conditions: [ - { - kind: PushRules_1.ConditionKind.EventMatch, - key: "type", - pattern: "m.reaction", - }, - ], - actions: [PushRules_1.PushRuleActionName.DontNotify], - }, - { - rule_id: PushRules_1.RuleId.IsUserMention, - default: true, - enabled: true, - conditions: [ - { - kind: PushRules_1.ConditionKind.EventPropertyContains, - key: "content.org\\.matrix\\.msc3952\\.mentions.user_ids", - value: "", // The user ID is dynamically added in rewriteDefaultRules. - }, - ], - actions: [PushRules_1.PushRuleActionName.Notify, { set_tweak: PushRules_1.TweakName.Highlight }], - }, - { - rule_id: PushRules_1.RuleId.IsRoomMention, - default: true, - enabled: true, - conditions: [ - { - kind: PushRules_1.ConditionKind.EventPropertyIs, - key: "content.org\\.matrix\\.msc3952\\.mentions.room", - value: true, - }, - { - kind: PushRules_1.ConditionKind.SenderNotificationPermission, - key: "room", - }, - ], - actions: [PushRules_1.PushRuleActionName.Notify, { set_tweak: PushRules_1.TweakName.Highlight }], - }, - { - // For homeservers which don't support MSC3786 yet - rule_id: ".org.matrix.msc3786.rule.room.server_acl", - default: true, - enabled: true, - conditions: [ - { - kind: PushRules_1.ConditionKind.EventMatch, - key: "type", - pattern: event_1.EventType.RoomServerAcl, - }, - { - kind: PushRules_1.ConditionKind.EventMatch, - key: "state_key", - pattern: "", - }, - ], - actions: [], - }, -]; -const DEFAULT_UNDERRIDE_RULES = [ - { - // For homeservers which don't support MSC3914 yet - rule_id: ".org.matrix.msc3914.rule.room.call", - default: true, - enabled: true, - conditions: [ - { - kind: PushRules_1.ConditionKind.EventMatch, - key: "type", - pattern: "org.matrix.msc3401.call", - }, - { - kind: PushRules_1.ConditionKind.CallStarted, - }, - ], - actions: [PushRules_1.PushRuleActionName.Notify, { set_tweak: PushRules_1.TweakName.Sound, value: "default" }], - }, -]; -class PushProcessor { - /** - * Construct a Push Processor. - * @param client - The Matrix client object to use - */ - constructor(client) { - this.client = client; - /** - * Maps the original key from the push rules to a list of property names - * after unescaping. - */ - this.parsedKeys = new Map(); - } - /** - * Convert a list of actions into a object with the actions as keys and their values - * @example - * eg. `[ 'notify', { set_tweak: 'sound', value: 'default' } ]` - * becomes `{ notify: true, tweaks: { sound: 'default' } }` - * @param actionList - The actions list - * - * @returns A object with key 'notify' (true or false) and an object of actions - */ - static actionListToActionsObject(actionList) { - const actionObj = { notify: false, tweaks: {} }; - for (const action of actionList) { - if (action === PushRules_1.PushRuleActionName.Notify) { - actionObj.notify = true; - } - else if (typeof action === "object") { - if (action.value === undefined) { - action.value = true; - } - actionObj.tweaks[action.set_tweak] = action.value; - } - } - return actionObj; - } - /** - * Rewrites conditions on a client's push rules to match the defaults - * where applicable. Useful for upgrading push rules to more strict - * conditions when the server is falling behind on defaults. - * @param incomingRules - The client's existing push rules - * @param userId - The Matrix ID of the client. - * @returns The rewritten rules - */ - static rewriteDefaultRules(incomingRules, userId = undefined) { - var _a; - let newRules = JSON.parse(JSON.stringify(incomingRules)); // deep clone - // These lines are mostly to make the tests happy. We shouldn't run into these - // properties missing in practice. - if (!newRules) - newRules = {}; - if (!newRules.global) - newRules.global = {}; - if (!newRules.global.override) - newRules.global.override = []; - if (!newRules.global.underride) - newRules.global.underride = []; - // Merge the client-level defaults with the ones from the server - const globalOverrides = newRules.global.override; - for (const originalOverride of DEFAULT_OVERRIDE_RULES) { - const existingRule = globalOverrides.find((r) => r.rule_id === originalOverride.rule_id); - // Dynamically add the user ID as the value for the is_user_mention rule. - let override; - if (originalOverride.rule_id === PushRules_1.RuleId.IsUserMention) { - // If the user ID wasn't provided, skip the rule. - if (!userId) { - continue; - } - override = JSON.parse(JSON.stringify(originalOverride)); // deep clone - override.conditions[0].value = userId; - } - else { - override = originalOverride; - } - if (existingRule) { - // Copy over the actions, default, and conditions. Don't touch the user's preference. - existingRule.default = override.default; - existingRule.conditions = override.conditions; - existingRule.actions = override.actions; - } - else { - // Add the rule - const ruleId = override.rule_id; - logger_1.logger.warn(`Adding default global override for ${ruleId}`); - globalOverrides.push(override); - } - } - const globalUnderrides = (_a = newRules.global.underride) !== null && _a !== void 0 ? _a : []; - for (const underride of DEFAULT_UNDERRIDE_RULES) { - const existingRule = globalUnderrides.find((r) => r.rule_id === underride.rule_id); - if (existingRule) { - // Copy over the actions, default, and conditions. Don't touch the user's preference. - existingRule.default = underride.default; - existingRule.conditions = underride.conditions; - existingRule.actions = underride.actions; - } - else { - // Add the rule - const ruleId = underride.rule_id; - logger_1.logger.warn(`Adding default global underride for ${ruleId}`); - globalUnderrides.push(underride); - } - } - return newRules; - } - /** - * Pre-caches the parsed keys for push rules and cleans out any obsolete cache - * entries. Should be called after push rules are updated. - * @param newRules - The new push rules. - */ - updateCachedPushRuleKeys(newRules) { - // These lines are mostly to make the tests happy. We shouldn't run into these - // properties missing in practice. - if (!newRules) - newRules = {}; - if (!newRules.global) - newRules.global = {}; - if (!newRules.global.override) - newRules.global.override = []; - if (!newRules.global.room) - newRules.global.room = []; - if (!newRules.global.sender) - newRules.global.sender = []; - if (!newRules.global.underride) - newRules.global.underride = []; - // Process the 'key' property on event_match conditions pre-cache the - // values and clean-out any unused values. - const toRemoveKeys = new Set(this.parsedKeys.keys()); - for (const ruleset of [ - newRules.global.override, - newRules.global.room, - newRules.global.sender, - newRules.global.underride, - ]) { - for (const rule of ruleset) { - if (!rule.conditions) { - continue; - } - for (const condition of rule.conditions) { - if (condition.kind !== PushRules_1.ConditionKind.EventMatch) { - continue; - } - // Ensure we keep this key. - toRemoveKeys.delete(condition.key); - // Pre-process the key. - this.parsedKeys.set(condition.key, PushProcessor.partsForDottedKey(condition.key)); - } - } - } - // Any keys that were previously cached, but are no longer needed should - // be removed. - toRemoveKeys.forEach((k) => this.parsedKeys.delete(k)); - } - matchingRuleFromKindSet(ev, kindset) { - for (const kind of RULEKINDS_IN_ORDER) { - const ruleset = kindset[kind]; - if (!ruleset) { - continue; - } - for (const rule of ruleset) { - if (!rule.enabled) { - continue; - } - const rawrule = this.templateRuleToRaw(kind, rule); - if (!rawrule) { - continue; - } - if (this.ruleMatchesEvent(rawrule, ev)) { - return Object.assign(Object.assign({}, rule), { kind }); - } - } - } - return null; - } - templateRuleToRaw(kind, tprule) { - const rawrule = { - rule_id: tprule.rule_id, - actions: tprule.actions, - conditions: [], - }; - switch (kind) { - case PushRules_1.PushRuleKind.Underride: - case PushRules_1.PushRuleKind.Override: - rawrule.conditions = tprule.conditions; - break; - case PushRules_1.PushRuleKind.RoomSpecific: - if (!tprule.rule_id) { - return null; - } - rawrule.conditions.push({ - kind: PushRules_1.ConditionKind.EventMatch, - key: "room_id", - value: tprule.rule_id, - }); - break; - case PushRules_1.PushRuleKind.SenderSpecific: - if (!tprule.rule_id) { - return null; - } - rawrule.conditions.push({ - kind: PushRules_1.ConditionKind.EventMatch, - key: "user_id", - value: tprule.rule_id, - }); - break; - case PushRules_1.PushRuleKind.ContentSpecific: - if (!tprule.pattern) { - return null; - } - rawrule.conditions.push({ - kind: PushRules_1.ConditionKind.EventMatch, - key: "content.body", - pattern: tprule.pattern, - }); - break; - } - return rawrule; - } - eventFulfillsCondition(cond, ev) { - switch (cond.kind) { - case PushRules_1.ConditionKind.EventMatch: - return this.eventFulfillsEventMatchCondition(cond, ev); - case PushRules_1.ConditionKind.EventPropertyIs: - return this.eventFulfillsEventPropertyIsCondition(cond, ev); - case PushRules_1.ConditionKind.EventPropertyContains: - return this.eventFulfillsEventPropertyContains(cond, ev); - case PushRules_1.ConditionKind.ContainsDisplayName: - return this.eventFulfillsDisplayNameCondition(cond, ev); - case PushRules_1.ConditionKind.RoomMemberCount: - return this.eventFulfillsRoomMemberCountCondition(cond, ev); - case PushRules_1.ConditionKind.SenderNotificationPermission: - return this.eventFulfillsSenderNotifPermCondition(cond, ev); - case PushRules_1.ConditionKind.CallStarted: - case PushRules_1.ConditionKind.CallStartedPrefix: - return this.eventFulfillsCallStartedCondition(cond, ev); - } - // unknown conditions: we previously matched all unknown conditions, - // but given that rules can be added to the base rules on a server, - // it's probably better to not match unknown conditions. - return false; - } - eventFulfillsSenderNotifPermCondition(cond, ev) { - const notifLevelKey = cond["key"]; - if (!notifLevelKey) { - return false; - } - const room = this.client.getRoom(ev.getRoomId()); - if (!(room === null || room === void 0 ? void 0 : room.currentState)) { - return false; - } - // Note that this should not be the current state of the room but the state at - // the point the event is in the DAG. Unfortunately the js-sdk does not store - // this. - return room.currentState.mayTriggerNotifOfType(notifLevelKey, ev.getSender()); - } - eventFulfillsRoomMemberCountCondition(cond, ev) { - if (!cond.is) { - return false; - } - const room = this.client.getRoom(ev.getRoomId()); - if (!room || !room.currentState || !room.currentState.members) { - return false; - } - const memberCount = room.currentState.getJoinedMemberCount(); - const m = cond.is.match(/^([=<>]*)(\d*)$/); - if (!m) { - return false; - } - const ineq = m[1]; - const rhs = parseInt(m[2]); - if (isNaN(rhs)) { - return false; - } - switch (ineq) { - case "": - case "==": - return memberCount == rhs; - case "<": - return memberCount < rhs; - case ">": - return memberCount > rhs; - case "<=": - return memberCount <= rhs; - case ">=": - return memberCount >= rhs; - default: - return false; - } - } - eventFulfillsDisplayNameCondition(cond, ev) { - var _a; - let content = ev.getContent(); - if (ev.isEncrypted() && ev.getClearContent()) { - content = ev.getClearContent(); - } - if (!content || !content.body || typeof content.body != "string") { - return false; - } - const room = this.client.getRoom(ev.getRoomId()); - const member = (_a = room === null || room === void 0 ? void 0 : room.currentState) === null || _a === void 0 ? void 0 : _a.getMember(this.client.credentials.userId); - if (!member) { - return false; - } - const displayName = member.name; - // N.B. we can't use \b as it chokes on unicode. however \W seems to be okay - // as shorthand for [^0-9A-Za-z_]. - const pat = new RegExp("(^|\\W)" + (0, utils_1.escapeRegExp)(displayName) + "(\\W|$)", "i"); - return content.body.search(pat) > -1; - } - /** - * Check whether the given event matches the push rule condition by fetching - * the property from the event and comparing against the condition's glob-based - * pattern. - * @param cond - The push rule condition to check for a match. - * @param ev - The event to check for a match. - */ - eventFulfillsEventMatchCondition(cond, ev) { - if (!cond.key) { - return false; - } - const val = this.valueForDottedKey(cond.key, ev); - if (typeof val !== "string") { - return false; - } - // XXX This does not match in a case-insensitive manner. - // - // See https://spec.matrix.org/v1.5/client-server-api/#conditions-1 - if (cond.value) { - return cond.value === val; - } - if (typeof cond.pattern !== "string") { - return false; - } - const regex = cond.key === "content.body" - ? this.createCachedRegex("(^|\\W)", cond.pattern, "(\\W|$)") - : this.createCachedRegex("^", cond.pattern, "$"); - return !!val.match(regex); - } - /** - * Check whether the given event matches the push rule condition by fetching - * the property from the event and comparing exactly against the condition's - * value. - * @param cond - The push rule condition to check for a match. - * @param ev - The event to check for a match. - */ - eventFulfillsEventPropertyIsCondition(cond, ev) { - if (!cond.key || cond.value === undefined) { - return false; - } - return cond.value === this.valueForDottedKey(cond.key, ev); - } - /** - * Check whether the given event matches the push rule condition by fetching - * the property from the event and comparing exactly against the condition's - * value. - * @param cond - The push rule condition to check for a match. - * @param ev - The event to check for a match. - */ - eventFulfillsEventPropertyContains(cond, ev) { - if (!cond.key || cond.value === undefined) { - return false; - } - const val = this.valueForDottedKey(cond.key, ev); - if (!Array.isArray(val)) { - return false; - } - return val.includes(cond.value); - } - eventFulfillsCallStartedCondition(_cond, ev) { - // Since servers don't support properly sending push notification - // about MSC3401 call events, we do the handling ourselves - return (["m.ring", "m.prompt"].includes(ev.getContent()["m.intent"]) && - !("m.terminated" in ev.getContent()) && - (ev.getPrevContent()["m.terminated"] !== ev.getContent()["m.terminated"] || - (0, utils_1.deepCompare)(ev.getPrevContent(), {}))); - } - createCachedRegex(prefix, glob, suffix) { - if (PushProcessor.cachedGlobToRegex[glob]) { - return PushProcessor.cachedGlobToRegex[glob]; - } - PushProcessor.cachedGlobToRegex[glob] = new RegExp(prefix + (0, utils_1.globToRegexp)(glob) + suffix, "i"); - return PushProcessor.cachedGlobToRegex[glob]; - } - /** - * Parse the key into the separate fields to search by splitting on - * unescaped ".", and then removing any escape characters. - * - * @param str - The key of the push rule condition: a dotted field. - * @returns The unescaped parts to fetch. - * @internal - */ - static partsForDottedKey(str) { - const result = []; - // The current field and whether the previous character was the escape - // character (a backslash). - let part = ""; - let escaped = false; - // Iterate over each character, and decide whether to append to the current - // part (following the escape rules) or to start a new part (based on the - // field separator). - for (const c of str) { - // If the previous character was the escape character (a backslash) - // then decide what to append to the current part. - if (escaped) { - if (c === "\\" || c === ".") { - // An escaped backslash or dot just gets added. - part += c; - } - else { - // A character that shouldn't be escaped gets the backslash prepended. - part += "\\" + c; - } - // This always resets being escaped. - escaped = false; - continue; - } - if (c == ".") { - // The field separator creates a new part. - result.push(part); - part = ""; - } - else if (c == "\\") { - // A backslash adds no characters, but starts an escape sequence. - escaped = true; - } - else { - // Otherwise, just add the current character. - part += c; - } - } - // Ensure the final part is included. If there's an open escape sequence - // it should be included. - if (escaped) { - part += "\\"; - } - result.push(part); - return result; - } - /** - * For a dotted field and event, fetch the value at that position, if one - * exists. - * - * @param key - The key of the push rule condition: a dotted field to fetch. - * @param ev - The matrix event to fetch the field from. - * @returns The value at the dotted path given by key. - */ - valueForDottedKey(key, ev) { - // The key should already have been parsed via updateCachedPushRuleKeys, - // but if it hasn't (maybe via an old consumer of the SDK which hasn't - // been updated?) then lazily calculate it here. - let parts = this.parsedKeys.get(key); - if (parts === undefined) { - parts = PushProcessor.partsForDottedKey(key); - this.parsedKeys.set(key, parts); - } - let val; - // special-case the first component to deal with encrypted messages - const firstPart = parts[0]; - let currentIndex = 0; - if (firstPart === "content") { - val = ev.getContent(); - ++currentIndex; - } - else if (firstPart === "type") { - val = ev.getType(); - ++currentIndex; - } - else { - // use the raw event for any other fields - val = ev.event; - } - for (; currentIndex < parts.length; ++currentIndex) { - // The previous iteration resulted in null or undefined, bail (and - // avoid the type error of attempting to retrieve a property). - if ((0, utils_1.isNullOrUndefined)(val)) { - return undefined; - } - const thisPart = parts[currentIndex]; - val = val[thisPart]; - } - return val; - } - matchingRuleForEventWithRulesets(ev, rulesets) { - if (!rulesets) { - return null; - } - if (ev.getSender() === this.client.credentials.userId) { - return null; - } - return this.matchingRuleFromKindSet(ev, rulesets.global); - } - pushActionsForEventAndRulesets(ev, rulesets) { - const rule = this.matchingRuleForEventWithRulesets(ev, rulesets); - if (!rule) { - return {}; - } - const actionObj = PushProcessor.actionListToActionsObject(rule.actions); - // Some actions are implicit in some situations: we add those here - if (actionObj.tweaks.highlight === undefined) { - // if it isn't specified, highlight if it's a content - // rule but otherwise not - actionObj.tweaks.highlight = rule.kind == PushRules_1.PushRuleKind.ContentSpecific; - } - return actionObj; - } - ruleMatchesEvent(rule, ev) { - var _a; - // Disable the deprecated mentions push rules if the new mentions property exists. - if (this.client.supportsIntentionalMentions() && - ev.getContent()["org.matrix.msc3952.mentions"] !== undefined && - (rule.rule_id === PushRules_1.RuleId.ContainsUserName || - rule.rule_id === PushRules_1.RuleId.ContainsDisplayName || - rule.rule_id === PushRules_1.RuleId.AtRoomNotification)) { - return false; - } - return !((_a = rule.conditions) === null || _a === void 0 ? void 0 : _a.some((cond) => !this.eventFulfillsCondition(cond, ev))); - } - /** - * Get the user's push actions for the given event - */ - actionsForEvent(ev) { - return this.pushActionsForEventAndRulesets(ev, this.client.pushRules); - } - /** - * Get one of the users push rules by its ID - * - * @param ruleId - The ID of the rule to search for - * @returns The push rule, or null if no such rule was found - */ - getPushRuleById(ruleId) { - var _a; - const result = this.getPushRuleAndKindById(ruleId); - return (_a = result === null || result === void 0 ? void 0 : result.rule) !== null && _a !== void 0 ? _a : null; - } - /** - * Get one of the users push rules by its ID - * - * @param ruleId - The ID of the rule to search for - * @returns rule The push rule, or null if no such rule was found - * @returns kind - The PushRuleKind of the rule to search for - */ - getPushRuleAndKindById(ruleId) { - var _a; - for (const scope of ["global"]) { - if (((_a = this.client.pushRules) === null || _a === void 0 ? void 0 : _a[scope]) === undefined) - continue; - for (const kind of RULEKINDS_IN_ORDER) { - if (this.client.pushRules[scope][kind] === undefined) - continue; - for (const rule of this.client.pushRules[scope][kind]) { - if (rule.rule_id === ruleId) - return { rule, kind }; - } - } - } - return null; - } -} -exports.PushProcessor = PushProcessor; -PushProcessor.cachedGlobToRegex = {}; // $glob: RegExp - -},{"./@types/PushRules":304,"./@types/event":306,"./logger":374,"./utils":416}],398:[function(require,module,exports){ -"use strict"; -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.randomUppercaseString = exports.randomLowercaseString = exports.randomString = void 0; -const LOWERCASE = "abcdefghijklmnopqrstuvwxyz"; -const UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const DIGITS = "0123456789"; -function randomString(len) { - return randomStringFrom(len, UPPERCASE + LOWERCASE + DIGITS); -} -exports.randomString = randomString; -function randomLowercaseString(len) { - return randomStringFrom(len, LOWERCASE); -} -exports.randomLowercaseString = randomLowercaseString; -function randomUppercaseString(len) { - return randomStringFrom(len, UPPERCASE); -} -exports.randomUppercaseString = randomUppercaseString; -function randomStringFrom(len, chars) { - let ret = ""; - for (let i = 0; i < len; ++i) { - ret += chars.charAt(Math.floor(Math.random() * chars.length)); - } - return ret; -} - -},{}],399:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.clearTimeout = exports.setTimeout = void 0; -/* A re-implementation of the javascript callback functions (setTimeout, - * clearTimeout; setInterval and clearInterval are not yet implemented) which - * try to improve handling of large clock jumps (as seen when - * suspending/resuming the system). - * - * In particular, if a timeout would have fired while the system was suspended, - * it will instead fire as soon as possible after resume. - */ -const logger_1 = require("./logger"); -// we schedule a callback at least this often, to check if we've missed out on -// some wall-clock time due to being suspended. -const TIMER_CHECK_PERIOD_MS = 1000; -// counter, for making up ids to return from setTimeout -let count = 0; -// the key for our callback with the real global.setTimeout -let realCallbackKey; -// a sorted list of the callbacks to be run. -// each is an object with keys [runAt, func, params, key]. -const callbackList = []; -// var debuglog = logger.log.bind(logger); -/* istanbul ignore next */ -const debuglog = function (...params) { }; -/** - * reimplementation of window.setTimeout, which will call the callback if - * the wallclock time goes past the deadline. - * - * @param func - callback to be called after a delay - * @param delayMs - number of milliseconds to delay by - * - * @returns an identifier for this callback, which may be passed into - * clearTimeout later. - */ -function setTimeout(func, delayMs, ...params) { - delayMs = delayMs || 0; - if (delayMs < 0) { - delayMs = 0; - } - const runAt = Date.now() + delayMs; - const key = count++; - debuglog("setTimeout: scheduling cb", key, "at", runAt, "(delay", delayMs, ")"); - const data = { - runAt: runAt, - func: func, - params: params, - key: key, - }; - // figure out where it goes in the list - const idx = binarySearch(callbackList, function (el) { - return el.runAt - runAt; - }); - callbackList.splice(idx, 0, data); - scheduleRealCallback(); - return key; -} -exports.setTimeout = setTimeout; -/** - * reimplementation of window.clearTimeout, which mirrors setTimeout - * - * @param key - result from an earlier setTimeout call - */ -function clearTimeout(key) { - if (callbackList.length === 0) { - return; - } - // remove the element from the list - let i; - for (i = 0; i < callbackList.length; i++) { - const cb = callbackList[i]; - if (cb.key == key) { - callbackList.splice(i, 1); - break; - } - } - // iff it was the first one in the list, reschedule our callback. - if (i === 0) { - scheduleRealCallback(); - } -} -exports.clearTimeout = clearTimeout; -// use the real global.setTimeout to schedule a callback to runCallbacks. -function scheduleRealCallback() { - if (realCallbackKey) { - global.clearTimeout(realCallbackKey); - } - const first = callbackList[0]; - if (!first) { - debuglog("scheduleRealCallback: no more callbacks, not rescheduling"); - return; - } - const timestamp = Date.now(); - const delayMs = Math.min(first.runAt - timestamp, TIMER_CHECK_PERIOD_MS); - debuglog("scheduleRealCallback: now:", timestamp, "delay:", delayMs); - realCallbackKey = global.setTimeout(runCallbacks, delayMs); -} -function runCallbacks() { - const timestamp = Date.now(); - debuglog("runCallbacks: now:", timestamp); - // get the list of things to call - const callbacksToRun = []; - // eslint-disable-next-line - while (true) { - const first = callbackList[0]; - if (!first || first.runAt > timestamp) { - break; - } - const cb = callbackList.shift(); - debuglog("runCallbacks: popping", cb.key); - callbacksToRun.push(cb); - } - // reschedule the real callback before running our functions, to - // keep the codepaths the same whether or not our functions - // register their own setTimeouts. - scheduleRealCallback(); - for (const cb of callbacksToRun) { - try { - cb.func.apply(global, cb.params); - } - catch (e) { - logger_1.logger.error("Uncaught exception in callback function", e); - } - } -} -/* search in a sorted array. - * - * returns the index of the last element for which func returns - * greater than zero, or array.length if no such element exists. - */ -function binarySearch(array, func) { - // min is inclusive, max exclusive. - let min = 0; - let max = array.length; - while (min < max) { - const mid = (min + max) >> 1; - const res = func(array[mid]); - if (res > 0) { - // the element at 'mid' is too big; set it as the new max. - max = mid; - } - else { - // the element at 'mid' is too small. 'min' is inclusive, so +1. - min = mid + 1; - } - } - // presumably, min==max now. - return min; -} - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./logger":374}],400:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.initRustCrypto = void 0; -function initRustCrypto(_http, _userId, _deviceId) { - return __awaiter(this, void 0, void 0, function* () { - throw new Error("Rust crypto is not supported under browserify."); - }); -} -exports.initRustCrypto = initRustCrypto; - -},{}],401:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RUST_SDK_STORE_PREFIX = void 0; -/** The prefix used on indexeddbs created by rust-crypto */ -exports.RUST_SDK_STORE_PREFIX = "matrix-js-sdk"; - -},{}],402:[function(require,module,exports){ -module.exports = require('/home/runner/work/matrix-js-sdk/matrix-js-sdk/src/rust-crypto/browserify-index.ts'); -},{"/home/runner/work/matrix-js-sdk/matrix-js-sdk/src/rust-crypto/browserify-index.ts":400}],403:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MatrixScheduler = void 0; -/** - * This is an internal module which manages queuing, scheduling and retrying - * of requests. - */ -const utils = __importStar(require("./utils")); -const logger_1 = require("./logger"); -const event_1 = require("./@types/event"); -const http_api_1 = require("./http-api"); -const DEBUG = false; // set true to enable console logging. -// eslint-disable-next-line camelcase -class MatrixScheduler { - /** - * Retries events up to 4 times using exponential backoff. This produces wait - * times of 2, 4, 8, and 16 seconds (30s total) after which we give up. If the - * failure was due to a rate limited request, the time specified in the error is - * waited before being retried. - * @param attempts - Number of attempts that have been made, including the one that just failed (ie. starting at 1) - * @see retryAlgorithm - */ - // eslint-disable-next-line @typescript-eslint/naming-convention - static RETRY_BACKOFF_RATELIMIT(event, attempts, err) { - if (err.httpStatus === 400 || err.httpStatus === 403 || err.httpStatus === 401) { - // client error; no amount of retrying with save you now. - return -1; - } - if (err instanceof http_api_1.ConnectionError) { - return -1; - } - // if event that we are trying to send is too large in any way then retrying won't help - if (err.name === "M_TOO_LARGE") { - return -1; - } - if (err.name === "M_LIMIT_EXCEEDED") { - const waitTime = err.data.retry_after_ms; - if (waitTime > 0) { - return waitTime; - } - } - if (attempts > 4) { - return -1; // give up - } - return 1000 * Math.pow(2, attempts); - } - /** - * Queues `m.room.message` events and lets other events continue - * concurrently. - * @see queueAlgorithm - */ - // eslint-disable-next-line @typescript-eslint/naming-convention - static QUEUE_MESSAGES(event) { - // enqueue messages or events that associate with another event (redactions and relations) - if (event.getType() === event_1.EventType.RoomMessage || event.hasAssociation()) { - // put these events in the 'message' queue. - return "message"; - } - // allow all other events continue concurrently. - return null; - } - /** - * Construct a scheduler for Matrix. Requires - * {@link MatrixScheduler#setProcessFunction} to be provided - * with a way of processing events. - * @param retryAlgorithm - Optional. The retry - * algorithm to apply when determining when to try to send an event again. - * Defaults to {@link MatrixScheduler.RETRY_BACKOFF_RATELIMIT}. - * @param queueAlgorithm - Optional. The queuing - * algorithm to apply when determining which events should be sent before the - * given event. Defaults to {@link MatrixScheduler.QUEUE_MESSAGES}. - */ - constructor( - /** - * The retry algorithm to apply when retrying events. To stop retrying, return - * `-1`. If this event was part of a queue, it will be removed from - * the queue. - * @param event - The event being retried. - * @param attempts - The number of failed attempts. This will always be \>= 1. - * @param err - The most recent error message received when trying - * to send this event. - * @returns The number of milliseconds to wait before trying again. If - * this is 0, the request will be immediately retried. If this is - * `-1`, the event will be marked as - * {@link EventStatus.NOT_SENT} and will not be retried. - */ - retryAlgorithm = MatrixScheduler.RETRY_BACKOFF_RATELIMIT, - /** - * The queuing algorithm to apply to events. This function must be idempotent as - * it may be called multiple times with the same event. All queues created are - * serviced in a FIFO manner. To send the event ASAP, return `null` - * which will not put this event in a queue. Events that fail to send that form - * part of a queue will be removed from the queue and the next event in the - * queue will be sent. - * @param event - The event to be sent. - * @returns The name of the queue to put the event into. If a queue with - * this name does not exist, it will be created. If this is `null`, - * the event is not put into a queue and will be sent concurrently. - */ - queueAlgorithm = MatrixScheduler.QUEUE_MESSAGES) { - this.retryAlgorithm = retryAlgorithm; - this.queueAlgorithm = queueAlgorithm; - // queueName: [{ - // event: MatrixEvent, // event to send - // defer: Deferred, // defer to resolve/reject at the END of the retries - // attempts: Number // number of times we've called processFn - // }, ...] - this.queues = {}; - this.activeQueues = []; - this.procFn = null; - this.processQueue = (queueName) => { - // get head of queue - const obj = this.peekNextEvent(queueName); - if (!obj) { - this.disableQueue(queueName); - return; - } - debuglog("Queue '%s' has %s pending events", queueName, this.queues[queueName].length); - // fire the process function and if it resolves, resolve the deferred. Else - // invoke the retry algorithm. - // First wait for a resolved promise, so the resolve handlers for - // the deferred of the previously sent event can run. - // This way enqueued relations/redactions to enqueued events can receive - // the remove id of their target before being sent. - Promise.resolve() - .then(() => { - return this.procFn(obj.event); - }) - .then((res) => { - // remove this from the queue - this.removeNextEvent(queueName); - debuglog("Queue '%s' sent event %s", queueName, obj.event.getId()); - obj.defer.resolve(res); - // keep processing - this.processQueue(queueName); - }, (err) => { - obj.attempts += 1; - // ask the retry algorithm when/if we should try again - const waitTimeMs = this.retryAlgorithm(obj.event, obj.attempts, err); - debuglog("retry(%s) err=%s event_id=%s waitTime=%s", obj.attempts, err, obj.event.getId(), waitTimeMs); - if (waitTimeMs === -1) { - // give up (you quitter!) - debuglog("Queue '%s' giving up on event %s", queueName, obj.event.getId()); - // remove this from the queue - this.clearQueue(queueName, err); - } - else { - setTimeout(this.processQueue, waitTimeMs, queueName); - } - }); - }; - } - /** - * Retrieve a queue based on an event. The event provided does not need to be in - * the queue. - * @param event - An event to get the queue for. - * @returns A shallow copy of events in the queue or null. - * Modifying this array will not modify the list itself. Modifying events in - * this array will modify the underlying event in the queue. - * @see MatrixScheduler.removeEventFromQueue To remove an event from the queue. - */ - getQueueForEvent(event) { - const name = this.queueAlgorithm(event); - if (!name || !this.queues[name]) { - return null; - } - return this.queues[name].map(function (obj) { - return obj.event; - }); - } - /** - * Remove this event from the queue. The event is equal to another event if they - * have the same ID returned from event.getId(). - * @param event - The event to remove. - * @returns True if this event was removed. - */ - removeEventFromQueue(event) { - const name = this.queueAlgorithm(event); - if (!name || !this.queues[name]) { - return false; - } - let removed = false; - utils.removeElement(this.queues[name], (element) => { - if (element.event.getId() === event.getId()) { - // XXX we should probably reject the promise? - // https://github.com/matrix-org/matrix-js-sdk/issues/496 - removed = true; - return true; - } - return false; - }); - return removed; - } - /** - * Set the process function. Required for events in the queue to be processed. - * If set after events have been added to the queue, this will immediately start - * processing them. - * @param fn - The function that can process events - * in the queue. - */ - setProcessFunction(fn) { - this.procFn = fn; - this.startProcessingQueues(); - } - /** - * Queue an event if it is required and start processing queues. - * @param event - The event that may be queued. - * @returns A promise if the event was queued, which will be - * resolved or rejected in due time, else null. - */ - queueEvent(event) { - const queueName = this.queueAlgorithm(event); - if (!queueName) { - return null; - } - // add the event to the queue and make a deferred for it. - if (!this.queues[queueName]) { - this.queues[queueName] = []; - } - const defer = utils.defer(); - this.queues[queueName].push({ - event: event, - defer: defer, - attempts: 0, - }); - debuglog("Queue algorithm dumped event %s into queue '%s'", event.getId(), queueName); - this.startProcessingQueues(); - return defer.promise; - } - startProcessingQueues() { - if (!this.procFn) - return; - // for each inactive queue with events in them - Object.keys(this.queues) - .filter((queueName) => { - return this.activeQueues.indexOf(queueName) === -1 && this.queues[queueName].length > 0; - }) - .forEach((queueName) => { - // mark the queue as active - this.activeQueues.push(queueName); - // begin processing the head of the queue - debuglog("Spinning up queue: '%s'", queueName); - this.processQueue(queueName); - }); - } - disableQueue(queueName) { - // queue is empty. Mark as inactive and stop recursing. - const index = this.activeQueues.indexOf(queueName); - if (index >= 0) { - this.activeQueues.splice(index, 1); - } - debuglog("Stopping queue '%s' as it is now empty", queueName); - } - clearQueue(queueName, err) { - debuglog("clearing queue '%s'", queueName); - let obj; - while ((obj = this.removeNextEvent(queueName))) { - obj.defer.reject(err); - } - this.disableQueue(queueName); - } - peekNextEvent(queueName) { - const queue = this.queues[queueName]; - if (!Array.isArray(queue)) { - return undefined; - } - return queue[0]; - } - removeNextEvent(queueName) { - const queue = this.queues[queueName]; - if (!Array.isArray(queue)) { - return undefined; - } - return queue.shift(); - } -} -exports.MatrixScheduler = MatrixScheduler; -/* istanbul ignore next */ -function debuglog(...args) { - if (DEBUG) { - logger_1.logger.log(...args); - } -} - -},{"./@types/event":306,"./http-api":367,"./logger":374,"./utils":416}],404:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021-2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); - -},{}],405:[function(require,module,exports){ -"use strict"; -/* -Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SERVICE_TYPES = void 0; -var SERVICE_TYPES; -(function (SERVICE_TYPES) { - SERVICE_TYPES["IS"] = "SERVICE_TYPE_IS"; - SERVICE_TYPES["IM"] = "SERVICE_TYPE_IM"; -})(SERVICE_TYPES = exports.SERVICE_TYPES || (exports.SERVICE_TYPES = {})); - -},{}],406:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SlidingSyncSdk = void 0; -const room_1 = require("./models/room"); -const logger_1 = require("./logger"); -const utils = __importStar(require("./utils")); -const event_timeline_1 = require("./models/event-timeline"); -const client_1 = require("./client"); -const sync_1 = require("./sync"); -const http_api_1 = require("./http-api"); -const sliding_sync_1 = require("./sliding-sync"); -const event_1 = require("./@types/event"); -const room_state_1 = require("./models/room-state"); -const room_member_1 = require("./models/room-member"); -// Number of consecutive failed syncs that will lead to a syncState of ERROR as opposed -// to RECONNECTING. This is needed to inform the client of server issues when the -// keepAlive is successful but the server /sync fails. -const FAILED_SYNC_ERROR_THRESHOLD = 3; -class ExtensionE2EE { - constructor(crypto) { - this.crypto = crypto; - } - name() { - return "e2ee"; - } - when() { - return sliding_sync_1.ExtensionState.PreProcess; - } - onRequest(isInitial) { - if (!isInitial) { - return undefined; - } - return { - enabled: true, // this is sticky so only send it on the initial request - }; - } - onResponse(data) { - return __awaiter(this, void 0, void 0, function* () { - // Handle device list updates - if (data["device_lists"]) { - yield this.crypto.handleDeviceListChanges({ - oldSyncToken: "yep", // XXX need to do this so the device list changes get processed :( - }, data["device_lists"]); - } - // Handle one_time_keys_count - if (data["device_one_time_keys_count"]) { - const currentCount = data["device_one_time_keys_count"].signed_curve25519 || 0; - this.crypto.updateOneTimeKeyCount(currentCount); - } - if (data["device_unused_fallback_key_types"] || data["org.matrix.msc2732.device_unused_fallback_key_types"]) { - // The presence of device_unused_fallback_key_types indicates that the - // server supports fallback keys. If there's no unused - // signed_curve25519 fallback key we need a new one. - const unusedFallbackKeys = data["device_unused_fallback_key_types"] || data["org.matrix.msc2732.device_unused_fallback_key_types"]; - this.crypto.setNeedsNewFallback(Array.isArray(unusedFallbackKeys) && !unusedFallbackKeys.includes("signed_curve25519")); - } - this.crypto.onSyncCompleted({}); - }); - } -} -class ExtensionToDevice { - constructor(client, cryptoCallbacks) { - this.client = client; - this.cryptoCallbacks = cryptoCallbacks; - this.nextBatch = null; - } - name() { - return "to_device"; - } - when() { - return sliding_sync_1.ExtensionState.PreProcess; - } - onRequest(isInitial) { - const extReq = { - since: this.nextBatch !== null ? this.nextBatch : undefined, - }; - if (isInitial) { - extReq["limit"] = 100; - extReq["enabled"] = true; - } - return extReq; - } - onResponse(data) { - return __awaiter(this, void 0, void 0, function* () { - const cancelledKeyVerificationTxns = []; - let events = data["events"] || []; - if (events.length > 0 && this.cryptoCallbacks) { - events = yield this.cryptoCallbacks.preprocessToDeviceMessages(events); - } - events - .map(this.client.getEventMapper()) - .map((toDeviceEvent) => { - // map is a cheap inline forEach - // We want to flag m.key.verification.start events as cancelled - // if there's an accompanying m.key.verification.cancel event, so - // we pull out the transaction IDs from the cancellation events - // so we can flag the verification events as cancelled in the loop - // below. - if (toDeviceEvent.getType() === "m.key.verification.cancel") { - const txnId = toDeviceEvent.getContent()["transaction_id"]; - if (txnId) { - cancelledKeyVerificationTxns.push(txnId); - } - } - // as mentioned above, .map is a cheap inline forEach, so return - // the unmodified event. - return toDeviceEvent; - }) - .forEach((toDeviceEvent) => { - const content = toDeviceEvent.getContent(); - if (toDeviceEvent.getType() == "m.room.message" && content.msgtype == "m.bad.encrypted") { - // the mapper already logged a warning. - logger_1.logger.log("Ignoring undecryptable to-device event from " + toDeviceEvent.getSender()); - return; - } - if (toDeviceEvent.getType() === "m.key.verification.start" || - toDeviceEvent.getType() === "m.key.verification.request") { - const txnId = content["transaction_id"]; - if (cancelledKeyVerificationTxns.includes(txnId)) { - toDeviceEvent.flagCancelled(); - } - } - this.client.emit(client_1.ClientEvent.ToDeviceEvent, toDeviceEvent); - }); - this.nextBatch = data.next_batch; - }); - } -} -class ExtensionAccountData { - constructor(client) { - this.client = client; - } - name() { - return "account_data"; - } - when() { - return sliding_sync_1.ExtensionState.PostProcess; - } - onRequest(isInitial) { - if (!isInitial) { - return undefined; - } - return { - enabled: true, - }; - } - onResponse(data) { - if (data.global && data.global.length > 0) { - this.processGlobalAccountData(data.global); - } - for (const roomId in data.rooms) { - const accountDataEvents = mapEvents(this.client, roomId, data.rooms[roomId]); - const room = this.client.getRoom(roomId); - if (!room) { - logger_1.logger.warn("got account data for room but room doesn't exist on client:", roomId); - continue; - } - room.addAccountData(accountDataEvents); - accountDataEvents.forEach((e) => { - this.client.emit(client_1.ClientEvent.Event, e); - }); - } - } - processGlobalAccountData(globalAccountData) { - const events = mapEvents(this.client, undefined, globalAccountData); - const prevEventsMap = events.reduce((m, c) => { - m[c.getType()] = this.client.store.getAccountData(c.getType()); - return m; - }, {}); - this.client.store.storeAccountDataEvents(events); - events.forEach((accountDataEvent) => { - // Honour push rules that come down the sync stream but also - // honour push rules that were previously cached. Base rules - // will be updated when we receive push rules via getPushRules - // (see sync) before syncing over the network. - if (accountDataEvent.getType() === event_1.EventType.PushRules) { - const rules = accountDataEvent.getContent(); - this.client.setPushRules(rules); - } - const prevEvent = prevEventsMap[accountDataEvent.getType()]; - this.client.emit(client_1.ClientEvent.AccountData, accountDataEvent, prevEvent); - return accountDataEvent; - }); - } -} -class ExtensionTyping { - constructor(client) { - this.client = client; - } - name() { - return "typing"; - } - when() { - return sliding_sync_1.ExtensionState.PostProcess; - } - onRequest(isInitial) { - if (!isInitial) { - return undefined; // don't send a JSON object for subsequent requests, we don't need to. - } - return { - enabled: true, - }; - } - onResponse(data) { - if (!(data === null || data === void 0 ? void 0 : data.rooms)) { - return; - } - for (const roomId in data.rooms) { - processEphemeralEvents(this.client, roomId, [data.rooms[roomId]]); - } - } -} -class ExtensionReceipts { - constructor(client) { - this.client = client; - } - name() { - return "receipts"; - } - when() { - return sliding_sync_1.ExtensionState.PostProcess; - } - onRequest(isInitial) { - if (isInitial) { - return { - enabled: true, - }; - } - return undefined; // don't send a JSON object for subsequent requests, we don't need to. - } - onResponse(data) { - if (!(data === null || data === void 0 ? void 0 : data.rooms)) { - return; - } - for (const roomId in data.rooms) { - processEphemeralEvents(this.client, roomId, [data.rooms[roomId]]); - } - } -} -/** - * A copy of SyncApi such that it can be used as a drop-in replacement for sync v2. For the actual - * sliding sync API, see sliding-sync.ts or the class SlidingSync. - */ -class SlidingSyncSdk { - constructor(slidingSync, client, opts, syncOpts) { - this.slidingSync = slidingSync; - this.client = client; - this.syncState = null; - this.lastPos = null; - this.failCount = 0; - this.notifEvents = []; // accumulator of sync events in the current sync response - this.opts = (0, sync_1.defaultClientOpts)(opts); - this.syncOpts = (0, sync_1.defaultSyncApiOpts)(syncOpts); - if (client.getNotifTimelineSet()) { - client.reEmitter.reEmit(client.getNotifTimelineSet(), [room_1.RoomEvent.Timeline, room_1.RoomEvent.TimelineReset]); - } - this.slidingSync.on(sliding_sync_1.SlidingSyncEvent.Lifecycle, this.onLifecycle.bind(this)); - this.slidingSync.on(sliding_sync_1.SlidingSyncEvent.RoomData, this.onRoomData.bind(this)); - const extensions = [ - new ExtensionToDevice(this.client, this.syncOpts.cryptoCallbacks), - new ExtensionAccountData(this.client), - new ExtensionTyping(this.client), - new ExtensionReceipts(this.client), - ]; - if (this.syncOpts.crypto) { - extensions.push(new ExtensionE2EE(this.syncOpts.crypto)); - } - extensions.forEach((ext) => { - this.slidingSync.registerExtension(ext); - }); - } - onRoomData(roomId, roomData) { - let room = this.client.store.getRoom(roomId); - if (!room) { - if (!roomData.initial) { - logger_1.logger.debug("initial flag not set but no stored room exists for room ", roomId, roomData); - return; - } - room = (0, sync_1._createAndReEmitRoom)(this.client, roomId, this.opts); - } - this.processRoomData(this.client, room, roomData); - } - onLifecycle(state, resp, err) { - if (err) { - logger_1.logger.debug("onLifecycle", state, err); - } - switch (state) { - case sliding_sync_1.SlidingSyncState.Complete: - this.purgeNotifications(); - if (!resp) { - break; - } - // Element won't stop showing the initial loading spinner unless we fire SyncState.Prepared - if (!this.lastPos) { - this.updateSyncState(sync_1.SyncState.Prepared, { - oldSyncToken: undefined, - nextSyncToken: resp.pos, - catchingUp: false, - fromCache: false, - }); - } - // Conversely, Element won't show the room list unless there is at least 1x SyncState.Syncing - // so hence for the very first sync we will fire prepared then immediately syncing. - this.updateSyncState(sync_1.SyncState.Syncing, { - oldSyncToken: this.lastPos, - nextSyncToken: resp.pos, - catchingUp: false, - fromCache: false, - }); - this.lastPos = resp.pos; - break; - case sliding_sync_1.SlidingSyncState.RequestFinished: - if (err) { - this.failCount += 1; - this.updateSyncState(this.failCount > FAILED_SYNC_ERROR_THRESHOLD ? sync_1.SyncState.Error : sync_1.SyncState.Reconnecting, { - error: new http_api_1.MatrixError(err), - }); - if (this.shouldAbortSync(new http_api_1.MatrixError(err))) { - return; // shouldAbortSync actually stops syncing too so we don't need to do anything. - } - } - else { - this.failCount = 0; - } - break; - } - } - /** - * Sync rooms the user has left. - * @returns Resolved when they've been added to the store. - */ - syncLeftRooms() { - return __awaiter(this, void 0, void 0, function* () { - return []; // TODO - }); - } - /** - * Peek into a room. This will result in the room in question being synced so it - * is accessible via getRooms(). Live updates for the room will be provided. - * @param roomId - The room ID to peek into. - * @returns A promise which resolves once the room has been added to the - * store. - */ - peek(_roomId) { - return __awaiter(this, void 0, void 0, function* () { - return null; // TODO - }); - } - /** - * Stop polling for updates in the peeked room. NOPs if there is no room being - * peeked. - */ - stopPeeking() { - // TODO - } - /** - * Returns the current state of this sync object - * @see MatrixClient#event:"sync" - */ - getSyncState() { - return this.syncState; - } - /** - * Returns the additional data object associated with - * the current sync state, or null if there is no - * such data. - * Sync errors, if available, are put in the 'error' key of - * this object. - */ - getSyncStateData() { - var _a; - return (_a = this.syncStateData) !== null && _a !== void 0 ? _a : null; - } - // Helper functions which set up JS SDK structs are below and are identical to the sync v2 counterparts - createRoom(roomId) { - // XXX cargoculted from sync.ts - const { timelineSupport } = this.client; - const room = new room_1.Room(roomId, this.client, this.client.getUserId(), { - lazyLoadMembers: this.opts.lazyLoadMembers, - pendingEventOrdering: this.opts.pendingEventOrdering, - timelineSupport, - }); - this.client.reEmitter.reEmit(room, [ - room_1.RoomEvent.Name, - room_1.RoomEvent.Redaction, - room_1.RoomEvent.RedactionCancelled, - room_1.RoomEvent.Receipt, - room_1.RoomEvent.Tags, - room_1.RoomEvent.LocalEchoUpdated, - room_1.RoomEvent.AccountData, - room_1.RoomEvent.MyMembership, - room_1.RoomEvent.Timeline, - room_1.RoomEvent.TimelineReset, - ]); - this.registerStateListeners(room); - return room; - } - registerStateListeners(room) { - // XXX cargoculted from sync.ts - // we need to also re-emit room state and room member events, so hook it up - // to the client now. We need to add a listener for RoomState.members in - // order to hook them correctly. - this.client.reEmitter.reEmit(room.currentState, [ - room_state_1.RoomStateEvent.Events, - room_state_1.RoomStateEvent.Members, - room_state_1.RoomStateEvent.NewMember, - room_state_1.RoomStateEvent.Update, - ]); - room.currentState.on(room_state_1.RoomStateEvent.NewMember, (event, state, member) => { - var _a; - member.user = (_a = this.client.getUser(member.userId)) !== null && _a !== void 0 ? _a : undefined; - this.client.reEmitter.reEmit(member, [ - room_member_1.RoomMemberEvent.Name, - room_member_1.RoomMemberEvent.Typing, - room_member_1.RoomMemberEvent.PowerLevel, - room_member_1.RoomMemberEvent.Membership, - ]); - }); - } - /* - private deregisterStateListeners(room: Room): void { // XXX cargoculted from sync.ts - // could do with a better way of achieving this. - room.currentState.removeAllListeners(RoomStateEvent.Events); - room.currentState.removeAllListeners(RoomStateEvent.Members); - room.currentState.removeAllListeners(RoomStateEvent.NewMember); - } */ - shouldAbortSync(error) { - if (error.errcode === "M_UNKNOWN_TOKEN") { - // The logout already happened, we just need to stop. - logger_1.logger.warn("Token no longer valid - assuming logout"); - this.stop(); - this.updateSyncState(sync_1.SyncState.Error, { error }); - return true; - } - return false; - } - processRoomData(client, room, roomData) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - roomData = ensureNameEvent(client, room.roomId, roomData); - const stateEvents = mapEvents(this.client, room.roomId, roomData.required_state); - // Prevent events from being decrypted ahead of time - // this helps large account to speed up faster - // room::decryptCriticalEvent is in charge of decrypting all the events - // required for a client to function properly - let timelineEvents = mapEvents(this.client, room.roomId, roomData.timeline, false); - const ephemeralEvents = []; // TODO this.mapSyncEventsFormat(joinObj.ephemeral); - // TODO: handle threaded / beacon events - if (roomData.initial) { - // we should not know about any of these timeline entries if this is a genuinely new room. - // If we do, then we've effectively done scrollback (e.g requesting timeline_limit: 1 for - // this room, then timeline_limit: 50). - const knownEvents = new Set(); - room.getLiveTimeline() - .getEvents() - .forEach((e) => { - knownEvents.add(e.getId()); - }); - // all unknown events BEFORE a known event must be scrollback e.g: - // D E <-- what we know - // A B C D E F <-- what we just received - // means: - // A B C <-- scrollback - // D E <-- dupes - // F <-- new event - // We bucket events based on if we have seen a known event yet. - const oldEvents = []; - const newEvents = []; - let seenKnownEvent = false; - for (let i = timelineEvents.length - 1; i >= 0; i--) { - const recvEvent = timelineEvents[i]; - if (knownEvents.has(recvEvent.getId())) { - seenKnownEvent = true; - continue; // don't include this event, it's a dupe - } - if (seenKnownEvent) { - // old -> new - oldEvents.push(recvEvent); - } - else { - // old -> new - newEvents.unshift(recvEvent); - } - } - timelineEvents = newEvents; - if (oldEvents.length > 0) { - // old events are scrollback, insert them now - room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline(), roomData.prev_batch); - } - } - const encrypted = this.client.isRoomEncrypted(room.roomId); - // we do this first so it's correct when any of the events fire - if (roomData.notification_count != null) { - room.setUnreadNotificationCount(room_1.NotificationCountType.Total, roomData.notification_count); - } - if (roomData.highlight_count != null) { - // We track unread notifications ourselves in encrypted rooms, so don't - // bother setting it here. We trust our calculations better than the - // server's for this case, and therefore will assume that our non-zero - // count is accurate. - if (!encrypted || (encrypted && room.getUnreadNotificationCount(room_1.NotificationCountType.Highlight) <= 0)) { - room.setUnreadNotificationCount(room_1.NotificationCountType.Highlight, roomData.highlight_count); - } - } - if (Number.isInteger(roomData.invited_count)) { - room.currentState.setInvitedMemberCount(roomData.invited_count); - } - if (Number.isInteger(roomData.joined_count)) { - room.currentState.setJoinedMemberCount(roomData.joined_count); - } - if (roomData.invite_state) { - const inviteStateEvents = mapEvents(this.client, room.roomId, roomData.invite_state); - this.injectRoomEvents(room, inviteStateEvents); - if (roomData.initial) { - room.recalculate(); - this.client.store.storeRoom(room); - this.client.emit(client_1.ClientEvent.Room, room); - } - inviteStateEvents.forEach((e) => { - this.client.emit(client_1.ClientEvent.Event, e); - }); - room.updateMyMembership("invite"); - return; - } - if (roomData.initial) { - // set the back-pagination token. Do this *before* adding any - // events so that clients can start back-paginating. - room.getLiveTimeline().setPaginationToken((_a = roomData.prev_batch) !== null && _a !== void 0 ? _a : null, event_timeline_1.EventTimeline.BACKWARDS); - } - /* TODO - else if (roomData.limited) { - - let limited = true; - - // we've got a limited sync, so we *probably* have a gap in the - // timeline, so should reset. But we might have been peeking or - // paginating and already have some of the events, in which - // case we just want to append any subsequent events to the end - // of the existing timeline. - // - // This is particularly important in the case that we already have - // *all* of the events in the timeline - in that case, if we reset - // the timeline, we'll end up with an entirely empty timeline, - // which we'll try to paginate but not get any new events (which - // will stop us linking the empty timeline into the chain). - // - for (let i = timelineEvents.length - 1; i >= 0; i--) { - const eventId = timelineEvents[i].getId(); - if (room.getTimelineForEvent(eventId)) { - logger.debug("Already have event " + eventId + " in limited " + - "sync - not resetting"); - limited = false; - - // we might still be missing some of the events before i; - // we don't want to be adding them to the end of the - // timeline because that would put them out of order. - timelineEvents.splice(0, i); - - // XXX: there's a problem here if the skipped part of the - // timeline modifies the state set in stateEvents, because - // we'll end up using the state from stateEvents rather - // than the later state from timelineEvents. We probably - // need to wind stateEvents forward over the events we're - // skipping. - break; - } - } - - if (limited) { - room.resetLiveTimeline( - roomData.prev_batch, - null, // TODO this.syncOpts.canResetEntireTimeline(room.roomId) ? null : syncEventData.oldSyncToken, - ); - - // We have to assume any gap in any timeline is - // reason to stop incrementally tracking notifications and - // reset the timeline. - this.client.resetNotifTimelineSet(); - this.registerStateListeners(room); - } - } */ - this.injectRoomEvents(room, stateEvents, timelineEvents, roomData.num_live); - // we deliberately don't add ephemeral events to the timeline - room.addEphemeralEvents(ephemeralEvents); - // local fields must be set before any async calls because call site assumes - // synchronous execution prior to emitting SlidingSyncState.Complete - room.updateMyMembership("join"); - room.recalculate(); - if (roomData.initial) { - client.store.storeRoom(room); - client.emit(client_1.ClientEvent.Room, room); - } - // check if any timeline events should bing and add them to the notifEvents array: - // we'll purge this once we've fully processed the sync response - this.addNotifications(timelineEvents); - const processRoomEvent = (e) => __awaiter(this, void 0, void 0, function* () { - client.emit(client_1.ClientEvent.Event, e); - if (e.isState() && e.getType() == event_1.EventType.RoomEncryption && this.syncOpts.cryptoCallbacks) { - yield this.syncOpts.cryptoCallbacks.onCryptoEvent(room, e); - } - }); - yield utils.promiseMapSeries(stateEvents, processRoomEvent); - yield utils.promiseMapSeries(timelineEvents, processRoomEvent); - ephemeralEvents.forEach(function (e) { - client.emit(client_1.ClientEvent.Event, e); - }); - // Decrypt only the last message in all rooms to make sure we can generate a preview - // And decrypt all events after the recorded read receipt to ensure an accurate - // notification count - room.decryptCriticalEvents(); - }); - } - /** - * Injects events into a room's model. - * @param stateEventList - A list of state events. This is the state - * at the *START* of the timeline list if it is supplied. - * @param timelineEventList - A list of timeline events. Lower index - * is earlier in time. Higher index is later. - * @param numLive - the number of events in timelineEventList which just happened, - * supplied from the server. - */ - injectRoomEvents(room, stateEventList, timelineEventList, numLive) { - timelineEventList = timelineEventList || []; - stateEventList = stateEventList || []; - numLive = numLive || 0; - // If there are no events in the timeline yet, initialise it with - // the given state events - const liveTimeline = room.getLiveTimeline(); - const timelineWasEmpty = liveTimeline.getEvents().length == 0; - if (timelineWasEmpty) { - // Passing these events into initialiseState will freeze them, so we need - // to compute and cache the push actions for them now, otherwise sync dies - // with an attempt to assign to read only property. - // XXX: This is pretty horrible and is assuming all sorts of behaviour from - // these functions that it shouldn't be. We should probably either store the - // push actions cache elsewhere so we can freeze MatrixEvents, or otherwise - // find some solution where MatrixEvents are immutable but allow for a cache - // field. - for (const ev of stateEventList) { - this.client.getPushActionsForEvent(ev); - } - liveTimeline.initialiseState(stateEventList); - } - // If the timeline wasn't empty, we process the state events here: they're - // defined as updates to the state before the start of the timeline, so this - // starts to roll the state forward. - // XXX: That's what we *should* do, but this can happen if we were previously - // peeking in a room, in which case we obviously do *not* want to add the - // state events here onto the end of the timeline. Historically, the js-sdk - // has just set these new state events on the old and new state. This seems - // very wrong because there could be events in the timeline that diverge the - // state, in which case this is going to leave things out of sync. However, - // for now I think it;s best to behave the same as the code has done previously. - if (!timelineWasEmpty) { - // XXX: As above, don't do this... - //room.addLiveEvents(stateEventList || []); - // Do this instead... - room.oldState.setStateEvents(stateEventList); - room.currentState.setStateEvents(stateEventList); - } - // the timeline is broken into 'live' events which just happened and normal timeline events - // which are still to be appended to the end of the live timeline but happened a while ago. - // The live events are marked as fromCache=false to ensure that downstream components know - // this is a live event, not historical (from a remote server cache). - let liveTimelineEvents = []; - if (numLive > 0) { - // last numLive events are live - liveTimelineEvents = timelineEventList.slice(-1 * numLive); - // everything else is not live - timelineEventList = timelineEventList.slice(0, -1 * liveTimelineEvents.length); - } - // execute the timeline events. This will continue to diverge the current state - // if the timeline has any state events in it. - // This also needs to be done before running push rules on the events as they need - // to be decorated with sender etc. - room.addLiveEvents(timelineEventList, { - fromCache: true, - }); - if (liveTimelineEvents.length > 0) { - room.addLiveEvents(liveTimelineEvents, { - fromCache: false, - }); - } - room.recalculate(); - // resolve invites now we have set the latest state - this.resolveInvites(room); - } - resolveInvites(room) { - if (!room || !this.opts.resolveInvitesToProfiles) { - return; - } - const client = this.client; - // For each invited room member we want to give them a displayname/avatar url - // if they have one (the m.room.member invites don't contain this). - room.getMembersWithMembership("invite").forEach(function (member) { - if (member.requestedProfileInfo) - return; - member.requestedProfileInfo = true; - // try to get a cached copy first. - const user = client.getUser(member.userId); - let promise; - if (user) { - promise = Promise.resolve({ - avatar_url: user.avatarUrl, - displayname: user.displayName, - }); - } - else { - promise = client.getProfileInfo(member.userId); - } - promise.then(function (info) { - // slightly naughty by doctoring the invite event but this means all - // the code paths remain the same between invite/join display name stuff - // which is a worthy trade-off for some minor pollution. - const inviteEvent = member.events.member; - if (inviteEvent.getContent().membership !== "invite") { - // between resolving and now they have since joined, so don't clobber - return; - } - inviteEvent.getContent().avatar_url = info.avatar_url; - inviteEvent.getContent().displayname = info.displayname; - // fire listeners - member.setMembershipEvent(inviteEvent, room.currentState); - }, function (_err) { - // OH WELL. - }); - }); - } - retryImmediately() { - return true; - } - /** - * Main entry point. Blocks until stop() is called. - */ - sync() { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.debug("Sliding sync init loop"); - // 1) We need to get push rules so we can check if events should bing as we get - // them from /sync. - while (!this.client.isGuest()) { - try { - logger_1.logger.debug("Getting push rules..."); - const result = yield this.client.getPushRules(); - logger_1.logger.debug("Got push rules"); - this.client.pushRules = result; - break; - } - catch (err) { - logger_1.logger.error("Getting push rules failed", err); - if (this.shouldAbortSync(err)) { - return; - } - } - } - // start syncing - yield this.slidingSync.start(); - }); - } - /** - * Stops the sync object from syncing. - */ - stop() { - logger_1.logger.debug("SyncApi.stop"); - this.slidingSync.stop(); - } - /** - * Sets the sync state and emits an event to say so - * @param newState - The new state string - * @param data - Object of additional data to emit in the event - */ - updateSyncState(newState, data) { - const old = this.syncState; - this.syncState = newState; - this.syncStateData = data; - this.client.emit(client_1.ClientEvent.Sync, this.syncState, old, data); - } - /** - * Takes a list of timelineEvents and adds and adds to notifEvents - * as appropriate. - * This must be called after the room the events belong to has been stored. - * - * @param timelineEventList - A list of timeline events. Lower index - * is earlier in time. Higher index is later. - */ - addNotifications(timelineEventList) { - // gather our notifications into this.notifEvents - if (!this.client.getNotifTimelineSet()) { - return; - } - for (const timelineEvent of timelineEventList) { - const pushActions = this.client.getPushActionsForEvent(timelineEvent); - if (pushActions && pushActions.notify && pushActions.tweaks && pushActions.tweaks.highlight) { - this.notifEvents.push(timelineEvent); - } - } - } - /** - * Purge any events in the notifEvents array. Used after a /sync has been complete. - * This should not be called at a per-room scope (e.g in onRoomData) because otherwise the ordering - * will be messed up e.g room A gets a bing, room B gets a newer bing, but both in the same /sync - * response. If we purge at a per-room scope then we could process room B before room A leading to - * room B appearing earlier in the notifications timeline, even though it has the higher origin_server_ts. - */ - purgeNotifications() { - this.notifEvents.sort(function (a, b) { - return a.getTs() - b.getTs(); - }); - this.notifEvents.forEach((event) => { - var _a; - (_a = this.client.getNotifTimelineSet()) === null || _a === void 0 ? void 0 : _a.addLiveEvent(event); - }); - this.notifEvents = []; - } -} -exports.SlidingSyncSdk = SlidingSyncSdk; -function ensureNameEvent(client, roomId, roomData) { - // make sure m.room.name is in required_state if there is a name, replacing anything previously - // there if need be. This ensures clients transparently 'calculate' the right room name. Native - // sliding sync clients should just read the "name" field. - if (!roomData.name) { - return roomData; - } - for (const stateEvent of roomData.required_state) { - if (stateEvent.type === event_1.EventType.RoomName && stateEvent.state_key === "") { - stateEvent.content = { - name: roomData.name, - }; - return roomData; - } - } - roomData.required_state.push({ - event_id: "$fake-sliding-sync-name-event-" + roomId, - state_key: "", - type: event_1.EventType.RoomName, - content: { - name: roomData.name, - }, - sender: client.getUserId(), - origin_server_ts: new Date().getTime(), - }); - return roomData; -} -// Helper functions which set up JS SDK structs are below and are identical to the sync v2 counterparts, -// just outside the class. -function mapEvents(client, roomId, events, decrypt = true) { - const mapper = client.getEventMapper({ decrypt }); - return events.map(function (e) { - e.room_id = roomId; - return mapper(e); - }); -} -function processEphemeralEvents(client, roomId, ephEvents) { - const ephemeralEvents = mapEvents(client, roomId, ephEvents); - const room = client.getRoom(roomId); - if (!room) { - logger_1.logger.warn("got ephemeral events for room but room doesn't exist on client:", roomId); - return; - } - room.addEphemeralEvents(ephemeralEvents); - ephemeralEvents.forEach((e) => { - client.emit(client_1.ClientEvent.Event, e); - }); -} - -},{"./@types/event":306,"./client":321,"./http-api":367,"./logger":374,"./models/event-timeline":382,"./models/room":392,"./models/room-member":389,"./models/room-state":390,"./sliding-sync":407,"./sync":414,"./utils":416}],407:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SlidingSync = exports.SlidingSyncEvent = exports.ExtensionState = exports.SlidingSyncState = exports.MSC3575_STATE_KEY_LAZY = exports.MSC3575_STATE_KEY_ME = exports.MSC3575_WILDCARD = void 0; -const logger_1 = require("./logger"); -const typed_event_emitter_1 = require("./models/typed-event-emitter"); -const utils_1 = require("./utils"); -// /sync requests allow you to set a timeout= but the request may continue -// beyond that and wedge forever, so we need to track how long we are willing -// to keep open the connection. This constant is *ADDED* to the timeout= value -// to determine the max time we're willing to wait. -const BUFFER_PERIOD_MS = 10 * 1000; -exports.MSC3575_WILDCARD = "*"; -exports.MSC3575_STATE_KEY_ME = "$ME"; -exports.MSC3575_STATE_KEY_LAZY = "$LAZY"; -var SlidingSyncState; -(function (SlidingSyncState) { - /** - * Fired by SlidingSyncEvent.Lifecycle event immediately before processing the response. - */ - SlidingSyncState["RequestFinished"] = "FINISHED"; - /** - * Fired by SlidingSyncEvent.Lifecycle event immediately after all room data listeners have been - * invoked, but before list listeners. - */ - SlidingSyncState["Complete"] = "COMPLETE"; -})(SlidingSyncState = exports.SlidingSyncState || (exports.SlidingSyncState = {})); -/** - * Internal Class. SlidingList represents a single list in sliding sync. The list can have filters, - * multiple sliding windows, and maintains the index-\>room_id mapping. - */ -class SlidingList { - /** - * Construct a new sliding list. - * @param list - The range, sort and filter values to use for this list. - */ - constructor(list) { - // returned data - this.roomIndexToRoomId = {}; - this.joinedCount = 0; - this.replaceList(list); - } - /** - * Mark this list as modified or not. Modified lists will return sticky params with calls to getList. - * This is useful for the first time the list is sent, or if the list has changed in some way. - * @param modified - True to mark this list as modified so all sticky parameters will be re-sent. - */ - setModified(modified) { - this.isModified = modified; - } - /** - * Update the list range for this list. Does not affect modified status as list ranges are non-sticky. - * @param newRanges - The new ranges for the list - */ - updateListRange(newRanges) { - this.list.ranges = JSON.parse(JSON.stringify(newRanges)); - } - /** - * Replace list parameters. All fields will be replaced with the new list parameters. - * @param list - The new list parameters - */ - replaceList(list) { - list.filters = list.filters || {}; - list.ranges = list.ranges || []; - this.list = JSON.parse(JSON.stringify(list)); - this.isModified = true; - // reset values as the join count may be very different (if filters changed) including the rooms - // (e.g. sort orders or sliding window ranges changed) - // the constantly changing sliding window ranges. Not an array for performance reasons - // E.g. tracking ranges 0-99, 500-599, we don't want to have a 600 element array - this.roomIndexToRoomId = {}; - // the total number of joined rooms according to the server, always >= len(roomIndexToRoomId) - this.joinedCount = 0; - } - /** - * Return a copy of the list suitable for a request body. - * @param forceIncludeAllParams - True to forcibly include all params even if the list - * hasn't been modified. Callers may want to do this if they are modifying the list prior to calling - * updateList. - */ - getList(forceIncludeAllParams) { - let list = { - ranges: JSON.parse(JSON.stringify(this.list.ranges)), - }; - if (this.isModified || forceIncludeAllParams) { - list = JSON.parse(JSON.stringify(this.list)); - } - return list; - } - /** - * Check if a given index is within the list range. This is required even though the /sync API - * provides explicit updates with index positions because of the following situation: - * 0 1 2 3 4 5 6 7 8 indexes - * a b c d e f COMMANDS: SYNC 0 2 a b c; SYNC 6 8 d e f; - * a b c d _ f COMMAND: DELETE 7; - * e a b c d f COMMAND: INSERT 0 e; - * c=3 is wrong as we are not tracking it, ergo we need to see if `i` is in range else drop it - * @param i - The index to check - * @returns True if the index is within a sliding window - */ - isIndexInRange(i) { - for (const r of this.list.ranges) { - if (r[0] <= i && i <= r[1]) { - return true; - } - } - return false; - } -} -/** - * When onResponse extensions should be invoked: before or after processing the main response. - */ -var ExtensionState; -(function (ExtensionState) { - // Call onResponse before processing the response body. This is useful when your extension is - // preparing the ground for the response body e.g. processing to-device messages before the - // encrypted event arrives. - ExtensionState["PreProcess"] = "ExtState.PreProcess"; - // Call onResponse after processing the response body. This is useful when your extension is - // decorating data from the client, and you rely on MatrixClient.getRoom returning the Room object - // e.g. room account data. - ExtensionState["PostProcess"] = "ExtState.PostProcess"; -})(ExtensionState = exports.ExtensionState || (exports.ExtensionState = {})); -/** - * Events which can be fired by the SlidingSync class. These are designed to provide different levels - * of information when processing sync responses. - * - RoomData: concerns rooms, useful for SlidingSyncSdk to update its knowledge of rooms. - * - Lifecycle: concerns callbacks at various well-defined points in the sync process. - * - List: concerns lists, useful for UI layers to re-render room lists. - * Specifically, the order of event invocation is: - * - Lifecycle (state=RequestFinished) - * - RoomData (N times) - * - Lifecycle (state=Complete) - * - List (at most once per list) - */ -var SlidingSyncEvent; -(function (SlidingSyncEvent) { - /** - * This event fires when there are updates for a room. Fired as and when rooms are encountered - * in the response. - */ - SlidingSyncEvent["RoomData"] = "SlidingSync.RoomData"; - /** - * This event fires at various points in the /sync loop lifecycle. - * - SlidingSyncState.RequestFinished: Fires after we receive a valid response but before the - * response has been processed. Perform any pre-process steps here. If there was a problem syncing, - * `err` will be set (e.g network errors). - * - SlidingSyncState.Complete: Fires after all SlidingSyncEvent.RoomData have been fired but before - * SlidingSyncEvent.List. - */ - SlidingSyncEvent["Lifecycle"] = "SlidingSync.Lifecycle"; - /** - * This event fires whenever there has been a change to this list index. It fires exactly once - * per list, even if there were multiple operations for the list. - * It fires AFTER Lifecycle and RoomData events. - */ - SlidingSyncEvent["List"] = "SlidingSync.List"; -})(SlidingSyncEvent = exports.SlidingSyncEvent || (exports.SlidingSyncEvent = {})); -/** - * SlidingSync is a high-level data structure which controls the majority of sliding sync. - * It has no hooks into JS SDK except for needing a MatrixClient to perform the HTTP request. - * This means this class (and everything it uses) can be used in isolation from JS SDK if needed. - * To hook this up with the JS SDK, you need to use SlidingSyncSdk. - */ -class SlidingSync extends typed_event_emitter_1.TypedEventEmitter { - /** - * Create a new sliding sync instance - * @param proxyBaseUrl - The base URL of the sliding sync proxy - * @param lists - The lists to use for sliding sync. - * @param roomSubscriptionInfo - The params to use for room subscriptions. - * @param client - The client to use for /sync calls. - * @param timeoutMS - The number of milliseconds to wait for a response. - */ - constructor(proxyBaseUrl, lists, roomSubscriptionInfo, client, timeoutMS) { - super(); - this.proxyBaseUrl = proxyBaseUrl; - this.roomSubscriptionInfo = roomSubscriptionInfo; - this.client = client; - this.timeoutMS = timeoutMS; - this.listModifiedCount = 0; - this.terminated = false; - // flag set when resend() is called because we cannot rely on detecting AbortError in JS SDK :( - this.needsResend = false; - // the txn_id to send with the next request. - this.txnId = null; - // a list (in chronological order of when they were sent) of objects containing the txn ID and - // a defer to resolve/reject depending on whether they were successfully sent or not. - this.txnIdDefers = []; - // map of extension name to req/resp handler - this.extensions = {}; - this.desiredRoomSubscriptions = new Set(); // the *desired* room subscriptions - this.confirmedRoomSubscriptions = new Set(); - // map of custom subscription name to the subscription - this.customSubscriptions = new Map(); - // map of room ID to custom subscription name - this.roomIdToCustomSubscription = new Map(); - this.lists = new Map(); - lists.forEach((list, key) => { - this.lists.set(key, new SlidingList(list)); - }); - } - /** - * Add a custom room subscription, referred to by an arbitrary name. If a subscription with this - * name already exists, it is replaced. No requests are sent by calling this method. - * @param name - The name of the subscription. Only used to reference this subscription in - * useCustomSubscription. - * @param sub - The subscription information. - */ - addCustomSubscription(name, sub) { - if (this.customSubscriptions.has(name)) { - logger_1.logger.warn(`addCustomSubscription: ${name} already exists as a custom subscription, ignoring.`); - return; - } - this.customSubscriptions.set(name, sub); - } - /** - * Use a custom subscription previously added via addCustomSubscription. No requests are sent - * by calling this method. Use modifyRoomSubscriptions to resend subscription information. - * @param roomId - The room to use the subscription in. - * @param name - The name of the subscription. If this name is unknown, the default subscription - * will be used. - */ - useCustomSubscription(roomId, name) { - // We already know about this custom subscription, as it is immutable, - // we don't need to unconfirm the subscription. - if (this.roomIdToCustomSubscription.get(roomId) === name) { - return; - } - this.roomIdToCustomSubscription.set(roomId, name); - // unconfirm this subscription so a resend() will send it up afresh. - this.confirmedRoomSubscriptions.delete(roomId); - } - /** - * Get the room index data for a list. - * @param key - The list key - * @returns The list data which contains the rooms in this list - */ - getListData(key) { - const data = this.lists.get(key); - if (!data) { - return null; - } - return { - joinedCount: data.joinedCount, - roomIndexToRoomId: Object.assign({}, data.roomIndexToRoomId), - }; - } - /** - * Get the full request list parameters for a list index. This function is provided for callers to use - * in conjunction with setList to update fields on an existing list. - * @param key - The list key to get the params for. - * @returns A copy of the list params or undefined. - */ - getListParams(key) { - const params = this.lists.get(key); - if (!params) { - return null; - } - return params.getList(true); - } - /** - * Set new ranges for an existing list. Calling this function when _only_ the ranges have changed - * is more efficient than calling setList(index,list) as this function won't resend sticky params, - * whereas setList always will. - * @param key - The list key to modify - * @param ranges - The new ranges to apply. - * @returns A promise which resolves to the transaction ID when it has been received down sync - * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled - * immediately after sending, in which case the action will be applied in the subsequent request) - */ - setListRanges(key, ranges) { - const list = this.lists.get(key); - if (!list) { - return Promise.reject(new Error("no list with key " + key)); - } - list.updateListRange(ranges); - return this.resend(); - } - /** - * Add or replace a list. Calling this function will interrupt the /sync request to resend new - * lists. - * @param key - The key to modify - * @param list - The new list parameters. - * @returns A promise which resolves to the transaction ID when it has been received down sync - * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled - * immediately after sending, in which case the action will be applied in the subsequent request) - */ - setList(key, list) { - const existingList = this.lists.get(key); - if (existingList) { - existingList.replaceList(list); - this.lists.set(key, existingList); - } - else { - this.lists.set(key, new SlidingList(list)); - } - this.listModifiedCount += 1; - return this.resend(); - } - /** - * Get the room subscriptions for the sync API. - * @returns A copy of the desired room subscriptions. - */ - getRoomSubscriptions() { - return new Set(Array.from(this.desiredRoomSubscriptions)); - } - /** - * Modify the room subscriptions for the sync API. Calling this function will interrupt the - * /sync request to resend new subscriptions. If the /sync stream has not started, this will - * prepare the room subscriptions for when start() is called. - * @param s - The new desired room subscriptions. - * @returns A promise which resolves to the transaction ID when it has been received down sync - * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled - * immediately after sending, in which case the action will be applied in the subsequent request) - */ - modifyRoomSubscriptions(s) { - this.desiredRoomSubscriptions = s; - return this.resend(); - } - /** - * Modify which events to retrieve for room subscriptions. Invalidates all room subscriptions - * such that they will be sent up afresh. - * @param rs - The new room subscription fields to fetch. - * @returns A promise which resolves to the transaction ID when it has been received down sync - * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled - * immediately after sending, in which case the action will be applied in the subsequent request) - */ - modifyRoomSubscriptionInfo(rs) { - this.roomSubscriptionInfo = rs; - this.confirmedRoomSubscriptions = new Set(); - return this.resend(); - } - /** - * Register an extension to send with the /sync request. - * @param ext - The extension to register. - */ - registerExtension(ext) { - if (this.extensions[ext.name()]) { - throw new Error(`registerExtension: ${ext.name()} already exists as an extension`); - } - this.extensions[ext.name()] = ext; - } - getExtensionRequest(isInitial) { - const ext = {}; - Object.keys(this.extensions).forEach((extName) => { - ext[extName] = this.extensions[extName].onRequest(isInitial); - }); - return ext; - } - onPreExtensionsResponse(ext) { - Object.keys(ext).forEach((extName) => { - if (this.extensions[extName].when() == ExtensionState.PreProcess) { - this.extensions[extName].onResponse(ext[extName]); - } - }); - } - onPostExtensionsResponse(ext) { - Object.keys(ext).forEach((extName) => { - if (this.extensions[extName].when() == ExtensionState.PostProcess) { - this.extensions[extName].onResponse(ext[extName]); - } - }); - } - /** - * Invoke all attached room data listeners. - * @param roomId - The room which received some data. - * @param roomData - The raw sliding sync response JSON. - */ - invokeRoomDataListeners(roomId, roomData) { - if (!roomData.required_state) { - roomData.required_state = []; - } - if (!roomData.timeline) { - roomData.timeline = []; - } - this.emit(SlidingSyncEvent.RoomData, roomId, roomData); - } - /** - * Invoke all attached lifecycle listeners. - * @param state - The Lifecycle state - * @param resp - The raw sync response JSON - * @param err - Any error that occurred when making the request e.g. network errors. - */ - invokeLifecycleListeners(state, resp, err) { - this.emit(SlidingSyncEvent.Lifecycle, state, resp, err); - } - shiftRight(listKey, hi, low) { - const list = this.lists.get(listKey); - if (!list) { - return; - } - // l h - // 0,1,2,3,4 <- before - // 0,1,2,2,3 <- after, hi is deleted and low is duplicated - for (let i = hi; i > low; i--) { - if (list.isIndexInRange(i)) { - list.roomIndexToRoomId[i] = list.roomIndexToRoomId[i - 1]; - } - } - } - shiftLeft(listKey, hi, low) { - const list = this.lists.get(listKey); - if (!list) { - return; - } - // l h - // 0,1,2,3,4 <- before - // 0,1,3,4,4 <- after, low is deleted and hi is duplicated - for (let i = low; i < hi; i++) { - if (list.isIndexInRange(i)) { - list.roomIndexToRoomId[i] = list.roomIndexToRoomId[i + 1]; - } - } - } - removeEntry(listKey, index) { - const list = this.lists.get(listKey); - if (!list) { - return; - } - // work out the max index - let max = -1; - for (const n in list.roomIndexToRoomId) { - if (Number(n) > max) { - max = Number(n); - } - } - if (max < 0 || index > max) { - return; - } - // Everything higher than the gap needs to be shifted left. - this.shiftLeft(listKey, max, index); - delete list.roomIndexToRoomId[max]; - } - addEntry(listKey, index) { - const list = this.lists.get(listKey); - if (!list) { - return; - } - // work out the max index - let max = -1; - for (const n in list.roomIndexToRoomId) { - if (Number(n) > max) { - max = Number(n); - } - } - if (max < 0 || index > max) { - return; - } - // Everything higher than the gap needs to be shifted right, +1 so we don't delete the highest element - this.shiftRight(listKey, max + 1, index); - } - processListOps(list, listKey) { - let gapIndex = -1; - const listData = this.lists.get(listKey); - if (!listData) { - return; - } - list.ops.forEach((op) => { - if (!listData) { - return; - } - switch (op.op) { - case "DELETE": { - logger_1.logger.debug("DELETE", listKey, op.index, ";"); - delete listData.roomIndexToRoomId[op.index]; - if (gapIndex !== -1) { - // we already have a DELETE operation to process, so process it. - this.removeEntry(listKey, gapIndex); - } - gapIndex = op.index; - break; - } - case "INSERT": { - logger_1.logger.debug("INSERT", listKey, op.index, op.room_id, ";"); - if (listData.roomIndexToRoomId[op.index]) { - // something is in this space, shift items out of the way - if (gapIndex < 0) { - // we haven't been told where to shift from, so make way for a new room entry. - this.addEntry(listKey, op.index); - } - else if (gapIndex > op.index) { - // the gap is further down the list, shift every element to the right - // starting at the gap so we can just shift each element in turn: - // [A,B,C,_] gapIndex=3, op.index=0 - // [A,B,C,C] i=3 - // [A,B,B,C] i=2 - // [A,A,B,C] i=1 - // Terminate. We'll assign into op.index next. - this.shiftRight(listKey, gapIndex, op.index); - } - else if (gapIndex < op.index) { - // the gap is further up the list, shift every element to the left - // starting at the gap so we can just shift each element in turn - this.shiftLeft(listKey, op.index, gapIndex); - } - } - // forget the gap, we don't need it anymore. This is outside the check for - // a room being present in this index position because INSERTs always universally - // forget the gap, not conditionally based on the presence of a room in the INSERT - // position. Without this, DELETE 0; INSERT 0; would do the wrong thing. - gapIndex = -1; - listData.roomIndexToRoomId[op.index] = op.room_id; - break; - } - case "INVALIDATE": { - const startIndex = op.range[0]; - for (let i = startIndex; i <= op.range[1]; i++) { - delete listData.roomIndexToRoomId[i]; - } - logger_1.logger.debug("INVALIDATE", listKey, op.range[0], op.range[1], ";"); - break; - } - case "SYNC": { - const startIndex = op.range[0]; - for (let i = startIndex; i <= op.range[1]; i++) { - const roomId = op.room_ids[i - startIndex]; - if (!roomId) { - break; // we are at the end of list - } - listData.roomIndexToRoomId[i] = roomId; - } - logger_1.logger.debug("SYNC", listKey, op.range[0], op.range[1], (op.room_ids || []).join(" "), ";"); - break; - } - } - }); - if (gapIndex !== -1) { - // we already have a DELETE operation to process, so process it - // Everything higher than the gap needs to be shifted left. - this.removeEntry(listKey, gapIndex); - } - } - /** - * Resend a Sliding Sync request. Used when something has changed in the request. Resolves with - * the transaction ID of this request on success. Rejects with the transaction ID of this request - * on failure. - */ - resend() { - var _a; - if (this.needsResend && this.txnIdDefers.length > 0) { - // we already have a resend queued, so just return the same promise - return this.txnIdDefers[this.txnIdDefers.length - 1].promise; - } - this.needsResend = true; - this.txnId = this.client.makeTxnId(); - const d = (0, utils_1.defer)(); - this.txnIdDefers.push(Object.assign(Object.assign({}, d), { txnId: this.txnId })); - (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort(); - this.abortController = new AbortController(); - return d.promise; - } - resolveTransactionDefers(txnId) { - if (!txnId) { - return; - } - // find the matching index - let txnIndex = -1; - for (let i = 0; i < this.txnIdDefers.length; i++) { - if (this.txnIdDefers[i].txnId === txnId) { - txnIndex = i; - break; - } - } - if (txnIndex === -1) { - // this shouldn't happen; we shouldn't be seeing txn_ids for things we don't know about, - // whine about it. - logger_1.logger.warn(`resolveTransactionDefers: seen ${txnId} but it isn't a pending txn, ignoring.`); - return; - } - // This list is sorted in time, so if the input txnId ACKs in the middle of this array, - // then everything before it that hasn't been ACKed yet never will and we should reject them. - for (let i = 0; i < txnIndex; i++) { - this.txnIdDefers[i].reject(this.txnIdDefers[i].txnId); - } - this.txnIdDefers[txnIndex].resolve(txnId); - // clear out settled promises, including the one we resolved. - this.txnIdDefers = this.txnIdDefers.slice(txnIndex + 1); - } - /** - * Stop syncing with the server. - */ - stop() { - var _a; - this.terminated = true; - (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort(); - // remove all listeners so things can be GC'd - this.removeAllListeners(SlidingSyncEvent.Lifecycle); - this.removeAllListeners(SlidingSyncEvent.List); - this.removeAllListeners(SlidingSyncEvent.RoomData); - } - /** - * Re-setup this connection e.g in the event of an expired session. - */ - resetup() { - var _a; - logger_1.logger.warn("SlidingSync: resetting connection info"); - // any pending txn ID defers will be forgotten already by the server, so clear them out - this.txnIdDefers.forEach((d) => { - d.reject(d.txnId); - }); - this.txnIdDefers = []; - // resend sticky params and de-confirm all subscriptions - this.lists.forEach((l) => { - l.setModified(true); - }); - this.confirmedRoomSubscriptions = new Set(); // leave desired ones alone though! - // reset the connection as we might be wedged - this.needsResend = true; - (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort(); - this.abortController = new AbortController(); - } - /** - * Start syncing with the server. Blocks until stopped. - */ - start() { - return __awaiter(this, void 0, void 0, function* () { - this.abortController = new AbortController(); - let currentPos; - while (!this.terminated) { - this.needsResend = false; - let doNotUpdateList = false; - let resp; - try { - const listModifiedCount = this.listModifiedCount; - const reqLists = {}; - this.lists.forEach((l, key) => { - reqLists[key] = l.getList(false); - }); - const reqBody = { - lists: reqLists, - pos: currentPos, - timeout: this.timeoutMS, - clientTimeout: this.timeoutMS + BUFFER_PERIOD_MS, - extensions: this.getExtensionRequest(currentPos === undefined), - }; - // check if we are (un)subscribing to a room and modify request this one time for it - const newSubscriptions = difference(this.desiredRoomSubscriptions, this.confirmedRoomSubscriptions); - const unsubscriptions = difference(this.confirmedRoomSubscriptions, this.desiredRoomSubscriptions); - if (unsubscriptions.size > 0) { - reqBody.unsubscribe_rooms = Array.from(unsubscriptions); - } - if (newSubscriptions.size > 0) { - reqBody.room_subscriptions = {}; - for (const roomId of newSubscriptions) { - const customSubName = this.roomIdToCustomSubscription.get(roomId); - let sub = this.roomSubscriptionInfo; - if (customSubName && this.customSubscriptions.has(customSubName)) { - sub = this.customSubscriptions.get(customSubName); - } - reqBody.room_subscriptions[roomId] = sub; - } - } - if (this.txnId) { - reqBody.txn_id = this.txnId; - this.txnId = null; - } - this.pendingReq = this.client.slidingSync(reqBody, this.proxyBaseUrl, this.abortController.signal); - resp = yield this.pendingReq; - currentPos = resp.pos; - // update what we think we're subscribed to. - for (const roomId of newSubscriptions) { - this.confirmedRoomSubscriptions.add(roomId); - } - for (const roomId of unsubscriptions) { - this.confirmedRoomSubscriptions.delete(roomId); - } - if (listModifiedCount !== this.listModifiedCount) { - // the lists have been modified whilst we were waiting for 'await' to return, but the abort() - // call did nothing. It is NOT SAFE to modify the list array now. We'll process the response but - // not update list pointers. - logger_1.logger.debug("list modified during await call, not updating list"); - doNotUpdateList = true; - } - // mark all these lists as having been sent as sticky so we don't keep sending sticky params - this.lists.forEach((l) => { - l.setModified(false); - }); - // set default empty values so we don't need to null check - resp.lists = resp.lists || {}; - resp.rooms = resp.rooms || {}; - resp.extensions = resp.extensions || {}; - Object.keys(resp.lists).forEach((key) => { - const list = this.lists.get(key); - if (!list || !resp) { - return; - } - list.joinedCount = resp.lists[key].count; - }); - this.invokeLifecycleListeners(SlidingSyncState.RequestFinished, resp); - } - catch (err) { - if (err.httpStatus) { - this.invokeLifecycleListeners(SlidingSyncState.RequestFinished, null, err); - if (err.httpStatus === 400) { - // session probably expired TODO: assign an errcode - // so drop state and re-request - this.resetup(); - currentPos = undefined; - yield (0, utils_1.sleep)(50); // in case the 400 was for something else; don't tightloop - continue; - } // else fallthrough to generic error handling - } - else if (this.needsResend || err.name === "AbortError") { - continue; // don't sleep as we caused this error by abort()ing the request. - } - logger_1.logger.error(err); - yield (0, utils_1.sleep)(5000); - } - if (!resp) { - continue; - } - this.onPreExtensionsResponse(resp.extensions); - Object.keys(resp.rooms).forEach((roomId) => { - this.invokeRoomDataListeners(roomId, resp.rooms[roomId]); - }); - const listKeysWithUpdates = new Set(); - if (!doNotUpdateList) { - for (const [key, list] of Object.entries(resp.lists)) { - list.ops = list.ops || []; - if (list.ops.length > 0) { - listKeysWithUpdates.add(key); - } - this.processListOps(list, key); - } - } - this.invokeLifecycleListeners(SlidingSyncState.Complete, resp); - this.onPostExtensionsResponse(resp.extensions); - listKeysWithUpdates.forEach((listKey) => { - const list = this.lists.get(listKey); - if (!list) { - return; - } - this.emit(SlidingSyncEvent.List, listKey, list.joinedCount, Object.assign({}, list.roomIndexToRoomId)); - }); - this.resolveTransactionDefers(resp.txn_id); - } - }); - } -} -exports.SlidingSync = SlidingSync; -const difference = (setA, setB) => { - const diff = new Set(setA); - for (const elem of setB) { - diff.delete(elem); - } - return diff; -}; - -},{"./logger":374,"./models/typed-event-emitter":395,"./utils":416}],408:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LocalIndexedDBStoreBackend = void 0; -const sync_accumulator_1 = require("../sync-accumulator"); -const utils = __importStar(require("../utils")); -const IndexedDBHelpers = __importStar(require("../indexeddb-helpers")); -const logger_1 = require("../logger"); -const DB_MIGRATIONS = [ - (db) => { - // Make user store, clobber based on user ID. (userId property of User objects) - db.createObjectStore("users", { keyPath: ["userId"] }); - // Make account data store, clobber based on event type. - // (event.type property of MatrixEvent objects) - db.createObjectStore("accountData", { keyPath: ["type"] }); - // Make /sync store (sync tokens, room data, etc), always clobber (const key). - db.createObjectStore("sync", { keyPath: ["clobber"] }); - }, - (db) => { - const oobMembersStore = db.createObjectStore("oob_membership_events", { - keyPath: ["room_id", "state_key"], - }); - oobMembersStore.createIndex("room", "room_id"); - }, - (db) => { - db.createObjectStore("client_options", { keyPath: ["clobber"] }); - }, - (db) => { - db.createObjectStore("to_device_queue", { autoIncrement: true }); - }, - // Expand as needed. -]; -const VERSION = DB_MIGRATIONS.length; -/** - * Helper method to collect results from a Cursor and promiseify it. - * @param store - The store to perform openCursor on. - * @param keyRange - Optional key range to apply on the cursor. - * @param resultMapper - A function which is repeatedly called with a - * Cursor. - * Return the data you want to keep. - * @returns Promise which resolves to an array of whatever you returned from - * resultMapper. - */ -function selectQuery(store, keyRange, resultMapper) { - const query = store.openCursor(keyRange); - return new Promise((resolve, reject) => { - const results = []; - query.onerror = () => { - reject(new Error("Query failed: " + query.error)); - }; - // collect results - query.onsuccess = () => { - const cursor = query.result; - if (!cursor) { - resolve(results); - return; // end of results - } - results.push(resultMapper(cursor)); - cursor.continue(); - }; - }); -} -function txnAsPromise(txn) { - return new Promise((resolve, reject) => { - txn.oncomplete = function (event) { - resolve(event); - }; - txn.onerror = function () { - reject(txn.error); - }; - }); -} -function reqAsEventPromise(req) { - return new Promise((resolve, reject) => { - req.onsuccess = function (event) { - resolve(event); - }; - req.onerror = function () { - reject(req.error); - }; - }); -} -function reqAsPromise(req) { - return new Promise((resolve, reject) => { - req.onsuccess = () => resolve(req); - req.onerror = (err) => reject(err); - }); -} -function reqAsCursorPromise(req) { - return reqAsEventPromise(req).then((event) => req.result); -} -class LocalIndexedDBStoreBackend { - static exists(indexedDB, dbName) { - dbName = "matrix-js-sdk:" + (dbName || "default"); - return IndexedDBHelpers.exists(indexedDB, dbName); - } - /** - * Does the actual reading from and writing to the indexeddb - * - * Construct a new Indexed Database store backend. This requires a call to - * `connect()` before this store can be used. - * @param indexedDB - The Indexed DB interface e.g - * `window.indexedDB` - * @param dbName - Optional database name. The same name must be used - * to open the same database. - */ - constructor(indexedDB, dbName = "default") { - this.indexedDB = indexedDB; - this.disconnected = true; - this._isNewlyCreated = false; - this.pendingUserPresenceData = []; - this.dbName = "matrix-js-sdk:" + dbName; - this.syncAccumulator = new sync_accumulator_1.SyncAccumulator(); - } - /** - * Attempt to connect to the database. This can fail if the user does not - * grant permission. - * @returns Promise which resolves if successfully connected. - */ - connect(onClose) { - if (!this.disconnected) { - logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: already connected or connecting`); - return Promise.resolve(); - } - this.disconnected = false; - logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: connecting...`); - const req = this.indexedDB.open(this.dbName, VERSION); - req.onupgradeneeded = (ev) => { - const db = req.result; - const oldVersion = ev.oldVersion; - logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`); - if (oldVersion < 1) { - // The database did not previously exist - this._isNewlyCreated = true; - } - DB_MIGRATIONS.forEach((migration, index) => { - if (oldVersion <= index) - migration(db); - }); - }; - req.onblocked = () => { - logger_1.logger.log(`can't yet open LocalIndexedDBStoreBackend because it is open elsewhere`); - }; - logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: awaiting connection...`); - return reqAsEventPromise(req).then(() => __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`LocalIndexedDBStoreBackend.connect: connected`); - this.db = req.result; - // add a poorly-named listener for when deleteDatabase is called - // so we can close our db connections. - this.db.onversionchange = () => { - var _a; - (_a = this.db) === null || _a === void 0 ? void 0 : _a.close(); // this does not call onclose - this.disconnected = true; - this.db = undefined; - onClose === null || onClose === void 0 ? void 0 : onClose(); - }; - this.db.onclose = () => { - this.disconnected = true; - this.db = undefined; - onClose === null || onClose === void 0 ? void 0 : onClose(); - }; - yield this.init(); - })); - } - /** @returns whether or not the database was newly created in this session. */ - isNewlyCreated() { - return Promise.resolve(this._isNewlyCreated); - } - /** - * Having connected, load initial data from the database and prepare for use - * @returns Promise which resolves on success - */ - init() { - return Promise.all([this.loadAccountData(), this.loadSyncData()]).then(([accountData, syncData]) => { - logger_1.logger.log(`LocalIndexedDBStoreBackend: loaded initial data`); - this.syncAccumulator.accumulate({ - next_batch: syncData.nextBatch, - rooms: syncData.roomsData, - account_data: { - events: accountData, - }, - }, true); - }); - } - /** - * Returns the out-of-band membership events for this room that - * were previously loaded. - * @returns the events, potentially an empty array if OOB loading didn't yield any new members - * @returns in case the members for this room haven't been stored yet - */ - getOutOfBandMembers(roomId) { - return new Promise((resolve, reject) => { - const tx = this.db.transaction(["oob_membership_events"], "readonly"); - const store = tx.objectStore("oob_membership_events"); - const roomIndex = store.index("room"); - const range = IDBKeyRange.only(roomId); - const request = roomIndex.openCursor(range); - const membershipEvents = []; - // did we encounter the oob_written marker object - // amongst the results? That means OOB member - // loading already happened for this room - // but there were no members to persist as they - // were all known already - let oobWritten = false; - request.onsuccess = () => { - const cursor = request.result; - if (!cursor) { - // Unknown room - if (!membershipEvents.length && !oobWritten) { - return resolve(null); - } - return resolve(membershipEvents); - } - const record = cursor.value; - if (record.oob_written) { - oobWritten = true; - } - else { - membershipEvents.push(record); - } - cursor.continue(); - }; - request.onerror = (err) => { - reject(err); - }; - }).then((events) => { - logger_1.logger.log(`LL: got ${events === null || events === void 0 ? void 0 : events.length} membershipEvents from storage for room ${roomId} ...`); - return events; - }); - } - /** - * Stores the out-of-band membership events for this room. Note that - * it still makes sense to store an empty array as the OOB status for the room is - * marked as fetched, and getOutOfBandMembers will return an empty array instead of null - * @param membershipEvents - the membership events to store - */ - setOutOfBandMembers(roomId, membershipEvents) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`LL: backend about to store ${membershipEvents.length}` + ` members for ${roomId}`); - const tx = this.db.transaction(["oob_membership_events"], "readwrite"); - const store = tx.objectStore("oob_membership_events"); - membershipEvents.forEach((e) => { - store.put(e); - }); - // aside from all the events, we also write a marker object to the store - // to mark the fact that OOB members have been written for this room. - // It's possible that 0 members need to be written as all where previously know - // but we still need to know whether to return null or [] from getOutOfBandMembers - // where null means out of band members haven't been stored yet for this room - const markerObject = { - room_id: roomId, - oob_written: true, - state_key: 0, - }; - store.put(markerObject); - yield txnAsPromise(tx); - logger_1.logger.log(`LL: backend done storing for ${roomId}!`); - }); - } - clearOutOfBandMembers(roomId) { - return __awaiter(this, void 0, void 0, function* () { - // the approach to delete all members for a room - // is to get the min and max state key from the index - // for that room, and then delete between those - // keys in the store. - // this should be way faster than deleting every member - // individually for a large room. - const readTx = this.db.transaction(["oob_membership_events"], "readonly"); - const store = readTx.objectStore("oob_membership_events"); - const roomIndex = store.index("room"); - const roomRange = IDBKeyRange.only(roomId); - const minStateKeyProm = reqAsCursorPromise(roomIndex.openKeyCursor(roomRange, "next")).then((cursor) => (cursor === null || cursor === void 0 ? void 0 : cursor.primaryKey)[1]); - const maxStateKeyProm = reqAsCursorPromise(roomIndex.openKeyCursor(roomRange, "prev")).then((cursor) => (cursor === null || cursor === void 0 ? void 0 : cursor.primaryKey)[1]); - const [minStateKey, maxStateKey] = yield Promise.all([minStateKeyProm, maxStateKeyProm]); - const writeTx = this.db.transaction(["oob_membership_events"], "readwrite"); - const writeStore = writeTx.objectStore("oob_membership_events"); - const membersKeyRange = IDBKeyRange.bound([roomId, minStateKey], [roomId, maxStateKey]); - logger_1.logger.log(`LL: Deleting all users + marker in storage for room ${roomId}, with key range:`, [roomId, minStateKey], [roomId, maxStateKey]); - yield reqAsPromise(writeStore.delete(membersKeyRange)); - }); - } - /** - * Clear the entire database. This should be used when logging out of a client - * to prevent mixing data between accounts. - * @returns Resolved when the database is cleared. - */ - clearDatabase() { - return new Promise((resolve) => { - logger_1.logger.log(`Removing indexeddb instance: ${this.dbName}`); - const req = this.indexedDB.deleteDatabase(this.dbName); - req.onblocked = () => { - logger_1.logger.log(`can't yet delete indexeddb ${this.dbName} because it is open elsewhere`); - }; - req.onerror = () => { - // in firefox, with indexedDB disabled, this fails with a - // DOMError. We treat this as non-fatal, so that we can still - // use the app. - logger_1.logger.warn(`unable to delete js-sdk store indexeddb: ${req.error}`); - resolve(); - }; - req.onsuccess = () => { - logger_1.logger.log(`Removed indexeddb instance: ${this.dbName}`); - resolve(); - }; - }); - } - /** - * @param copy - If false, the data returned is from internal - * buffers and must not be mutated. Otherwise, a copy is made before - * returning such that the data can be safely mutated. Default: true. - * - * @returns Promise which resolves with a sync response to restore the - * client state to where it was at the last save, or null if there - * is no saved sync data. - */ - getSavedSync(copy = true) { - const data = this.syncAccumulator.getJSON(); - if (!data.nextBatch) - return Promise.resolve(null); - if (copy) { - // We must deep copy the stored data so that the /sync processing code doesn't - // corrupt the internal state of the sync accumulator (it adds non-clonable keys) - return Promise.resolve(utils.deepCopy(data)); - } - else { - return Promise.resolve(data); - } - } - getNextBatchToken() { - return Promise.resolve(this.syncAccumulator.getNextBatchToken()); - } - setSyncData(syncData) { - return Promise.resolve().then(() => { - this.syncAccumulator.accumulate(syncData); - }); - } - /** - * Sync users and all accumulated sync data to the database. - * If a previous sync is in flight, the new data will be added to the - * next sync and the current sync's promise will be returned. - * @param userTuples - The user tuples - * @returns Promise which resolves if the data was persisted. - */ - syncToDatabase(userTuples) { - return __awaiter(this, void 0, void 0, function* () { - if (this.syncToDatabasePromise) { - logger_1.logger.warn("Skipping syncToDatabase() as persist already in flight"); - this.pendingUserPresenceData.push(...userTuples); - return this.syncToDatabasePromise; - } - userTuples.unshift(...this.pendingUserPresenceData); - this.syncToDatabasePromise = this.doSyncToDatabase(userTuples); - return this.syncToDatabasePromise; - }); - } - doSyncToDatabase(userTuples) { - return __awaiter(this, void 0, void 0, function* () { - try { - const syncData = this.syncAccumulator.getJSON(true); - yield Promise.all([ - this.persistUserPresenceEvents(userTuples), - this.persistAccountData(syncData.accountData), - this.persistSyncData(syncData.nextBatch, syncData.roomsData), - ]); - } - finally { - this.syncToDatabasePromise = undefined; - } - }); - } - /** - * Persist rooms /sync data along with the next batch token. - * @param nextBatch - The next_batch /sync value. - * @param roomsData - The 'rooms' /sync data from a SyncAccumulator - * @returns Promise which resolves if the data was persisted. - */ - persistSyncData(nextBatch, roomsData) { - logger_1.logger.log("Persisting sync data up to", nextBatch); - return utils.promiseTry(() => { - const txn = this.db.transaction(["sync"], "readwrite"); - const store = txn.objectStore("sync"); - store.put({ - clobber: "-", - nextBatch, - roomsData, - }); // put == UPSERT - return txnAsPromise(txn).then(() => { - logger_1.logger.log("Persisted sync data up to", nextBatch); - }); - }); - } - /** - * Persist a list of account data events. Events with the same 'type' will - * be replaced. - * @param accountData - An array of raw user-scoped account data events - * @returns Promise which resolves if the events were persisted. - */ - persistAccountData(accountData) { - return utils.promiseTry(() => { - const txn = this.db.transaction(["accountData"], "readwrite"); - const store = txn.objectStore("accountData"); - for (const event of accountData) { - store.put(event); // put == UPSERT - } - return txnAsPromise(txn).then(); - }); - } - /** - * Persist a list of [user id, presence event] they are for. - * Users with the same 'userId' will be replaced. - * Presence events should be the event in its raw form (not the Event - * object) - * @param tuples - An array of [userid, event] tuples - * @returns Promise which resolves if the users were persisted. - */ - persistUserPresenceEvents(tuples) { - return utils.promiseTry(() => { - const txn = this.db.transaction(["users"], "readwrite"); - const store = txn.objectStore("users"); - for (const tuple of tuples) { - store.put({ - userId: tuple[0], - event: tuple[1], - }); // put == UPSERT - } - return txnAsPromise(txn).then(); - }); - } - /** - * Load all user presence events from the database. This is not cached. - * FIXME: It would probably be more sensible to store the events in the - * sync. - * @returns A list of presence events in their raw form. - */ - getUserPresenceEvents() { - return utils.promiseTry(() => { - const txn = this.db.transaction(["users"], "readonly"); - const store = txn.objectStore("users"); - return selectQuery(store, undefined, (cursor) => { - return [cursor.value.userId, cursor.value.event]; - }); - }); - } - /** - * Load all the account data events from the database. This is not cached. - * @returns A list of raw global account events. - */ - loadAccountData() { - logger_1.logger.log(`LocalIndexedDBStoreBackend: loading account data...`); - return utils.promiseTry(() => { - const txn = this.db.transaction(["accountData"], "readonly"); - const store = txn.objectStore("accountData"); - return selectQuery(store, undefined, (cursor) => { - return cursor.value; - }).then((result) => { - logger_1.logger.log(`LocalIndexedDBStoreBackend: loaded account data`); - return result; - }); - }); - } - /** - * Load the sync data from the database. - * @returns An object with "roomsData" and "nextBatch" keys. - */ - loadSyncData() { - logger_1.logger.log(`LocalIndexedDBStoreBackend: loading sync data...`); - return utils.promiseTry(() => { - const txn = this.db.transaction(["sync"], "readonly"); - const store = txn.objectStore("sync"); - return selectQuery(store, undefined, (cursor) => { - return cursor.value; - }).then((results) => { - logger_1.logger.log(`LocalIndexedDBStoreBackend: loaded sync data`); - if (results.length > 1) { - logger_1.logger.warn("loadSyncData: More than 1 sync row found."); - } - return results.length > 0 ? results[0] : {}; - }); - }); - } - getClientOptions() { - return Promise.resolve().then(() => { - const txn = this.db.transaction(["client_options"], "readonly"); - const store = txn.objectStore("client_options"); - return selectQuery(store, undefined, (cursor) => { - var _a; - return (_a = cursor.value) === null || _a === void 0 ? void 0 : _a.options; - }).then((results) => results[0]); - }); - } - storeClientOptions(options) { - return __awaiter(this, void 0, void 0, function* () { - const txn = this.db.transaction(["client_options"], "readwrite"); - const store = txn.objectStore("client_options"); - store.put({ - clobber: "-", - options: options, - }); // put == UPSERT - yield txnAsPromise(txn); - }); - } - saveToDeviceBatches(batches) { - return __awaiter(this, void 0, void 0, function* () { - const txn = this.db.transaction(["to_device_queue"], "readwrite"); - const store = txn.objectStore("to_device_queue"); - for (const batch of batches) { - store.add(batch); - } - yield txnAsPromise(txn); - }); - } - getOldestToDeviceBatch() { - return __awaiter(this, void 0, void 0, function* () { - const txn = this.db.transaction(["to_device_queue"], "readonly"); - const store = txn.objectStore("to_device_queue"); - const cursor = yield reqAsCursorPromise(store.openCursor()); - if (!cursor) - return null; - const resultBatch = cursor.value; - return { - id: cursor.key, - txnId: resultBatch.txnId, - eventType: resultBatch.eventType, - batch: resultBatch.batch, - }; - }); - } - removeToDeviceBatch(id) { - return __awaiter(this, void 0, void 0, function* () { - const txn = this.db.transaction(["to_device_queue"], "readwrite"); - const store = txn.objectStore("to_device_queue"); - store.delete(id); - yield txnAsPromise(txn); - }); - } -} -exports.LocalIndexedDBStoreBackend = LocalIndexedDBStoreBackend; - -},{"../indexeddb-helpers":372,"../logger":374,"../sync-accumulator":413,"../utils":416}],409:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RemoteIndexedDBStoreBackend = void 0; -const logger_1 = require("../logger"); -const utils_1 = require("../utils"); -class RemoteIndexedDBStoreBackend { - /** - * An IndexedDB store backend where the actual backend sits in a web - * worker. - * - * Construct a new Indexed Database store backend. This requires a call to - * `connect()` before this store can be used. - * @param workerFactory - Factory which produces a Worker - * @param dbName - Optional database name. The same name must be used - * to open the same database. - */ - constructor(workerFactory, dbName) { - this.workerFactory = workerFactory; - this.dbName = dbName; - this.nextSeq = 0; - // The currently in-flight requests to the actual backend - this.inFlight = {}; // seq: promise - this.onWorkerMessage = (ev) => { - var _a; - const msg = ev.data; - if (msg.command == "closed") { - (_a = this.onClose) === null || _a === void 0 ? void 0 : _a.call(this); - } - else if (msg.command == "cmd_success" || msg.command == "cmd_fail") { - if (msg.seq === undefined) { - logger_1.logger.error("Got reply from worker with no seq"); - return; - } - const def = this.inFlight[msg.seq]; - if (def === undefined) { - logger_1.logger.error("Got reply for unknown seq " + msg.seq); - return; - } - delete this.inFlight[msg.seq]; - if (msg.command == "cmd_success") { - def.resolve(msg.result); - } - else { - const error = new Error(msg.error.message); - error.name = msg.error.name; - def.reject(error); - } - } - else { - logger_1.logger.warn("Unrecognised message from worker: ", msg); - } - }; - } - /** - * Attempt to connect to the database. This can fail if the user does not - * grant permission. - * @returns Promise which resolves if successfully connected. - */ - connect(onClose) { - this.onClose = onClose; - return this.ensureStarted().then(() => this.doCmd("connect")); - } - /** - * Clear the entire database. This should be used when logging out of a client - * to prevent mixing data between accounts. - * @returns Resolved when the database is cleared. - */ - clearDatabase() { - return this.ensureStarted().then(() => this.doCmd("clearDatabase")); - } - /** @returns whether or not the database was newly created in this session. */ - isNewlyCreated() { - return this.doCmd("isNewlyCreated"); - } - /** - * @returns Promise which resolves with a sync response to restore the - * client state to where it was at the last save, or null if there - * is no saved sync data. - */ - getSavedSync() { - return this.doCmd("getSavedSync"); - } - getNextBatchToken() { - return this.doCmd("getNextBatchToken"); - } - setSyncData(syncData) { - return this.doCmd("setSyncData", [syncData]); - } - syncToDatabase(userTuples) { - return this.doCmd("syncToDatabase", [userTuples]); - } - /** - * Returns the out-of-band membership events for this room that - * were previously loaded. - * @returns the events, potentially an empty array if OOB loading didn't yield any new members - * @returns in case the members for this room haven't been stored yet - */ - getOutOfBandMembers(roomId) { - return this.doCmd("getOutOfBandMembers", [roomId]); - } - /** - * Stores the out-of-band membership events for this room. Note that - * it still makes sense to store an empty array as the OOB status for the room is - * marked as fetched, and getOutOfBandMembers will return an empty array instead of null - * @param membershipEvents - the membership events to store - * @returns when all members have been stored - */ - setOutOfBandMembers(roomId, membershipEvents) { - return this.doCmd("setOutOfBandMembers", [roomId, membershipEvents]); - } - clearOutOfBandMembers(roomId) { - return this.doCmd("clearOutOfBandMembers", [roomId]); - } - getClientOptions() { - return this.doCmd("getClientOptions"); - } - storeClientOptions(options) { - return this.doCmd("storeClientOptions", [options]); - } - /** - * Load all user presence events from the database. This is not cached. - * @returns A list of presence events in their raw form. - */ - getUserPresenceEvents() { - return this.doCmd("getUserPresenceEvents"); - } - saveToDeviceBatches(batches) { - return __awaiter(this, void 0, void 0, function* () { - return this.doCmd("saveToDeviceBatches", [batches]); - }); - } - getOldestToDeviceBatch() { - return __awaiter(this, void 0, void 0, function* () { - return this.doCmd("getOldestToDeviceBatch"); - }); - } - removeToDeviceBatch(id) { - return __awaiter(this, void 0, void 0, function* () { - return this.doCmd("removeToDeviceBatch", [id]); - }); - } - ensureStarted() { - if (!this.startPromise) { - this.worker = this.workerFactory(); - this.worker.onmessage = this.onWorkerMessage; - // tell the worker the db name. - this.startPromise = this.doCmd("setupWorker", [this.dbName]).then(() => { - logger_1.logger.log("IndexedDB worker is ready"); - }); - } - return this.startPromise; - } - doCmd(command, args) { - // wrap in a q so if the postMessage throws, - // the promise automatically gets rejected - return Promise.resolve().then(() => { - var _a; - const seq = this.nextSeq++; - const def = (0, utils_1.defer)(); - this.inFlight[seq] = def; - (_a = this.worker) === null || _a === void 0 ? void 0 : _a.postMessage({ command, seq, args }); - return def.promise; - }); - } -} -exports.RemoteIndexedDBStoreBackend = RemoteIndexedDBStoreBackend; - -},{"../logger":374,"../utils":416}],410:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 Vector Creations Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IndexedDBStore = void 0; -/* eslint-disable @babel/no-invalid-this */ -const memory_1 = require("./memory"); -const indexeddb_local_backend_1 = require("./indexeddb-local-backend"); -const indexeddb_remote_backend_1 = require("./indexeddb-remote-backend"); -const user_1 = require("../models/user"); -const event_1 = require("../models/event"); -const logger_1 = require("../logger"); -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -/** - * This is an internal module. See {@link IndexedDBStore} for the public class. - */ -// If this value is too small we'll be writing very often which will cause -// noticeable stop-the-world pauses. If this value is too big we'll be writing -// so infrequently that the /sync size gets bigger on reload. Writing more -// often does not affect the length of the pause since the entire /sync -// response is persisted each time. -const WRITE_DELAY_MS = 1000 * 60 * 5; // once every 5 minutes -class IndexedDBStore extends memory_1.MemoryStore { - static exists(indexedDB, dbName) { - return indexeddb_local_backend_1.LocalIndexedDBStoreBackend.exists(indexedDB, dbName); - } - /** - * Construct a new Indexed Database store, which extends MemoryStore. - * - * This store functions like a MemoryStore except it periodically persists - * the contents of the store to an IndexedDB backend. - * - * All data is still kept in-memory but can be loaded from disk by calling - * `startup()`. This can make startup times quicker as a complete - * sync from the server is not required. This does not reduce memory usage as all - * the data is eagerly fetched when `startup()` is called. - * ``` - * let opts = { indexedDB: window.indexedDB, localStorage: window.localStorage }; - * let store = new IndexedDBStore(opts); - * await store.startup(); // load from indexed db - * let client = sdk.createClient({ - * store: store, - * }); - * client.startClient(); - * client.on("sync", function(state, prevState, data) { - * if (state === "PREPARED") { - * console.log("Started up, now with go faster stripes!"); - * } - * }); - * ``` - * - * @param opts - Options object. - */ - constructor(opts) { - super(opts); - this.startedUp = false; - this.syncTs = 0; - // Records the last-modified-time of each user at the last point we saved - // the database, such that we can derive the set if users that have been - // modified since we last saved. - this.userModifiedMap = {}; // user_id : timestamp - this.emitter = new typed_event_emitter_1.TypedEventEmitter(); - this.on = this.emitter.on.bind(this.emitter); - this.onClose = () => { - this.emitter.emit("closed"); - }; - /** - * @returns Promise which resolves with a sync response to restore the - * client state to where it was at the last save, or null if there - * is no saved sync data. - */ - this.getSavedSync = this.degradable(() => { - return this.backend.getSavedSync(); - }, "getSavedSync"); - /** @returns whether or not the database was newly created in this session. */ - this.isNewlyCreated = this.degradable(() => { - return this.backend.isNewlyCreated(); - }, "isNewlyCreated"); - /** - * @returns If there is a saved sync, the nextBatch token - * for this sync, otherwise null. - */ - this.getSavedSyncToken = this.degradable(() => { - return this.backend.getNextBatchToken(); - }, "getSavedSyncToken"); - /** - * Delete all data from this store. - * @returns Promise which resolves if the data was deleted from the database. - */ - this.deleteAllData = this.degradable(() => { - super.deleteAllData(); - return this.backend.clearDatabase().then(() => { - logger_1.logger.log("Deleted indexeddb data."); - }, (err) => { - logger_1.logger.error(`Failed to delete indexeddb data: ${err}`); - throw err; - }); - }); - this.reallySave = this.degradable(() => { - this.syncTs = Date.now(); // set now to guard against multi-writes - // work out changed users (this doesn't handle deletions but you - // can't 'delete' users as they are just presence events). - const userTuples = []; - for (const u of this.getUsers()) { - if (this.userModifiedMap[u.userId] === u.getLastModifiedTime()) - continue; - if (!u.events.presence) - continue; - userTuples.push([u.userId, u.events.presence.event]); - // note that we've saved this version of the user - this.userModifiedMap[u.userId] = u.getLastModifiedTime(); - } - return this.backend.syncToDatabase(userTuples); - }); - this.setSyncData = this.degradable((syncData) => { - return this.backend.setSyncData(syncData); - }, "setSyncData"); - /** - * Returns the out-of-band membership events for this room that - * were previously loaded. - * @returns the events, potentially an empty array if OOB loading didn't yield any new members - * @returns in case the members for this room haven't been stored yet - */ - this.getOutOfBandMembers = this.degradable((roomId) => { - return this.backend.getOutOfBandMembers(roomId); - }, "getOutOfBandMembers"); - /** - * Stores the out-of-band membership events for this room. Note that - * it still makes sense to store an empty array as the OOB status for the room is - * marked as fetched, and getOutOfBandMembers will return an empty array instead of null - * @param membershipEvents - the membership events to store - * @returns when all members have been stored - */ - this.setOutOfBandMembers = this.degradable((roomId, membershipEvents) => { - super.setOutOfBandMembers(roomId, membershipEvents); - return this.backend.setOutOfBandMembers(roomId, membershipEvents); - }, "setOutOfBandMembers"); - this.clearOutOfBandMembers = this.degradable((roomId) => { - super.clearOutOfBandMembers(roomId); - return this.backend.clearOutOfBandMembers(roomId); - }, "clearOutOfBandMembers"); - this.getClientOptions = this.degradable(() => { - return this.backend.getClientOptions(); - }, "getClientOptions"); - this.storeClientOptions = this.degradable((options) => { - super.storeClientOptions(options); - return this.backend.storeClientOptions(options); - }, "storeClientOptions"); - if (!opts.indexedDB) { - throw new Error("Missing required option: indexedDB"); - } - if (opts.workerFactory) { - this.backend = new indexeddb_remote_backend_1.RemoteIndexedDBStoreBackend(opts.workerFactory, opts.dbName); - } - else { - this.backend = new indexeddb_local_backend_1.LocalIndexedDBStoreBackend(opts.indexedDB, opts.dbName); - } - } - /** - * @returns Resolved when loaded from indexed db. - */ - startup() { - if (this.startedUp) { - logger_1.logger.log(`IndexedDBStore.startup: already started`); - return Promise.resolve(); - } - logger_1.logger.log(`IndexedDBStore.startup: connecting to backend`); - return this.backend - .connect(this.onClose) - .then(() => { - logger_1.logger.log(`IndexedDBStore.startup: loading presence events`); - return this.backend.getUserPresenceEvents(); - }) - .then((userPresenceEvents) => { - logger_1.logger.log(`IndexedDBStore.startup: processing presence events`); - userPresenceEvents.forEach(([userId, rawEvent]) => { - const u = new user_1.User(userId); - if (rawEvent) { - u.setPresenceEvent(new event_1.MatrixEvent(rawEvent)); - } - this.userModifiedMap[u.userId] = u.getLastModifiedTime(); - this.storeUser(u); - }); - this.startedUp = true; - }); - } - /** - * Whether this store would like to save its data - * Note that obviously whether the store wants to save or - * not could change between calling this function and calling - * save(). - * - * @returns True if calling save() will actually save - * (at the time this function is called). - */ - wantsSave() { - const now = Date.now(); - return now - this.syncTs > WRITE_DELAY_MS; - } - /** - * Possibly write data to the database. - * - * @param force - True to force a save to happen - * @returns Promise resolves after the write completes - * (or immediately if no write is performed) - */ - save(force = false) { - if (force || this.wantsSave()) { - return this.reallySave(); - } - return Promise.resolve(); - } - /** - * All member functions of `IndexedDBStore` that access the backend use this wrapper to - * watch for failures after initial store startup, including `QuotaExceededError` as - * free disk space changes, etc. - * - * When IndexedDB fails via any of these paths, we degrade this back to a `MemoryStore` - * in place so that the current operation and all future ones are in-memory only. - * - * @param func - The degradable work to do. - * @param fallback - The method name for fallback. - * @returns A wrapped member function. - */ - degradable(func, fallback) { - const fallbackFn = fallback ? super[fallback] : null; - return (...args) => __awaiter(this, void 0, void 0, function* () { - try { - return yield func.call(this, ...args); - } - catch (e) { - logger_1.logger.error("IndexedDBStore failure, degrading to MemoryStore", e); - this.emitter.emit("degraded", e); - try { - // We try to delete IndexedDB after degrading since this store is only a - // cache (the app will still function correctly without the data). - // It's possible that deleting repair IndexedDB for the next app load, - // potentially by making a little more space available. - logger_1.logger.log("IndexedDBStore trying to delete degraded data"); - yield this.backend.clearDatabase(); - logger_1.logger.log("IndexedDBStore delete after degrading succeeded"); - } - catch (e) { - logger_1.logger.warn("IndexedDBStore delete after degrading failed", e); - } - // Degrade the store from being an instance of `IndexedDBStore` to instead be - // an instance of `MemoryStore` so that future API calls use the memory path - // directly and skip IndexedDB entirely. This should be safe as - // `IndexedDBStore` already extends from `MemoryStore`, so we are making the - // store become its parent type in a way. The mutator methods of - // `IndexedDBStore` also maintain the state that `MemoryStore` uses (many are - // not overridden at all). - if (fallbackFn) { - return fallbackFn.call(this, ...args); - } - } - }); - } - // XXX: ideally these would be stored in indexeddb as part of the room but, - // we don't store rooms as such and instead accumulate entire sync responses atm. - getPendingEvents(roomId) { - const _super = Object.create(null, { - getPendingEvents: { get: () => super.getPendingEvents } - }); - return __awaiter(this, void 0, void 0, function* () { - if (!this.localStorage) - return _super.getPendingEvents.call(this, roomId); - const serialized = this.localStorage.getItem(pendingEventsKey(roomId)); - if (serialized) { - try { - return JSON.parse(serialized); - } - catch (e) { - logger_1.logger.error("Could not parse persisted pending events", e); - } - } - return []; - }); - } - setPendingEvents(roomId, events) { - const _super = Object.create(null, { - setPendingEvents: { get: () => super.setPendingEvents } - }); - return __awaiter(this, void 0, void 0, function* () { - if (!this.localStorage) - return _super.setPendingEvents.call(this, roomId, events); - if (events.length > 0) { - this.localStorage.setItem(pendingEventsKey(roomId), JSON.stringify(events)); - } - else { - this.localStorage.removeItem(pendingEventsKey(roomId)); - } - }); - } - saveToDeviceBatches(batches) { - return this.backend.saveToDeviceBatches(batches); - } - getOldestToDeviceBatch() { - return this.backend.getOldestToDeviceBatch(); - } - removeToDeviceBatch(id) { - return this.backend.removeToDeviceBatch(id); - } -} -exports.IndexedDBStore = IndexedDBStore; -/** - * @param roomId - ID of the current room - * @returns Storage key to retrieve pending events - */ -function pendingEventsKey(roomId) { - return `mx_pending_events_${roomId}`; -} - -},{"../logger":374,"../models/event":383,"../models/typed-event-emitter":395,"../models/user":396,"./indexeddb-local-backend":408,"./indexeddb-remote-backend":409,"./memory":411}],411:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MemoryStore = void 0; -const user_1 = require("../models/user"); -const room_state_1 = require("../models/room-state"); -const utils_1 = require("../utils"); -function isValidFilterId(filterId) { - const isValidStr = typeof filterId === "string" && - !!filterId && - filterId !== "undefined" && // exclude these as we've serialized undefined in localStorage before - filterId !== "null"; - return isValidStr || typeof filterId === "number"; -} -class MemoryStore { - /** - * Construct a new in-memory data store for the Matrix Client. - * @param opts - Config options - */ - constructor(opts = {}) { - this.rooms = {}; // roomId: Room - this.users = {}; // userId: User - this.syncToken = null; - // userId: { - // filterId: Filter - // } - this.filters = new utils_1.MapWithDefault(() => new Map()); - this.accountData = new Map(); // type: content - this.oobMembers = new Map(); // roomId: [member events] - this.pendingEvents = {}; - this.pendingToDeviceBatches = []; - this.nextToDeviceBatchId = 0; - /** - * Called when a room member in a room being tracked by this store has been - * updated. - */ - this.onRoomMember = (event, state, member) => { - if (member.membership === "invite") { - // We do NOT add invited members because people love to typo user IDs - // which would then show up in these lists (!) - return; - } - const user = this.users[member.userId] || new user_1.User(member.userId); - if (member.name) { - user.setDisplayName(member.name); - if (member.events.member) { - user.setRawDisplayName(member.events.member.getDirectionalContent().displayname); - } - } - if (member.events.member && member.events.member.getContent().avatar_url) { - user.setAvatarUrl(member.events.member.getContent().avatar_url); - } - this.users[user.userId] = user; - }; - this.localStorage = opts.localStorage; - } - /** - * Retrieve the token to stream from. - * @returns The token or null. - */ - getSyncToken() { - return this.syncToken; - } - /** @returns whether or not the database was newly created in this session. */ - isNewlyCreated() { - return Promise.resolve(true); - } - /** - * Set the token to stream from. - * @param token - The token to stream from. - */ - setSyncToken(token) { - this.syncToken = token; - } - /** - * Store the given room. - * @param room - The room to be stored. All properties must be stored. - */ - storeRoom(room) { - this.rooms[room.roomId] = room; - // add listeners for room member changes so we can keep the room member - // map up-to-date. - room.currentState.on(room_state_1.RoomStateEvent.Members, this.onRoomMember); - // add existing members - room.currentState.getMembers().forEach((m) => { - this.onRoomMember(null, room.currentState, m); - }); - } - /** - * Retrieve a room by its' room ID. - * @param roomId - The room ID. - * @returns The room or null. - */ - getRoom(roomId) { - return this.rooms[roomId] || null; - } - /** - * Retrieve all known rooms. - * @returns A list of rooms, which may be empty. - */ - getRooms() { - return Object.values(this.rooms); - } - /** - * Permanently delete a room. - */ - removeRoom(roomId) { - if (this.rooms[roomId]) { - this.rooms[roomId].currentState.removeListener(room_state_1.RoomStateEvent.Members, this.onRoomMember); - } - delete this.rooms[roomId]; - } - /** - * Retrieve a summary of all the rooms. - * @returns A summary of each room. - */ - getRoomSummaries() { - return Object.values(this.rooms).map(function (room) { - return room.summary; - }); - } - /** - * Store a User. - * @param user - The user to store. - */ - storeUser(user) { - this.users[user.userId] = user; - } - /** - * Retrieve a User by its' user ID. - * @param userId - The user ID. - * @returns The user or null. - */ - getUser(userId) { - return this.users[userId] || null; - } - /** - * Retrieve all known users. - * @returns A list of users, which may be empty. - */ - getUsers() { - return Object.values(this.users); - } - /** - * Retrieve scrollback for this room. - * @param room - The matrix room - * @param limit - The max number of old events to retrieve. - * @returns An array of objects which will be at most 'limit' - * length and at least 0. The objects are the raw event JSON. - */ - scrollback(room, limit) { - return []; - } - /** - * Store events for a room. The events have already been added to the timeline - * @param room - The room to store events for. - * @param events - The events to store. - * @param token - The token associated with these events. - * @param toStart - True if these are paginated results. - */ - storeEvents(room, events, token, toStart) { - // no-op because they've already been added to the room instance. - } - /** - * Store a filter. - */ - storeFilter(filter) { - if (!(filter === null || filter === void 0 ? void 0 : filter.userId) || !(filter === null || filter === void 0 ? void 0 : filter.filterId)) - return; - this.filters.getOrCreate(filter.userId).set(filter.filterId, filter); - } - /** - * Retrieve a filter. - * @returns A filter or null. - */ - getFilter(userId, filterId) { - var _a; - return ((_a = this.filters.get(userId)) === null || _a === void 0 ? void 0 : _a.get(filterId)) || null; - } - /** - * Retrieve a filter ID with the given name. - * @param filterName - The filter name. - * @returns The filter ID or null. - */ - getFilterIdByName(filterName) { - if (!this.localStorage) { - return null; - } - const key = "mxjssdk_memory_filter_" + filterName; - // XXX Storage.getItem doesn't throw ... - // or are we using something different - // than window.localStorage in some cases - // that does throw? - // that would be very naughty - try { - const value = this.localStorage.getItem(key); - if (isValidFilterId(value)) { - return value; - } - } - catch (e) { } - return null; - } - /** - * Set a filter name to ID mapping. - */ - setFilterIdByName(filterName, filterId) { - if (!this.localStorage) { - return; - } - const key = "mxjssdk_memory_filter_" + filterName; - try { - if (isValidFilterId(filterId)) { - this.localStorage.setItem(key, filterId); - } - else { - this.localStorage.removeItem(key); - } - } - catch (e) { } - } - /** - * Store user-scoped account data events. - * N.B. that account data only allows a single event per type, so multiple - * events with the same type will replace each other. - * @param events - The events to store. - */ - storeAccountDataEvents(events) { - events.forEach((event) => { - // MSC3391: an event with content of {} should be interpreted as deleted - const isDeleted = !Object.keys(event.getContent()).length; - if (isDeleted) { - this.accountData.delete(event.getType()); - } - else { - this.accountData.set(event.getType(), event); - } - }); - } - /** - * Get account data event by event type - * @param eventType - The event type being queried - * @returns the user account_data event of given type, if any - */ - getAccountData(eventType) { - return this.accountData.get(eventType); - } - /** - * setSyncData does nothing as there is no backing data store. - * - * @param syncData - The sync data - * @returns An immediately resolved promise. - */ - setSyncData(syncData) { - return Promise.resolve(); - } - /** - * We never want to save becase we have nothing to save to. - * - * @returns If the store wants to save - */ - wantsSave() { - return false; - } - /** - * Save does nothing as there is no backing data store. - * @param force - True to force a save (but the memory - * store still can't save anything) - */ - save(force) { } - /** - * Startup does nothing as this store doesn't require starting up. - * @returns An immediately resolved promise. - */ - startup() { - return Promise.resolve(); - } - /** - * @returns Promise which resolves with a sync response to restore the - * client state to where it was at the last save, or null if there - * is no saved sync data. - */ - getSavedSync() { - return Promise.resolve(null); - } - /** - * @returns If there is a saved sync, the nextBatch token - * for this sync, otherwise null. - */ - getSavedSyncToken() { - return Promise.resolve(null); - } - /** - * Delete all data from this store. - * @returns An immediately resolved promise. - */ - deleteAllData() { - this.rooms = { - // roomId: Room - }; - this.users = { - // userId: User - }; - this.syncToken = null; - this.filters = new utils_1.MapWithDefault(() => new Map()); - this.accountData = new Map(); // type : content - return Promise.resolve(); - } - /** - * Returns the out-of-band membership events for this room that - * were previously loaded. - * @returns the events, potentially an empty array if OOB loading didn't yield any new members - * @returns in case the members for this room haven't been stored yet - */ - getOutOfBandMembers(roomId) { - return Promise.resolve(this.oobMembers.get(roomId) || null); - } - /** - * Stores the out-of-band membership events for this room. Note that - * it still makes sense to store an empty array as the OOB status for the room is - * marked as fetched, and getOutOfBandMembers will return an empty array instead of null - * @param membershipEvents - the membership events to store - * @returns when all members have been stored - */ - setOutOfBandMembers(roomId, membershipEvents) { - this.oobMembers.set(roomId, membershipEvents); - return Promise.resolve(); - } - clearOutOfBandMembers(roomId) { - this.oobMembers.delete(roomId); - return Promise.resolve(); - } - getClientOptions() { - return Promise.resolve(this.clientOptions); - } - storeClientOptions(options) { - this.clientOptions = Object.assign({}, options); - return Promise.resolve(); - } - getPendingEvents(roomId) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - return (_a = this.pendingEvents[roomId]) !== null && _a !== void 0 ? _a : []; - }); - } - setPendingEvents(roomId, events) { - return __awaiter(this, void 0, void 0, function* () { - this.pendingEvents[roomId] = events; - }); - } - saveToDeviceBatches(batches) { - for (const batch of batches) { - this.pendingToDeviceBatches.push({ - id: this.nextToDeviceBatchId++, - eventType: batch.eventType, - txnId: batch.txnId, - batch: batch.batch, - }); - } - return Promise.resolve(); - } - getOldestToDeviceBatch() { - return __awaiter(this, void 0, void 0, function* () { - if (this.pendingToDeviceBatches.length === 0) - return null; - return this.pendingToDeviceBatches[0]; - }); - } - removeToDeviceBatch(id) { - this.pendingToDeviceBatches = this.pendingToDeviceBatches.filter((batch) => batch.id !== id); - return Promise.resolve(); - } -} -exports.MemoryStore = MemoryStore; - -},{"../models/room-state":390,"../models/user":396,"../utils":416}],412:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StubStore = void 0; -/** - * Construct a stub store. This does no-ops on most store methods. - */ -class StubStore { - constructor() { - this.accountData = new Map(); // stub - this.fromToken = null; - } - /** @returns whether or not the database was newly created in this session. */ - isNewlyCreated() { - return Promise.resolve(true); - } - /** - * Get the sync token. - */ - getSyncToken() { - return this.fromToken; - } - /** - * Set the sync token. - */ - setSyncToken(token) { - this.fromToken = token; - } - /** - * No-op. - */ - storeRoom(room) { } - /** - * No-op. - */ - getRoom(roomId) { - return null; - } - /** - * No-op. - * @returns An empty array. - */ - getRooms() { - return []; - } - /** - * Permanently delete a room. - */ - removeRoom(roomId) { - return; - } - /** - * No-op. - * @returns An empty array. - */ - getRoomSummaries() { - return []; - } - /** - * No-op. - */ - storeUser(user) { } - /** - * No-op. - */ - getUser(userId) { - return null; - } - /** - * No-op. - */ - getUsers() { - return []; - } - /** - * No-op. - */ - scrollback(room, limit) { - return []; - } - /** - * Store events for a room. - * @param room - The room to store events for. - * @param events - The events to store. - * @param token - The token associated with these events. - * @param toStart - True if these are paginated results. - */ - storeEvents(room, events, token, toStart) { } - /** - * Store a filter. - */ - storeFilter(filter) { } - /** - * Retrieve a filter. - * @returns A filter or null. - */ - getFilter(userId, filterId) { - return null; - } - /** - * Retrieve a filter ID with the given name. - * @param filterName - The filter name. - * @returns The filter ID or null. - */ - getFilterIdByName(filterName) { - return null; - } - /** - * Set a filter name to ID mapping. - */ - setFilterIdByName(filterName, filterId) { } - /** - * Store user-scoped account data events - * @param events - The events to store. - */ - storeAccountDataEvents(events) { } - /** - * Get account data event by event type - * @param eventType - The event type being queried - */ - getAccountData(eventType) { - return undefined; - } - /** - * setSyncData does nothing as there is no backing data store. - * - * @param syncData - The sync data - * @returns An immediately resolved promise. - */ - setSyncData(syncData) { - return Promise.resolve(); - } - /** - * We never want to save because we have nothing to save to. - * - * @returns If the store wants to save - */ - wantsSave() { - return false; - } - /** - * Save does nothing as there is no backing data store. - */ - save() { } - /** - * Startup does nothing. - * @returns An immediately resolved promise. - */ - startup() { - return Promise.resolve(); - } - /** - * @returns Promise which resolves with a sync response to restore the - * client state to where it was at the last save, or null if there - * is no saved sync data. - */ - getSavedSync() { - return Promise.resolve(null); - } - /** - * @returns If there is a saved sync, the nextBatch token - * for this sync, otherwise null. - */ - getSavedSyncToken() { - return Promise.resolve(null); - } - /** - * Delete all data from this store. Does nothing since this store - * doesn't store anything. - * @returns An immediately resolved promise. - */ - deleteAllData() { - return Promise.resolve(); - } - getOutOfBandMembers() { - return Promise.resolve(null); - } - setOutOfBandMembers(roomId, membershipEvents) { - return Promise.resolve(); - } - clearOutOfBandMembers() { - return Promise.resolve(); - } - getClientOptions() { - return Promise.resolve(undefined); - } - storeClientOptions(options) { - return Promise.resolve(); - } - getPendingEvents(roomId) { - return __awaiter(this, void 0, void 0, function* () { - return []; - }); - } - setPendingEvents(roomId, events) { - return Promise.resolve(); - } - saveToDeviceBatches(batch) { - return __awaiter(this, void 0, void 0, function* () { - return Promise.resolve(); - }); - } - getOldestToDeviceBatch() { - return Promise.resolve(null); - } - removeToDeviceBatch(id) { - return __awaiter(this, void 0, void 0, function* () { - return Promise.resolve(); - }); - } -} -exports.StubStore = StubStore; - -},{}],413:[function(require,module,exports){ -"use strict"; -/* -Copyright 2017 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SyncAccumulator = exports.Category = void 0; -/** - * This is an internal module. See {@link SyncAccumulator} for the public class. - */ -const logger_1 = require("./logger"); -const utils_1 = require("./utils"); -const event_1 = require("./@types/event"); -const read_receipts_1 = require("./@types/read_receipts"); -const sync_1 = require("./@types/sync"); -/* eslint-enable camelcase */ -var Category; -(function (Category) { - Category["Invite"] = "invite"; - Category["Leave"] = "leave"; - Category["Join"] = "join"; -})(Category = exports.Category || (exports.Category = {})); -function isTaggedEvent(event) { - return "_localTs" in event && event["_localTs"] !== undefined; -} -/** - * The purpose of this class is to accumulate /sync responses such that a - * complete "initial" JSON response can be returned which accurately represents - * the sum total of the /sync responses accumulated to date. It only handles - * room data: that is, everything under the "rooms" top-level key. - * - * This class is used when persisting room data so a complete /sync response can - * be loaded from disk and incremental syncs can be performed on the server, - * rather than asking the server to do an initial sync on startup. - */ -class SyncAccumulator { - constructor(opts = {}) { - this.opts = opts; - this.accountData = {}; // $event_type: Object - this.inviteRooms = {}; // $roomId: { ... sync 'invite' json data ... } - this.joinRooms = {}; - // the /sync token which corresponds to the last time rooms were - // accumulated. We remember this so that any caller can obtain a - // coherent /sync response and know at what point they should be - // streaming from without losing events. - this.nextBatch = null; - this.opts.maxTimelineEntries = this.opts.maxTimelineEntries || 50; - } - accumulate(syncResponse, fromDatabase = false) { - this.accumulateRooms(syncResponse, fromDatabase); - this.accumulateAccountData(syncResponse); - this.nextBatch = syncResponse.next_batch; - } - accumulateAccountData(syncResponse) { - if (!syncResponse.account_data || !syncResponse.account_data.events) { - return; - } - // Clobbers based on event type. - syncResponse.account_data.events.forEach((e) => { - this.accountData[e.type] = e; - }); - } - /** - * Accumulate incremental /sync room data. - * @param syncResponse - the complete /sync JSON - * @param fromDatabase - True if the sync response is one saved to the database - */ - accumulateRooms(syncResponse, fromDatabase = false) { - if (!syncResponse.rooms) { - return; - } - if (syncResponse.rooms.invite) { - Object.keys(syncResponse.rooms.invite).forEach((roomId) => { - this.accumulateRoom(roomId, Category.Invite, syncResponse.rooms.invite[roomId], fromDatabase); - }); - } - if (syncResponse.rooms.join) { - Object.keys(syncResponse.rooms.join).forEach((roomId) => { - this.accumulateRoom(roomId, Category.Join, syncResponse.rooms.join[roomId], fromDatabase); - }); - } - if (syncResponse.rooms.leave) { - Object.keys(syncResponse.rooms.leave).forEach((roomId) => { - this.accumulateRoom(roomId, Category.Leave, syncResponse.rooms.leave[roomId], fromDatabase); - }); - } - } - accumulateRoom(roomId, category, data, fromDatabase = false) { - // Valid /sync state transitions - // +--------+ <======+ 1: Accept an invite - // +== | INVITE | | (5) 2: Leave a room - // | +--------+ =====+ | 3: Join a public room previously - // |(1) (4) | | left (handle as if new room) - // V (2) V | 4: Reject an invite - // +------+ ========> +--------+ 5: Invite to a room previously - // | JOIN | (3) | LEAVE* | left (handle as if new room) - // +------+ <======== +--------+ - // - // * equivalent to "no state" - switch (category) { - case Category.Invite: // (5) - this.accumulateInviteState(roomId, data); - break; - case Category.Join: - if (this.inviteRooms[roomId]) { - // (1) - // was previously invite, now join. We expect /sync to give - // the entire state and timeline on 'join', so delete previous - // invite state - delete this.inviteRooms[roomId]; - } - // (3) - this.accumulateJoinState(roomId, data, fromDatabase); - break; - case Category.Leave: - if (this.inviteRooms[roomId]) { - // (4) - delete this.inviteRooms[roomId]; - } - else { - // (2) - delete this.joinRooms[roomId]; - } - break; - default: - logger_1.logger.error("Unknown cateogory: ", category); - } - } - accumulateInviteState(roomId, data) { - if (!data.invite_state || !data.invite_state.events) { - // no new data - return; - } - if (!this.inviteRooms[roomId]) { - this.inviteRooms[roomId] = { - invite_state: data.invite_state, - }; - return; - } - // accumulate extra keys for invite->invite transitions - // clobber based on event type / state key - // We expect invite_state to be small, so just loop over the events - const currentData = this.inviteRooms[roomId]; - data.invite_state.events.forEach((e) => { - let hasAdded = false; - for (let i = 0; i < currentData.invite_state.events.length; i++) { - const current = currentData.invite_state.events[i]; - if (current.type === e.type && current.state_key == e.state_key) { - currentData.invite_state.events[i] = e; // update - hasAdded = true; - } - } - if (!hasAdded) { - currentData.invite_state.events.push(e); - } - }); - } - // Accumulate timeline and state events in a room. - accumulateJoinState(roomId, data, fromDatabase = false) { - // We expect this function to be called a lot (every /sync) so we want - // this to be fast. /sync stores events in an array but we often want - // to clobber based on type/state_key. Rather than convert arrays to - // maps all the time, just keep private maps which contain - // the actual current accumulated sync state, and array-ify it when - // getJSON() is called. - var _a, _b, _c, _d, _e, _f, _g, _h; - // State resolution: - // The 'state' key is the delta from the previous sync (or start of time - // if no token was supplied), to the START of the timeline. To obtain - // the current state, we need to "roll forward" state by reading the - // timeline. We want to store the current state so we can drop events - // out the end of the timeline based on opts.maxTimelineEntries. - // - // 'state' 'timeline' current state - // |-------x<======================>x - // T I M E - // - // When getJSON() is called, we 'roll back' the current state by the - // number of entries in the timeline to work out what 'state' should be. - // Back-pagination: - // On an initial /sync, the server provides a back-pagination token for - // the start of the timeline. When /sync deltas come down, they also - // include back-pagination tokens for the start of the timeline. This - // means not all events in the timeline have back-pagination tokens, as - // it is only the ones at the START of the timeline which have them. - // In order for us to have a valid timeline (and back-pagination token - // to match), we need to make sure that when we remove old timeline - // events, that we roll forward to an event which has a back-pagination - // token. This means we can't keep a strict sliding-window based on - // opts.maxTimelineEntries, and we may have a few less. We should never - // have more though, provided that the /sync limit is less than or equal - // to opts.maxTimelineEntries. - if (!this.joinRooms[roomId]) { - // Create truly empty objects so event types of 'hasOwnProperty' and co - // don't cause this code to break. - this.joinRooms[roomId] = { - _currentState: Object.create(null), - _timeline: [], - _accountData: Object.create(null), - _unreadNotifications: {}, - _unreadThreadNotifications: {}, - _summary: {}, - _readReceipts: {}, - _threadReadReceipts: {}, - }; - } - const currentData = this.joinRooms[roomId]; - if (data.account_data && data.account_data.events) { - // clobber based on type - data.account_data.events.forEach((e) => { - currentData._accountData[e.type] = e; - }); - } - // these probably clobber, spec is unclear. - if (data.unread_notifications) { - currentData._unreadNotifications = data.unread_notifications; - } - currentData._unreadThreadNotifications = - (_b = (_a = data[sync_1.UNREAD_THREAD_NOTIFICATIONS.stable]) !== null && _a !== void 0 ? _a : data[sync_1.UNREAD_THREAD_NOTIFICATIONS.unstable]) !== null && _b !== void 0 ? _b : undefined; - if (data.summary) { - const HEROES_KEY = "m.heroes"; - const INVITED_COUNT_KEY = "m.invited_member_count"; - const JOINED_COUNT_KEY = "m.joined_member_count"; - const acc = currentData._summary; - const sum = data.summary; - acc[HEROES_KEY] = sum[HEROES_KEY] || acc[HEROES_KEY]; - acc[JOINED_COUNT_KEY] = sum[JOINED_COUNT_KEY] || acc[JOINED_COUNT_KEY]; - acc[INVITED_COUNT_KEY] = sum[INVITED_COUNT_KEY] || acc[INVITED_COUNT_KEY]; - } - (_d = (_c = data.ephemeral) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.forEach((e) => { - // We purposefully do not persist m.typing events. - // Technically you could refresh a browser before the timer on a - // typing event is up, so it'll look like you aren't typing when - // you really still are. However, the alternative is worse. If - // we do persist typing events, it will look like people are - // typing forever until someone really does start typing (which - // will prompt Synapse to send down an actual m.typing event to - // clobber the one we persisted). - if (e.type !== event_1.EventType.Receipt || !e.content) { - // This means we'll drop unknown ephemeral events but that - // seems okay. - return; - } - // Handle m.receipt events. They clobber based on: - // (user_id, receipt_type) - // but they are keyed in the event as: - // content:{ $event_id: { $receipt_type: { $user_id: {json} }}} - // so store them in the former so we can accumulate receipt deltas - // quickly and efficiently (we expect a lot of them). Fold the - // receipt type into the key name since we only have 1 at the - // moment (m.read) and nested JSON objects are slower and more - // of a hassle to work with. We'll inflate this back out when - // getJSON() is called. - Object.keys(e.content).forEach((eventId) => { - Object.entries(e.content[eventId]).forEach(([key, value]) => { - var _a; - if (!(0, utils_1.isSupportedReceiptType)(key)) - return; - for (const userId of Object.keys(value)) { - const data = e.content[eventId][key][userId]; - const receipt = { - data: e.content[eventId][key][userId], - type: key, - eventId: eventId, - }; - if (!data.thread_id || data.thread_id === read_receipts_1.MAIN_ROOM_TIMELINE) { - currentData._readReceipts[userId] = receipt; - } - else { - currentData._threadReadReceipts = Object.assign(Object.assign({}, currentData._threadReadReceipts), { [data.thread_id]: Object.assign(Object.assign({}, ((_a = currentData._threadReadReceipts[data.thread_id]) !== null && _a !== void 0 ? _a : {})), { [userId]: receipt }) }); - } - } - }); - }); - }); - // if we got a limited sync, we need to remove all timeline entries or else - // we will have gaps in the timeline. - if (data.timeline && data.timeline.limited) { - currentData._timeline = []; - } - // Work out the current state. The deltas need to be applied in the order: - // - existing state which didn't come down /sync. - // - State events under the 'state' key. - // - State events in the 'timeline'. - (_f = (_e = data.state) === null || _e === void 0 ? void 0 : _e.events) === null || _f === void 0 ? void 0 : _f.forEach((e) => { - setState(currentData._currentState, e); - }); - (_h = (_g = data.timeline) === null || _g === void 0 ? void 0 : _g.events) === null || _h === void 0 ? void 0 : _h.forEach((e, index) => { - var _a; - // this nops if 'e' isn't a state event - setState(currentData._currentState, e); - // append the event to the timeline. The back-pagination token - // corresponds to the first event in the timeline - let transformedEvent; - if (!fromDatabase) { - transformedEvent = Object.assign({}, e); - if (transformedEvent.unsigned !== undefined) { - transformedEvent.unsigned = Object.assign({}, transformedEvent.unsigned); - } - const age = e.unsigned ? e.unsigned.age : e.age; - if (age !== undefined) - transformedEvent._localTs = Date.now() - age; - } - else { - transformedEvent = e; - } - currentData._timeline.push({ - event: transformedEvent, - token: index === 0 ? (_a = data.timeline.prev_batch) !== null && _a !== void 0 ? _a : null : null, - }); - }); - // attempt to prune the timeline by jumping between events which have - // pagination tokens. - if (currentData._timeline.length > this.opts.maxTimelineEntries) { - const startIndex = currentData._timeline.length - this.opts.maxTimelineEntries; - for (let i = startIndex; i < currentData._timeline.length; i++) { - if (currentData._timeline[i].token) { - // keep all events after this, including this one - currentData._timeline = currentData._timeline.slice(i, currentData._timeline.length); - break; - } - } - } - } - /** - * Return everything under the 'rooms' key from a /sync response which - * represents all room data that should be stored. This should be paired - * with the sync token which represents the most recent /sync response - * provided to accumulate(). - * @param forDatabase - True to generate a sync to be saved to storage - * @returns An object with a "nextBatch", "roomsData" and "accountData" - * keys. - * The "nextBatch" key is a string which represents at what point in the - * /sync stream the accumulator reached. This token should be used when - * restarting a /sync stream at startup. Failure to do so can lead to missing - * events. The "roomsData" key is an Object which represents the entire - * /sync response from the 'rooms' key onwards. The "accountData" key is - * a list of raw events which represent global account data. - */ - getJSON(forDatabase = false) { - const data = { - join: {}, - invite: {}, - // always empty. This is set by /sync when a room was previously - // in 'invite' or 'join'. On fresh startup, the client won't know - // about any previous room being in 'invite' or 'join' so we can - // just omit mentioning it at all, even if it has previously come - // down /sync. - // The notable exception is when a client is kicked or banned: - // we may want to hold onto that room so the client can clearly see - // why their room has disappeared. We don't persist it though because - // it is unclear *when* we can safely remove the room from the DB. - // Instead, we assume that if you're loading from the DB, you've - // refreshed the page, which means you've seen the kick/ban already. - leave: {}, - }; - Object.keys(this.inviteRooms).forEach((roomId) => { - data.invite[roomId] = this.inviteRooms[roomId]; - }); - Object.keys(this.joinRooms).forEach((roomId) => { - const roomData = this.joinRooms[roomId]; - const roomJson = { - ephemeral: { events: [] }, - account_data: { events: [] }, - state: { events: [] }, - timeline: { - events: [], - prev_batch: null, - }, - unread_notifications: roomData._unreadNotifications, - unread_thread_notifications: roomData._unreadThreadNotifications, - summary: roomData._summary, - }; - // Add account data - Object.keys(roomData._accountData).forEach((evType) => { - roomJson.account_data.events.push(roomData._accountData[evType]); - }); - // Add receipt data - const receiptEvent = { - type: event_1.EventType.Receipt, - room_id: roomId, - content: { - // $event_id: { "m.read": { $user_id: $json } } - }, - }; - const receiptEventContent = new utils_1.MapWithDefault(() => new utils_1.MapWithDefault(() => new Map())); - for (const [userId, receiptData] of Object.entries(roomData._readReceipts)) { - receiptEventContent - .getOrCreate(receiptData.eventId) - .getOrCreate(receiptData.type) - .set(userId, receiptData.data); - } - for (const threadReceipts of Object.values(roomData._threadReadReceipts)) { - for (const [userId, receiptData] of Object.entries(threadReceipts)) { - receiptEventContent - .getOrCreate(receiptData.eventId) - .getOrCreate(receiptData.type) - .set(userId, receiptData.data); - } - } - receiptEvent.content = (0, utils_1.recursiveMapToObject)(receiptEventContent); - // add only if we have some receipt data - if (receiptEventContent.size > 0) { - roomJson.ephemeral.events.push(receiptEvent); - } - // Add timeline data - roomData._timeline.forEach((msgData) => { - if (!roomJson.timeline.prev_batch) { - // the first event we add to the timeline MUST match up to - // the prev_batch token. - if (!msgData.token) { - return; // this shouldn't happen as we prune constantly. - } - roomJson.timeline.prev_batch = msgData.token; - } - let transformedEvent; - if (!forDatabase && isTaggedEvent(msgData.event)) { - // This means we have to copy each event, so we can fix it up to - // set a correct 'age' parameter whilst keeping the local timestamp - // on our stored event. If this turns out to be a bottleneck, it could - // be optimised either by doing this in the main process after the data - // has been structured-cloned to go between the worker & main process, - // or special-casing data from saved syncs to read the local timestamp - // directly rather than turning it into age to then immediately be - // transformed back again into a local timestamp. - transformedEvent = Object.assign({}, msgData.event); - if (transformedEvent.unsigned !== undefined) { - transformedEvent.unsigned = Object.assign({}, transformedEvent.unsigned); - } - delete transformedEvent._localTs; - transformedEvent.unsigned = transformedEvent.unsigned || {}; - transformedEvent.unsigned.age = Date.now() - msgData.event._localTs; - } - else { - transformedEvent = msgData.event; - } - roomJson.timeline.events.push(transformedEvent); - }); - // Add state data: roll back current state to the start of timeline, - // by "reverse clobbering" from the end of the timeline to the start. - // Convert maps back into arrays. - const rollBackState = Object.create(null); - for (let i = roomJson.timeline.events.length - 1; i >= 0; i--) { - const timelineEvent = roomJson.timeline.events[i]; - if (timelineEvent.state_key === null || - timelineEvent.state_key === undefined) { - continue; // not a state event - } - // since we're going back in time, we need to use the previous - // state value else we'll break causality. We don't have the - // complete previous state event, so we need to create one. - const prevStateEvent = (0, utils_1.deepCopy)(timelineEvent); - if (prevStateEvent.unsigned) { - if (prevStateEvent.unsigned.prev_content) { - prevStateEvent.content = prevStateEvent.unsigned.prev_content; - } - if (prevStateEvent.unsigned.prev_sender) { - prevStateEvent.sender = prevStateEvent.unsigned.prev_sender; - } - } - setState(rollBackState, prevStateEvent); - } - Object.keys(roomData._currentState).forEach((evType) => { - Object.keys(roomData._currentState[evType]).forEach((stateKey) => { - let ev = roomData._currentState[evType][stateKey]; - if (rollBackState[evType] && rollBackState[evType][stateKey]) { - // use the reverse clobbered event instead. - ev = rollBackState[evType][stateKey]; - } - roomJson.state.events.push(ev); - }); - }); - data.join[roomId] = roomJson; - }); - // Add account data - const accData = []; - Object.keys(this.accountData).forEach((evType) => { - accData.push(this.accountData[evType]); - }); - return { - nextBatch: this.nextBatch, - roomsData: data, - accountData: accData, - }; - } - getNextBatchToken() { - return this.nextBatch; - } -} -exports.SyncAccumulator = SyncAccumulator; -function setState(eventMap, event) { - if (event.state_key === null || event.state_key === undefined || !event.type) { - return; - } - if (!eventMap[event.type]) { - eventMap[event.type] = Object.create(null); - } - eventMap[event.type][event.state_key] = event; -} - -},{"./@types/event":306,"./@types/read_receipts":311,"./@types/sync":314,"./logger":374,"./utils":416}],414:[function(require,module,exports){ -(function (global){(function (){ -"use strict"; -/* -Copyright 2015 - 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports._createAndReEmitRoom = exports.SyncApi = exports.defaultSyncApiOpts = exports.defaultClientOpts = exports.SyncState = void 0; -const user_1 = require("./models/user"); -const room_1 = require("./models/room"); -const utils = __importStar(require("./utils")); -const utils_1 = require("./utils"); -const filter_1 = require("./filter"); -const event_timeline_1 = require("./models/event-timeline"); -const logger_1 = require("./logger"); -const errors_1 = require("./errors"); -const client_1 = require("./client"); -const http_api_1 = require("./http-api"); -const event_1 = require("./@types/event"); -const room_state_1 = require("./models/room-state"); -const room_member_1 = require("./models/room-member"); -const beacon_1 = require("./models/beacon"); -const sync_1 = require("./@types/sync"); -const feature_1 = require("./feature"); -const DEBUG = true; -// /sync requests allow you to set a timeout= but the request may continue -// beyond that and wedge forever, so we need to track how long we are willing -// to keep open the connection. This constant is *ADDED* to the timeout= value -// to determine the max time we're willing to wait. -const BUFFER_PERIOD_MS = 80 * 1000; -// Number of consecutive failed syncs that will lead to a syncState of ERROR as opposed -// to RECONNECTING. This is needed to inform the client of server issues when the -// keepAlive is successful but the server /sync fails. -const FAILED_SYNC_ERROR_THRESHOLD = 3; -var SyncState; -(function (SyncState) { - /** Emitted after we try to sync more than `FAILED_SYNC_ERROR_THRESHOLD` - * times and are still failing. Or when we enounter a hard error like the - * token being invalid. */ - SyncState["Error"] = "ERROR"; - /** Emitted after the first sync events are ready (this could even be sync - * events from the cache) */ - SyncState["Prepared"] = "PREPARED"; - /** Emitted when the sync loop is no longer running */ - SyncState["Stopped"] = "STOPPED"; - /** Emitted after each sync request happens */ - SyncState["Syncing"] = "SYNCING"; - /** Emitted after a connectivity error and we're ready to start syncing again */ - SyncState["Catchup"] = "CATCHUP"; - /** Emitted for each time we try reconnecting. Will switch to `Error` after - * we reach the `FAILED_SYNC_ERROR_THRESHOLD` - */ - SyncState["Reconnecting"] = "RECONNECTING"; -})(SyncState = exports.SyncState || (exports.SyncState = {})); -// Room versions where "insertion", "batch", and "marker" events are controlled -// by power-levels. MSC2716 is supported in existing room versions but they -// should only have special meaning when the room creator sends them. -const MSC2716_ROOM_VERSIONS = ["org.matrix.msc2716v3"]; -function getFilterName(userId, suffix) { - // scope this on the user ID because people may login on many accounts - // and they all need to be stored! - return `FILTER_SYNC_${userId}` + (suffix ? "_" + suffix : ""); -} -/* istanbul ignore next */ -function debuglog(...params) { - if (!DEBUG) - return; - logger_1.logger.log(...params); -} -var SetPresence; -(function (SetPresence) { - SetPresence["Offline"] = "offline"; - SetPresence["Online"] = "online"; - SetPresence["Unavailable"] = "unavailable"; -})(SetPresence || (SetPresence = {})); -/** add default settings to an IStoredClientOpts */ -function defaultClientOpts(opts) { - return Object.assign({ initialSyncLimit: 8, resolveInvitesToProfiles: false, pollTimeout: 30 * 1000, pendingEventOrdering: client_1.PendingEventOrdering.Chronological, threadSupport: false }, opts); -} -exports.defaultClientOpts = defaultClientOpts; -function defaultSyncApiOpts(syncOpts) { - return Object.assign({ canResetEntireTimeline: (_roomId) => false }, syncOpts); -} -exports.defaultSyncApiOpts = defaultSyncApiOpts; -class SyncApi { - /** - * Construct an entity which is able to sync with a homeserver. - * @param client - The matrix client instance to use. - * @param opts - client config options - * @param syncOpts - sync-specific options passed by the client - * @internal - */ - constructor(client, opts, syncOpts) { - this.client = client; - this._peekRoom = null; - this.syncState = null; - this.catchingUp = false; - this.running = false; - this.notifEvents = []; // accumulator of sync events in the current sync response - this.failedSyncCount = 0; // Number of consecutive failed /sync requests - this.storeIsInvalid = false; // flag set if the store needs to be cleared before we can start - this.getPushRules = () => __awaiter(this, void 0, void 0, function* () { - try { - debuglog("Getting push rules..."); - const result = yield this.client.getPushRules(); - debuglog("Got push rules"); - this.client.pushRules = result; - } - catch (err) { - logger_1.logger.error("Getting push rules failed", err); - if (this.shouldAbortSync(err)) - return; - // wait for saved sync to complete before doing anything else, - // otherwise the sync state will end up being incorrect - debuglog("Waiting for saved sync before retrying push rules..."); - yield this.recoverFromSyncStartupError(this.savedSyncPromise, err); - return this.getPushRules(); // try again - } - }); - this.buildDefaultFilter = () => { - const filter = new filter_1.Filter(this.client.credentials.userId); - if (this.client.canSupport.get(feature_1.Feature.ThreadUnreadNotifications) !== feature_1.ServerSupport.Unsupported) { - filter.setUnreadThreadNotifications(true); - } - return filter; - }; - this.checkLazyLoadStatus = () => __awaiter(this, void 0, void 0, function* () { - var _a; - debuglog("Checking lazy load status..."); - if (this.opts.lazyLoadMembers && this.client.isGuest()) { - this.opts.lazyLoadMembers = false; - } - if (this.opts.lazyLoadMembers) { - debuglog("Checking server lazy load support..."); - const supported = yield this.client.doesServerSupportLazyLoading(); - if (supported) { - debuglog("Enabling lazy load on sync filter..."); - if (!this.opts.filter) { - this.opts.filter = this.buildDefaultFilter(); - } - this.opts.filter.setLazyLoadMembers(true); - } - else { - debuglog("LL: lazy loading requested but not supported " + "by server, so disabling"); - this.opts.lazyLoadMembers = false; - } - } - // need to vape the store when enabling LL and wasn't enabled before - debuglog("Checking whether lazy loading has changed in store..."); - const shouldClear = yield this.wasLazyLoadingToggled(this.opts.lazyLoadMembers); - if (shouldClear) { - this.storeIsInvalid = true; - const error = new errors_1.InvalidStoreError(errors_1.InvalidStoreState.ToggledLazyLoading, !!this.opts.lazyLoadMembers); - this.updateSyncState(SyncState.Error, { error }); - // bail out of the sync loop now: the app needs to respond to this error. - // we leave the state as 'ERROR' which isn't great since this normally means - // we're retrying. The client must be stopped before clearing the stores anyway - // so the app should stop the client, clear the store and start it again. - logger_1.logger.warn("InvalidStoreError: store is not usable: stopping sync."); - return; - } - if (this.opts.lazyLoadMembers) { - (_a = this.syncOpts.crypto) === null || _a === void 0 ? void 0 : _a.enableLazyLoading(); - } - try { - debuglog("Storing client options..."); - yield this.client.storeClientOptions(); - debuglog("Stored client options"); - } - catch (err) { - logger_1.logger.error("Storing client options failed", err); - throw err; - } - }); - this.getFilter = () => __awaiter(this, void 0, void 0, function* () { - debuglog("Getting filter..."); - let filter; - if (this.opts.filter) { - filter = this.opts.filter; - } - else { - filter = this.buildDefaultFilter(); - } - let filterId; - try { - filterId = yield this.client.getOrCreateFilter(getFilterName(this.client.credentials.userId), filter); - } - catch (err) { - logger_1.logger.error("Getting filter failed", err); - if (this.shouldAbortSync(err)) - return {}; - // wait for saved sync to complete before doing anything else, - // otherwise the sync state will end up being incorrect - debuglog("Waiting for saved sync before retrying filter..."); - yield this.recoverFromSyncStartupError(this.savedSyncPromise, err); - return this.getFilter(); // try again - } - return { filter, filterId }; - }); - /** - * Event handler for the 'online' event - * This event is generally unreliable and precise behaviour - * varies between browsers, so we poll for connectivity too, - * but this might help us reconnect a little faster. - */ - this.onOnline = () => { - debuglog("Browser thinks we are back online"); - this.startKeepAlives(0); - }; - this.opts = defaultClientOpts(opts); - this.syncOpts = defaultSyncApiOpts(syncOpts); - if (client.getNotifTimelineSet()) { - client.reEmitter.reEmit(client.getNotifTimelineSet(), [room_1.RoomEvent.Timeline, room_1.RoomEvent.TimelineReset]); - } - } - createRoom(roomId) { - const room = _createAndReEmitRoom(this.client, roomId, this.opts); - room.on(room_state_1.RoomStateEvent.Marker, (markerEvent, markerFoundOptions) => { - this.onMarkerStateEvent(room, markerEvent, markerFoundOptions); - }); - return room; - } - /** When we see the marker state change in the room, we know there is some - * new historical messages imported by MSC2716 `/batch_send` somewhere in - * the room and we need to throw away the timeline to make sure the - * historical messages are shown when we paginate `/messages` again. - * @param room - The room where the marker event was sent - * @param markerEvent - The new marker event - * @param setStateOptions - When `timelineWasEmpty` is set - * as `true`, the given marker event will be ignored - */ - onMarkerStateEvent(room, markerEvent, { timelineWasEmpty } = {}) { - // We don't need to refresh the timeline if it was empty before the - // marker arrived. This could be happen in a variety of cases: - // 1. From the initial sync - // 2. If it's from the first state we're seeing after joining the room - // 3. Or whether it's coming from `syncFromCache` - if (timelineWasEmpty) { - logger_1.logger.debug(`MarkerState: Ignoring markerEventId=${markerEvent.getId()} in roomId=${room.roomId} ` + - `because the timeline was empty before the marker arrived which means there is nothing to refresh.`); - return; - } - const isValidMsc2716Event = - // Check whether the room version directly supports MSC2716, in - // which case, "marker" events are already auth'ed by - // power_levels - MSC2716_ROOM_VERSIONS.includes(room.getVersion()) || - // MSC2716 is also supported in all existing room versions but - // special meaning should only be given to "insertion", "batch", - // and "marker" events when they come from the room creator - markerEvent.getSender() === room.getCreator(); - // It would be nice if we could also specifically tell whether the - // historical messages actually affected the locally cached client - // timeline or not. The problem is we can't see the prev_events of - // the base insertion event that the marker was pointing to because - // prev_events aren't available in the client API's. In most cases, - // the history won't be in people's locally cached timelines in the - // client, so we don't need to bother everyone about refreshing - // their timeline. This works for a v1 though and there are use - // cases like initially bootstrapping your bridged room where people - // are likely to encounter the historical messages affecting their - // current timeline (think someone signing up for Beeper and - // importing their Whatsapp history). - if (isValidMsc2716Event) { - // Saw new marker event, let's let the clients know they should - // refresh the timeline. - logger_1.logger.debug(`MarkerState: Timeline needs to be refreshed because ` + - `a new markerEventId=${markerEvent.getId()} was sent in roomId=${room.roomId}`); - room.setTimelineNeedsRefresh(true); - room.emit(room_1.RoomEvent.HistoryImportedWithinTimeline, markerEvent, room); - } - else { - logger_1.logger.debug(`MarkerState: Ignoring markerEventId=${markerEvent.getId()} in roomId=${room.roomId} because ` + - `MSC2716 is not supported in the room version or for any room version, the marker wasn't sent ` + - `by the room creator.`); - } - } - /** - * Sync rooms the user has left. - * @returns Resolved when they've been added to the store. - */ - syncLeftRooms() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const client = this.client; - // grab a filter with limit=1 and include_leave=true - const filter = new filter_1.Filter(this.client.credentials.userId); - filter.setTimelineLimit(1); - filter.setIncludeLeaveRooms(true); - const localTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS; - const filterId = yield client.getOrCreateFilter(getFilterName(client.credentials.userId, "LEFT_ROOMS"), filter); - const qps = { - timeout: 0, - filter: filterId, - }; - const data = yield client.http.authedRequest(http_api_1.Method.Get, "/sync", qps, undefined, { - localTimeoutMs, - }); - let leaveRooms = []; - if ((_a = data.rooms) === null || _a === void 0 ? void 0 : _a.leave) { - leaveRooms = this.mapSyncResponseToRoomArray(data.rooms.leave); - } - const rooms = yield Promise.all(leaveRooms.map((leaveObj) => __awaiter(this, void 0, void 0, function* () { - const room = leaveObj.room; - if (!leaveObj.isBrandNewRoom) { - // the intention behind syncLeftRooms is to add in rooms which were - // *omitted* from the initial /sync. Rooms the user were joined to - // but then left whilst the app is running will appear in this list - // and we do not want to bother with them since they will have the - // current state already (and may get dupe messages if we add - // yet more timeline events!), so skip them. - // NB: When we persist rooms to localStorage this will be more - // complicated... - return; - } - leaveObj.timeline = leaveObj.timeline || { - prev_batch: null, - events: [], - }; - const events = this.mapSyncEventsFormat(leaveObj.timeline, room); - const stateEvents = this.mapSyncEventsFormat(leaveObj.state, room); - // set the back-pagination token. Do this *before* adding any - // events so that clients can start back-paginating. - room.getLiveTimeline().setPaginationToken(leaveObj.timeline.prev_batch, event_timeline_1.EventTimeline.BACKWARDS); - yield this.injectRoomEvents(room, stateEvents, events); - room.recalculate(); - client.store.storeRoom(room); - client.emit(client_1.ClientEvent.Room, room); - this.processEventsForNotifs(room, events); - return room; - }))); - return rooms.filter(Boolean); - }); - } - /** - * Peek into a room. This will result in the room in question being synced so it - * is accessible via getRooms(). Live updates for the room will be provided. - * @param roomId - The room ID to peek into. - * @returns A promise which resolves once the room has been added to the - * store. - */ - peek(roomId) { - var _a; - if (((_a = this._peekRoom) === null || _a === void 0 ? void 0 : _a.roomId) === roomId) { - return Promise.resolve(this._peekRoom); - } - const client = this.client; - this._peekRoom = this.createRoom(roomId); - return this.client.roomInitialSync(roomId, 20).then((response) => { - // make sure things are init'd - response.messages = response.messages || { chunk: [] }; - response.messages.chunk = response.messages.chunk || []; - response.state = response.state || []; - // FIXME: Mostly duplicated from injectRoomEvents but not entirely - // because "state" in this API is at the BEGINNING of the chunk - const oldStateEvents = utils.deepCopy(response.state).map(client.getEventMapper()); - const stateEvents = response.state.map(client.getEventMapper()); - const messages = response.messages.chunk.map(client.getEventMapper()); - // XXX: copypasted from /sync until we kill off this minging v1 API stuff) - // handle presence events (User objects) - if (Array.isArray(response.presence)) { - response.presence.map(client.getEventMapper()).forEach(function (presenceEvent) { - let user = client.store.getUser(presenceEvent.getContent().user_id); - if (user) { - user.setPresenceEvent(presenceEvent); - } - else { - user = createNewUser(client, presenceEvent.getContent().user_id); - user.setPresenceEvent(presenceEvent); - client.store.storeUser(user); - } - client.emit(client_1.ClientEvent.Event, presenceEvent); - }); - } - // set the pagination token before adding the events in case people - // fire off pagination requests in response to the Room.timeline - // events. - if (response.messages.start) { - this._peekRoom.oldState.paginationToken = response.messages.start; - } - // set the state of the room to as it was after the timeline executes - this._peekRoom.oldState.setStateEvents(oldStateEvents); - this._peekRoom.currentState.setStateEvents(stateEvents); - this.resolveInvites(this._peekRoom); - this._peekRoom.recalculate(); - // roll backwards to diverge old state. addEventsToTimeline - // will overwrite the pagination token, so make sure it overwrites - // it with the right thing. - this._peekRoom.addEventsToTimeline(messages.reverse(), true, this._peekRoom.getLiveTimeline(), response.messages.start); - client.store.storeRoom(this._peekRoom); - client.emit(client_1.ClientEvent.Room, this._peekRoom); - this.peekPoll(this._peekRoom); - return this._peekRoom; - }); - } - /** - * Stop polling for updates in the peeked room. NOPs if there is no room being - * peeked. - */ - stopPeeking() { - this._peekRoom = null; - } - /** - * Do a peek room poll. - * @param token - from= token - */ - peekPoll(peekRoom, token) { - var _a; - if (this._peekRoom !== peekRoom) { - debuglog("Stopped peeking in room %s", peekRoom.roomId); - return; - } - // FIXME: gut wrenching; hard-coded timeout values - this.client.http - .authedRequest(http_api_1.Method.Get, "/events", { - room_id: peekRoom.roomId, - timeout: String(30 * 1000), - from: token, - }, undefined, { - localTimeoutMs: 50 * 1000, - abortSignal: (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal, - }) - .then((res) => { - if (this._peekRoom !== peekRoom) { - debuglog("Stopped peeking in room %s", peekRoom.roomId); - return; - } - // We have a problem that we get presence both from /events and /sync - // however, /sync only returns presence for users in rooms - // you're actually joined to. - // in order to be sure to get presence for all of the users in the - // peeked room, we handle presence explicitly here. This may result - // in duplicate presence events firing for some users, which is a - // performance drain, but such is life. - // XXX: copypasted from /sync until we can kill this minging v1 stuff. - res.chunk - .filter(function (e) { - return e.type === "m.presence"; - }) - .map(this.client.getEventMapper()) - .forEach((presenceEvent) => { - let user = this.client.store.getUser(presenceEvent.getContent().user_id); - if (user) { - user.setPresenceEvent(presenceEvent); - } - else { - user = createNewUser(this.client, presenceEvent.getContent().user_id); - user.setPresenceEvent(presenceEvent); - this.client.store.storeUser(user); - } - this.client.emit(client_1.ClientEvent.Event, presenceEvent); - }); - // strip out events which aren't for the given room_id (e.g presence) - // and also ephemeral events (which we're assuming is anything without - // and event ID because the /events API doesn't separate them). - const events = res.chunk - .filter(function (e) { - return e.room_id === peekRoom.roomId && e.event_id; - }) - .map(this.client.getEventMapper()); - peekRoom.addLiveEvents(events); - this.peekPoll(peekRoom, res.end); - }, (err) => { - logger_1.logger.error("[%s] Peek poll failed: %s", peekRoom.roomId, err); - setTimeout(() => { - this.peekPoll(peekRoom, token); - }, 30 * 1000); - }); - } - /** - * Returns the current state of this sync object - * @see MatrixClient#event:"sync" - */ - getSyncState() { - return this.syncState; - } - /** - * Returns the additional data object associated with - * the current sync state, or null if there is no - * such data. - * Sync errors, if available, are put in the 'error' key of - * this object. - */ - getSyncStateData() { - var _a; - return (_a = this.syncStateData) !== null && _a !== void 0 ? _a : null; - } - recoverFromSyncStartupError(savedSyncPromise, error) { - return __awaiter(this, void 0, void 0, function* () { - // Wait for the saved sync to complete - we send the pushrules and filter requests - // before the saved sync has finished so they can run in parallel, but only process - // the results after the saved sync is done. Equivalently, we wait for it to finish - // before reporting failures from these functions. - yield savedSyncPromise; - const keepaliveProm = this.startKeepAlives(); - this.updateSyncState(SyncState.Error, { error }); - yield keepaliveProm; - }); - } - /** - * Is the lazy loading option different than in previous session? - * @param lazyLoadMembers - current options for lazy loading - * @returns whether or not the option has changed compared to the previous session */ - wasLazyLoadingToggled(lazyLoadMembers = false) { - return __awaiter(this, void 0, void 0, function* () { - // assume it was turned off before - // if we don't know any better - let lazyLoadMembersBefore = false; - const isStoreNewlyCreated = yield this.client.store.isNewlyCreated(); - if (!isStoreNewlyCreated) { - const prevClientOptions = yield this.client.store.getClientOptions(); - if (prevClientOptions) { - lazyLoadMembersBefore = !!prevClientOptions.lazyLoadMembers; - } - return lazyLoadMembersBefore !== lazyLoadMembers; - } - return false; - }); - } - shouldAbortSync(error) { - if (error.errcode === "M_UNKNOWN_TOKEN") { - // The logout already happened, we just need to stop. - logger_1.logger.warn("Token no longer valid - assuming logout"); - this.stop(); - this.updateSyncState(SyncState.Error, { error }); - return true; - } - return false; - } - /** - * Main entry point - */ - sync() { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - this.running = true; - this.abortController = new AbortController(); - (_b = (_a = global.window) === null || _a === void 0 ? void 0 : _a.addEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "online", this.onOnline, false); - if (this.client.isGuest()) { - // no push rules for guests, no access to POST filter for guests. - return this.doSync({}); - } - // Pull the saved sync token out first, before the worker starts sending - // all the sync data which could take a while. This will let us send our - // first incremental sync request before we've processed our saved data. - debuglog("Getting saved sync token..."); - const savedSyncTokenPromise = this.client.store.getSavedSyncToken().then((tok) => { - debuglog("Got saved sync token"); - return tok; - }); - this.savedSyncPromise = this.client.store - .getSavedSync() - .then((savedSync) => { - debuglog(`Got reply from saved sync, exists? ${!!savedSync}`); - if (savedSync) { - return this.syncFromCache(savedSync); - } - }) - .catch((err) => { - logger_1.logger.error("Getting saved sync failed", err); - }); - // We need to do one-off checks before we can begin the /sync loop. - // These are: - // 1) We need to get push rules so we can check if events should bing as we get - // them from /sync. - // 2) We need to get/create a filter which we can use for /sync. - // 3) We need to check the lazy loading option matches what was used in the - // stored sync. If it doesn't, we can't use the stored sync. - // Now start the first incremental sync request: this can also - // take a while so if we set it going now, we can wait for it - // to finish while we process our saved sync data. - yield this.getPushRules(); - yield this.checkLazyLoadStatus(); - const { filterId, filter } = yield this.getFilter(); - if (!filter) - return; // bail, getFilter failed - // reset the notifications timeline to prepare it to paginate from - // the current point in time. - // The right solution would be to tie /sync pagination tokens into - // /notifications API somehow. - this.client.resetNotifTimelineSet(); - if (!this.currentSyncRequest) { - let firstSyncFilter = filterId; - const savedSyncToken = yield savedSyncTokenPromise; - if (savedSyncToken) { - debuglog("Sending first sync request..."); - } - else { - debuglog("Sending initial sync request..."); - const initialFilter = this.buildDefaultFilter(); - initialFilter.setDefinition(filter.getDefinition()); - initialFilter.setTimelineLimit(this.opts.initialSyncLimit); - // Use an inline filter, no point uploading it for a single usage - firstSyncFilter = JSON.stringify(initialFilter.getDefinition()); - } - // Send this first sync request here so we can then wait for the saved - // sync data to finish processing before we process the results of this one. - this.currentSyncRequest = this.doSyncRequest({ filter: firstSyncFilter }, savedSyncToken); - } - // Now wait for the saved sync to finish... - debuglog("Waiting for saved sync before starting sync processing..."); - yield this.savedSyncPromise; - // process the first sync request and continue syncing with the normal filterId - return this.doSync({ filter: filterId }); - }); - } - /** - * Stops the sync object from syncing. - */ - stop() { - var _a, _b, _c; - debuglog("SyncApi.stop"); - // It is necessary to check for the existance of - // global.window AND global.window.removeEventListener. - // Some platforms (e.g. React Native) register global.window, - // but do not have global.window.removeEventListener. - (_b = (_a = global.window) === null || _a === void 0 ? void 0 : _a.removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "online", this.onOnline, false); - this.running = false; - (_c = this.abortController) === null || _c === void 0 ? void 0 : _c.abort(); - if (this.keepAliveTimer) { - clearTimeout(this.keepAliveTimer); - this.keepAliveTimer = undefined; - } - } - /** - * Retry a backed off syncing request immediately. This should only be used when - * the user explicitly attempts to retry their lost connection. - * @returns True if this resulted in a request being retried. - */ - retryImmediately() { - if (!this.connectionReturnedDefer) { - return false; - } - this.startKeepAlives(0); - return true; - } - /** - * Process a single set of cached sync data. - * @param savedSync - a saved sync that was persisted by a store. This - * should have been acquired via client.store.getSavedSync(). - */ - syncFromCache(savedSync) { - return __awaiter(this, void 0, void 0, function* () { - debuglog("sync(): not doing HTTP hit, instead returning stored /sync data"); - const nextSyncToken = savedSync.nextBatch; - // Set sync token for future incremental syncing - this.client.store.setSyncToken(nextSyncToken); - // No previous sync, set old token to null - const syncEventData = { - nextSyncToken, - catchingUp: false, - fromCache: true, - }; - const data = { - next_batch: nextSyncToken, - rooms: savedSync.roomsData, - account_data: { - events: savedSync.accountData, - }, - }; - try { - yield this.processSyncResponse(syncEventData, data); - } - catch (e) { - logger_1.logger.error("Error processing cached sync", e); - } - // Don't emit a prepared if we've bailed because the store is invalid: - // in this case the client will not be usable until stopped & restarted - // so this would be useless and misleading. - if (!this.storeIsInvalid) { - this.updateSyncState(SyncState.Prepared, syncEventData); - } - }); - } - /** - * Invoke me to do /sync calls - */ - doSync(syncOptions) { - return __awaiter(this, void 0, void 0, function* () { - while (this.running) { - const syncToken = this.client.store.getSyncToken(); - let data; - try { - if (!this.currentSyncRequest) { - this.currentSyncRequest = this.doSyncRequest(syncOptions, syncToken); - } - data = yield this.currentSyncRequest; - } - catch (e) { - const abort = yield this.onSyncError(e); - if (abort) - return; - continue; - } - finally { - this.currentSyncRequest = undefined; - } - // set the sync token NOW *before* processing the events. We do this so - // if something barfs on an event we can skip it rather than constantly - // polling with the same token. - this.client.store.setSyncToken(data.next_batch); - // Reset after a successful sync - this.failedSyncCount = 0; - yield this.client.store.setSyncData(data); - const syncEventData = { - oldSyncToken: syncToken !== null && syncToken !== void 0 ? syncToken : undefined, - nextSyncToken: data.next_batch, - catchingUp: this.catchingUp, - }; - if (this.syncOpts.crypto) { - // tell the crypto module we're about to process a sync - // response - yield this.syncOpts.crypto.onSyncWillProcess(syncEventData); - } - try { - yield this.processSyncResponse(syncEventData, data); - } - catch (e) { - // log the exception with stack if we have it, else fall back - // to the plain description - logger_1.logger.error("Caught /sync error", e); - // Emit the exception for client handling - this.client.emit(client_1.ClientEvent.SyncUnexpectedError, e); - } - // update this as it may have changed - syncEventData.catchingUp = this.catchingUp; - // emit synced events - if (!syncOptions.hasSyncedBefore) { - this.updateSyncState(SyncState.Prepared, syncEventData); - syncOptions.hasSyncedBefore = true; - } - // tell the crypto module to do its processing. It may block (to do a - // /keys/changes request). - if (this.syncOpts.cryptoCallbacks) { - yield this.syncOpts.cryptoCallbacks.onSyncCompleted(syncEventData); - } - // keep emitting SYNCING -> SYNCING for clients who want to do bulk updates - this.updateSyncState(SyncState.Syncing, syncEventData); - if (this.client.store.wantsSave()) { - // We always save the device list (if it's dirty) before saving the sync data: - // this means we know the saved device list data is at least as fresh as the - // stored sync data which means we don't have to worry that we may have missed - // device changes. We can also skip the delay since we're not calling this very - // frequently (and we don't really want to delay the sync for it). - if (this.syncOpts.crypto) { - yield this.syncOpts.crypto.saveDeviceList(0); - } - // tell databases that everything is now in a consistent state and can be saved. - this.client.store.save(); - } - } - if (!this.running) { - debuglog("Sync no longer running: exiting."); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.reject(); - this.connectionReturnedDefer = undefined; - } - this.updateSyncState(SyncState.Stopped); - } - }); - } - doSyncRequest(syncOptions, syncToken) { - var _a; - const qps = this.getSyncParams(syncOptions, syncToken); - return this.client.http.authedRequest(http_api_1.Method.Get, "/sync", qps, undefined, { - localTimeoutMs: qps.timeout + BUFFER_PERIOD_MS, - abortSignal: (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal, - }); - } - getSyncParams(syncOptions, syncToken) { - let timeout = this.opts.pollTimeout; - if (this.getSyncState() !== SyncState.Syncing || this.catchingUp) { - // unless we are happily syncing already, we want the server to return - // as quickly as possible, even if there are no events queued. This - // serves two purposes: - // - // * When the connection dies, we want to know asap when it comes back, - // so that we can hide the error from the user. (We don't want to - // have to wait for an event or a timeout). - // - // * We want to know if the server has any to_device messages queued up - // for us. We do that by calling it with a zero timeout until it - // doesn't give us any more to_device messages. - this.catchingUp = true; - timeout = 0; - } - let filter = syncOptions.filter; - if (this.client.isGuest() && !filter) { - filter = this.getGuestFilter(); - } - const qps = { filter, timeout }; - if (this.opts.disablePresence) { - qps.set_presence = SetPresence.Offline; - } - if (syncToken) { - qps.since = syncToken; - } - else { - // use a cachebuster for initialsyncs, to make sure that - // we don't get a stale sync - // (https://github.com/vector-im/vector-web/issues/1354) - qps._cacheBuster = Date.now(); - } - if ([SyncState.Reconnecting, SyncState.Error].includes(this.getSyncState())) { - // we think the connection is dead. If it comes back up, we won't know - // about it till /sync returns. If the timeout= is high, this could - // be a long time. Set it to 0 when doing retries so we don't have to wait - // for an event or a timeout before emiting the SYNCING event. - qps.timeout = 0; - } - return qps; - } - onSyncError(err) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.running) { - debuglog("Sync no longer running: exiting"); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.reject(); - this.connectionReturnedDefer = undefined; - } - this.updateSyncState(SyncState.Stopped); - return true; // abort - } - logger_1.logger.error("/sync error %s", err); - if (this.shouldAbortSync(err)) { - return true; // abort - } - this.failedSyncCount++; - logger_1.logger.log("Number of consecutive failed sync requests:", this.failedSyncCount); - debuglog("Starting keep-alive"); - // Note that we do *not* mark the sync connection as - // lost yet: we only do this if a keepalive poke - // fails, since long lived HTTP connections will - // go away sometimes and we shouldn't treat this as - // erroneous. We set the state to 'reconnecting' - // instead, so that clients can observe this state - // if they wish. - const keepAlivePromise = this.startKeepAlives(); - this.currentSyncRequest = undefined; - // Transition from RECONNECTING to ERROR after a given number of failed syncs - this.updateSyncState(this.failedSyncCount >= FAILED_SYNC_ERROR_THRESHOLD ? SyncState.Error : SyncState.Reconnecting, { error: err }); - const connDidFail = yield keepAlivePromise; - // Only emit CATCHUP if we detected a connectivity error: if we didn't, - // it's quite likely the sync will fail again for the same reason and we - // want to stay in ERROR rather than keep flip-flopping between ERROR - // and CATCHUP. - if (connDidFail && this.getSyncState() === SyncState.Error) { - this.updateSyncState(SyncState.Catchup, { - catchingUp: true, - }); - } - return false; - }); - } - /** - * Process data returned from a sync response and propagate it - * into the model objects - * - * @param syncEventData - Object containing sync tokens associated with this sync - * @param data - The response from /sync - */ - processSyncResponse(syncEventData, data) { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - const client = this.client; - // data looks like: - // { - // next_batch: $token, - // presence: { events: [] }, - // account_data: { events: [] }, - // device_lists: { changed: ["@user:server", ... ]}, - // to_device: { events: [] }, - // device_one_time_keys_count: { signed_curve25519: 42 }, - // rooms: { - // invite: { - // $roomid: { - // invite_state: { events: [] } - // } - // }, - // join: { - // $roomid: { - // state: { events: [] }, - // timeline: { events: [], prev_batch: $token, limited: true }, - // ephemeral: { events: [] }, - // summary: { - // m.heroes: [ $user_id ], - // m.joined_member_count: $count, - // m.invited_member_count: $count - // }, - // account_data: { events: [] }, - // unread_notifications: { - // highlight_count: 0, - // notification_count: 0, - // } - // } - // }, - // leave: { - // $roomid: { - // state: { events: [] }, - // timeline: { events: [], prev_batch: $token } - // } - // } - // } - // } - // TODO-arch: - // - Each event we pass through needs to be emitted via 'event', can we - // do this in one place? - // - The isBrandNewRoom boilerplate is boilerplatey. - // handle presence events (User objects) - if (Array.isArray((_a = data.presence) === null || _a === void 0 ? void 0 : _a.events)) { - data.presence.events.filter(utils_1.noUnsafeEventProps) - .map(client.getEventMapper()) - .forEach(function (presenceEvent) { - let user = client.store.getUser(presenceEvent.getSender()); - if (user) { - user.setPresenceEvent(presenceEvent); - } - else { - user = createNewUser(client, presenceEvent.getSender()); - user.setPresenceEvent(presenceEvent); - client.store.storeUser(user); - } - client.emit(client_1.ClientEvent.Event, presenceEvent); - }); - } - // handle non-room account_data - if (Array.isArray((_b = data.account_data) === null || _b === void 0 ? void 0 : _b.events)) { - const events = data.account_data.events.filter(utils_1.noUnsafeEventProps).map(client.getEventMapper()); - const prevEventsMap = events.reduce((m, c) => { - m[c.getType()] = client.store.getAccountData(c.getType()); - return m; - }, {}); - client.store.storeAccountDataEvents(events); - events.forEach(function (accountDataEvent) { - // Honour push rules that come down the sync stream but also - // honour push rules that were previously cached. Base rules - // will be updated when we receive push rules via getPushRules - // (see sync) before syncing over the network. - if (accountDataEvent.getType() === event_1.EventType.PushRules) { - const rules = accountDataEvent.getContent(); - client.setPushRules(rules); - } - const prevEvent = prevEventsMap[accountDataEvent.getType()]; - client.emit(client_1.ClientEvent.AccountData, accountDataEvent, prevEvent); - return accountDataEvent; - }); - } - // handle to-device events - if (data.to_device && Array.isArray(data.to_device.events) && data.to_device.events.length > 0) { - let toDeviceMessages = data.to_device.events.filter(utils_1.noUnsafeEventProps); - if (this.syncOpts.cryptoCallbacks) { - toDeviceMessages = yield this.syncOpts.cryptoCallbacks.preprocessToDeviceMessages(toDeviceMessages); - } - const cancelledKeyVerificationTxns = []; - toDeviceMessages - .map(client.getEventMapper({ toDevice: true })) - .map((toDeviceEvent) => { - // map is a cheap inline forEach - // We want to flag m.key.verification.start events as cancelled - // if there's an accompanying m.key.verification.cancel event, so - // we pull out the transaction IDs from the cancellation events - // so we can flag the verification events as cancelled in the loop - // below. - if (toDeviceEvent.getType() === "m.key.verification.cancel") { - const txnId = toDeviceEvent.getContent()["transaction_id"]; - if (txnId) { - cancelledKeyVerificationTxns.push(txnId); - } - } - // as mentioned above, .map is a cheap inline forEach, so return - // the unmodified event. - return toDeviceEvent; - }) - .forEach(function (toDeviceEvent) { - const content = toDeviceEvent.getContent(); - if (toDeviceEvent.getType() == "m.room.message" && content.msgtype == "m.bad.encrypted") { - // the mapper already logged a warning. - logger_1.logger.log("Ignoring undecryptable to-device event from " + toDeviceEvent.getSender()); - return; - } - if (toDeviceEvent.getType() === "m.key.verification.start" || - toDeviceEvent.getType() === "m.key.verification.request") { - const txnId = content["transaction_id"]; - if (cancelledKeyVerificationTxns.includes(txnId)) { - toDeviceEvent.flagCancelled(); - } - } - client.emit(client_1.ClientEvent.ToDeviceEvent, toDeviceEvent); - }); - } - else { - // no more to-device events: we can stop polling with a short timeout. - this.catchingUp = false; - } - // the returned json structure is a bit crap, so make it into a - // nicer form (array) after applying sanity to make sure we don't fail - // on missing keys (on the off chance) - let inviteRooms = []; - let joinRooms = []; - let leaveRooms = []; - if (data.rooms) { - if (data.rooms.invite) { - inviteRooms = this.mapSyncResponseToRoomArray(data.rooms.invite); - } - if (data.rooms.join) { - joinRooms = this.mapSyncResponseToRoomArray(data.rooms.join); - } - if (data.rooms.leave) { - leaveRooms = this.mapSyncResponseToRoomArray(data.rooms.leave); - } - } - this.notifEvents = []; - // Handle invites - yield utils.promiseMapSeries(inviteRooms, (inviteObj) => __awaiter(this, void 0, void 0, function* () { - var _e; - const room = inviteObj.room; - const stateEvents = this.mapSyncEventsFormat(inviteObj.invite_state, room); - yield this.injectRoomEvents(room, stateEvents); - const inviter = (_e = room.currentState.getStateEvents(event_1.EventType.RoomMember, client.getUserId())) === null || _e === void 0 ? void 0 : _e.getSender(); - const crypto = client.crypto; - if (crypto) { - const parkedHistory = yield crypto.cryptoStore.takeParkedSharedHistory(room.roomId); - for (const parked of parkedHistory) { - if (parked.senderId === inviter) { - yield crypto.olmDevice.addInboundGroupSession(room.roomId, parked.senderKey, parked.forwardingCurve25519KeyChain, parked.sessionId, parked.sessionKey, parked.keysClaimed, true, { sharedHistory: true, untrusted: true }); - } - } - } - if (inviteObj.isBrandNewRoom) { - room.recalculate(); - client.store.storeRoom(room); - client.emit(client_1.ClientEvent.Room, room); - } - else { - // Update room state for invite->reject->invite cycles - room.recalculate(); - } - stateEvents.forEach(function (e) { - client.emit(client_1.ClientEvent.Event, e); - }); - })); - // Handle joins - yield utils.promiseMapSeries(joinRooms, (joinObj) => __awaiter(this, void 0, void 0, function* () { - var _f, _g, _h, _j, _k, _l; - const room = joinObj.room; - const stateEvents = this.mapSyncEventsFormat(joinObj.state, room); - // Prevent events from being decrypted ahead of time - // this helps large account to speed up faster - // room::decryptCriticalEvent is in charge of decrypting all the events - // required for a client to function properly - const events = this.mapSyncEventsFormat(joinObj.timeline, room, false); - const ephemeralEvents = this.mapSyncEventsFormat(joinObj.ephemeral); - const accountDataEvents = this.mapSyncEventsFormat(joinObj.account_data); - const encrypted = client.isRoomEncrypted(room.roomId); - // We store the server-provided value first so it's correct when any of the events fire. - if (joinObj.unread_notifications) { - /** - * We track unread notifications ourselves in encrypted rooms, so don't - * bother setting it here. We trust our calculations better than the - * server's for this case, and therefore will assume that our non-zero - * count is accurate. - * - * @see import("./client").fixNotificationCountOnDecryption - */ - if (!encrypted || joinObj.unread_notifications.notification_count === 0) { - // In an encrypted room, if the room has notifications enabled then it's typical for - // the server to flag all new messages as notifying. However, some push rules calculate - // events as ignored based on their event contents (e.g. ignoring msgtype=m.notice messages) - // so we want to calculate this figure on the client in all cases. - room.setUnreadNotificationCount(room_1.NotificationCountType.Total, (_f = joinObj.unread_notifications.notification_count) !== null && _f !== void 0 ? _f : 0); - } - if (!encrypted || room.getUnreadNotificationCount(room_1.NotificationCountType.Highlight) <= 0) { - // If the locally stored highlight count is zero, use the server provided value. - room.setUnreadNotificationCount(room_1.NotificationCountType.Highlight, (_g = joinObj.unread_notifications.highlight_count) !== null && _g !== void 0 ? _g : 0); - } - } - const unreadThreadNotifications = (_h = joinObj[sync_1.UNREAD_THREAD_NOTIFICATIONS.name]) !== null && _h !== void 0 ? _h : joinObj[sync_1.UNREAD_THREAD_NOTIFICATIONS.altName]; - if (unreadThreadNotifications) { - // Only partially reset unread notification - // We want to keep the client-generated count. Particularly important - // for encrypted room that refresh their notification count on event - // decryption - room.resetThreadUnreadNotificationCount(Object.keys(unreadThreadNotifications)); - for (const [threadId, unreadNotification] of Object.entries(unreadThreadNotifications)) { - if (!encrypted || unreadNotification.notification_count === 0) { - room.setThreadUnreadNotificationCount(threadId, room_1.NotificationCountType.Total, (_j = unreadNotification.notification_count) !== null && _j !== void 0 ? _j : 0); - } - const hasNoNotifications = room.getThreadUnreadNotificationCount(threadId, room_1.NotificationCountType.Highlight) <= 0; - if (!encrypted || (encrypted && hasNoNotifications)) { - room.setThreadUnreadNotificationCount(threadId, room_1.NotificationCountType.Highlight, (_k = unreadNotification.highlight_count) !== null && _k !== void 0 ? _k : 0); - } - } - } - else { - room.resetThreadUnreadNotificationCount(); - } - joinObj.timeline = joinObj.timeline || {}; - if (joinObj.isBrandNewRoom) { - // set the back-pagination token. Do this *before* adding any - // events so that clients can start back-paginating. - if (joinObj.timeline.prev_batch !== null) { - room.getLiveTimeline().setPaginationToken(joinObj.timeline.prev_batch, event_timeline_1.EventTimeline.BACKWARDS); - } - } - else if (joinObj.timeline.limited) { - let limited = true; - // we've got a limited sync, so we *probably* have a gap in the - // timeline, so should reset. But we might have been peeking or - // paginating and already have some of the events, in which - // case we just want to append any subsequent events to the end - // of the existing timeline. - // - // This is particularly important in the case that we already have - // *all* of the events in the timeline - in that case, if we reset - // the timeline, we'll end up with an entirely empty timeline, - // which we'll try to paginate but not get any new events (which - // will stop us linking the empty timeline into the chain). - // - for (let i = events.length - 1; i >= 0; i--) { - const eventId = events[i].getId(); - if (room.getTimelineForEvent(eventId)) { - debuglog(`Already have event ${eventId} in limited sync - not resetting`); - limited = false; - // we might still be missing some of the events before i; - // we don't want to be adding them to the end of the - // timeline because that would put them out of order. - events.splice(0, i); - // XXX: there's a problem here if the skipped part of the - // timeline modifies the state set in stateEvents, because - // we'll end up using the state from stateEvents rather - // than the later state from timelineEvents. We probably - // need to wind stateEvents forward over the events we're - // skipping. - break; - } - } - if (limited) { - room.resetLiveTimeline(joinObj.timeline.prev_batch, this.syncOpts.canResetEntireTimeline(room.roomId) ? null : (_l = syncEventData.oldSyncToken) !== null && _l !== void 0 ? _l : null); - // We have to assume any gap in any timeline is - // reason to stop incrementally tracking notifications and - // reset the timeline. - client.resetNotifTimelineSet(); - } - } - // process any crypto events *before* emitting the RoomStateEvent events. This - // avoids a race condition if the application tries to send a message after the - // state event is processed, but before crypto is enabled, which then causes the - // crypto layer to complain. - if (this.syncOpts.cryptoCallbacks) { - for (const e of stateEvents.concat(events)) { - if (e.isState() && e.getType() === event_1.EventType.RoomEncryption && e.getStateKey() === "") { - yield this.syncOpts.cryptoCallbacks.onCryptoEvent(room, e); - } - } - } - try { - yield this.injectRoomEvents(room, stateEvents, events, syncEventData.fromCache); - } - catch (e) { - logger_1.logger.error(`Failed to process events on room ${room.roomId}:`, e); - } - // set summary after processing events, - // because it will trigger a name calculation - // which needs the room state to be up to date - if (joinObj.summary) { - room.setSummary(joinObj.summary); - } - // we deliberately don't add ephemeral events to the timeline - room.addEphemeralEvents(ephemeralEvents); - // we deliberately don't add accountData to the timeline - room.addAccountData(accountDataEvents); - room.recalculate(); - if (joinObj.isBrandNewRoom) { - client.store.storeRoom(room); - client.emit(client_1.ClientEvent.Room, room); - } - this.processEventsForNotifs(room, events); - const emitEvent = (e) => client.emit(client_1.ClientEvent.Event, e); - stateEvents.forEach(emitEvent); - events.forEach(emitEvent); - ephemeralEvents.forEach(emitEvent); - accountDataEvents.forEach(emitEvent); - // Decrypt only the last message in all rooms to make sure we can generate a preview - // And decrypt all events after the recorded read receipt to ensure an accurate - // notification count - room.decryptCriticalEvents(); - })); - // Handle leaves (e.g. kicked rooms) - yield utils.promiseMapSeries(leaveRooms, (leaveObj) => __awaiter(this, void 0, void 0, function* () { - const room = leaveObj.room; - const stateEvents = this.mapSyncEventsFormat(leaveObj.state, room); - const events = this.mapSyncEventsFormat(leaveObj.timeline, room); - const accountDataEvents = this.mapSyncEventsFormat(leaveObj.account_data); - yield this.injectRoomEvents(room, stateEvents, events); - room.addAccountData(accountDataEvents); - room.recalculate(); - if (leaveObj.isBrandNewRoom) { - client.store.storeRoom(room); - client.emit(client_1.ClientEvent.Room, room); - } - this.processEventsForNotifs(room, events); - stateEvents.forEach(function (e) { - client.emit(client_1.ClientEvent.Event, e); - }); - events.forEach(function (e) { - client.emit(client_1.ClientEvent.Event, e); - }); - accountDataEvents.forEach(function (e) { - client.emit(client_1.ClientEvent.Event, e); - }); - })); - // update the notification timeline, if appropriate. - // we only do this for live events, as otherwise we can't order them sanely - // in the timeline relative to ones paginated in by /notifications. - // XXX: we could fix this by making EventTimeline support chronological - // ordering... but it doesn't, right now. - if (syncEventData.oldSyncToken && this.notifEvents.length) { - this.notifEvents.sort(function (a, b) { - return a.getTs() - b.getTs(); - }); - this.notifEvents.forEach(function (event) { - var _a; - (_a = client.getNotifTimelineSet()) === null || _a === void 0 ? void 0 : _a.addLiveEvent(event); - }); - } - // Handle device list updates - if (data.device_lists) { - if (this.syncOpts.crypto) { - yield this.syncOpts.crypto.handleDeviceListChanges(syncEventData, data.device_lists); - } - else { - // FIXME if we *don't* have a crypto module, we still need to - // invalidate the device lists. But that would require a - // substantial bit of rework :/. - } - } - // Handle one_time_keys_count - if (data.device_one_time_keys_count) { - const map = new Map(Object.entries(data.device_one_time_keys_count)); - (_c = this.syncOpts.cryptoCallbacks) === null || _c === void 0 ? void 0 : _c.preprocessOneTimeKeyCounts(map); - } - if (data.device_unused_fallback_key_types || data["org.matrix.msc2732.device_unused_fallback_key_types"]) { - // The presence of device_unused_fallback_key_types indicates that the - // server supports fallback keys. If there's no unused - // signed_curve25519 fallback key we need a new one. - const unusedFallbackKeys = data.device_unused_fallback_key_types || data["org.matrix.msc2732.device_unused_fallback_key_types"]; - (_d = this.syncOpts.cryptoCallbacks) === null || _d === void 0 ? void 0 : _d.preprocessUnusedFallbackKeys(new Set(unusedFallbackKeys || null)); - } - }); - } - /** - * Starts polling the connectivity check endpoint - * @param delay - How long to delay until the first poll. - * defaults to a short, randomised interval (to prevent - * tight-looping if /versions succeeds but /sync etc. fail). - * @returns which resolves once the connection returns - */ - startKeepAlives(delay) { - if (delay === undefined) { - delay = 2000 + Math.floor(Math.random() * 5000); - } - if (this.keepAliveTimer !== null) { - clearTimeout(this.keepAliveTimer); - } - if (delay > 0) { - this.keepAliveTimer = setTimeout(this.pokeKeepAlive.bind(this), delay); - } - else { - this.pokeKeepAlive(); - } - if (!this.connectionReturnedDefer) { - this.connectionReturnedDefer = utils.defer(); - } - return this.connectionReturnedDefer.promise; - } - /** - * Make a dummy call to /_matrix/client/versions, to see if the HS is - * reachable. - * - * On failure, schedules a call back to itself. On success, resolves - * this.connectionReturnedDefer. - * - * @param connDidFail - True if a connectivity failure has been detected. Optional. - */ - pokeKeepAlive(connDidFail = false) { - var _a; - const success = () => { - clearTimeout(this.keepAliveTimer); - if (this.connectionReturnedDefer) { - this.connectionReturnedDefer.resolve(connDidFail); - this.connectionReturnedDefer = undefined; - } - }; - this.client.http - .request(http_api_1.Method.Get, "/_matrix/client/versions", undefined, // queryParams - undefined, // data - { - prefix: "", - localTimeoutMs: 15 * 1000, - abortSignal: (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal, - }) - .then(() => { - success(); - }, (err) => { - if (err.httpStatus == 400 || err.httpStatus == 404) { - // treat this as a success because the server probably just doesn't - // support /versions: point is, we're getting a response. - // We wait a short time though, just in case somehow the server - // is in a mode where it 400s /versions responses and sync etc. - // responses fail, this will mean we don't hammer in a loop. - this.keepAliveTimer = setTimeout(success, 2000); - } - else { - connDidFail = true; - this.keepAliveTimer = setTimeout(this.pokeKeepAlive.bind(this, connDidFail), 5000 + Math.floor(Math.random() * 5000)); - // A keepalive has failed, so we emit the - // error state (whether or not this is the - // first failure). - // Note we do this after setting the timer: - // this lets the unit tests advance the mock - // clock when they get the error. - this.updateSyncState(SyncState.Error, { error: err }); - } - }); - } - mapSyncResponseToRoomArray(obj) { - // Maps { roomid: {stuff}, roomid: {stuff} } - // to - // [{stuff+Room+isBrandNewRoom}, {stuff+Room+isBrandNewRoom}] - const client = this.client; - return Object.keys(obj) - .filter((k) => !(0, utils_1.unsafeProp)(k)) - .map((roomId) => { - const arrObj = obj[roomId]; - let room = client.store.getRoom(roomId); - let isBrandNewRoom = false; - if (!room) { - room = this.createRoom(roomId); - isBrandNewRoom = true; - } - arrObj.room = room; - arrObj.isBrandNewRoom = isBrandNewRoom; - return arrObj; - }); - } - mapSyncEventsFormat(obj, room, decrypt = true) { - if (!obj || !Array.isArray(obj.events)) { - return []; - } - const mapper = this.client.getEventMapper({ decrypt }); - return obj.events.filter(utils_1.noUnsafeEventProps).map(function (e) { - if (room) { - e.room_id = room.roomId; - } - return mapper(e); - }); - } - /** - */ - resolveInvites(room) { - if (!room || !this.opts.resolveInvitesToProfiles) { - return; - } - const client = this.client; - // For each invited room member we want to give them a displayname/avatar url - // if they have one (the m.room.member invites don't contain this). - room.getMembersWithMembership("invite").forEach(function (member) { - if (member.requestedProfileInfo) - return; - member.requestedProfileInfo = true; - // try to get a cached copy first. - const user = client.getUser(member.userId); - let promise; - if (user) { - promise = Promise.resolve({ - avatar_url: user.avatarUrl, - displayname: user.displayName, - }); - } - else { - promise = client.getProfileInfo(member.userId); - } - promise.then(function (info) { - // slightly naughty by doctoring the invite event but this means all - // the code paths remain the same between invite/join display name stuff - // which is a worthy trade-off for some minor pollution. - const inviteEvent = member.events.member; - if ((inviteEvent === null || inviteEvent === void 0 ? void 0 : inviteEvent.getContent().membership) !== "invite") { - // between resolving and now they have since joined, so don't clobber - return; - } - inviteEvent.getContent().avatar_url = info.avatar_url; - inviteEvent.getContent().displayname = info.displayname; - // fire listeners - member.setMembershipEvent(inviteEvent, room.currentState); - }, function (err) { - // OH WELL. - }); - }); - } - /** - * Injects events into a room's model. - * @param stateEventList - A list of state events. This is the state - * at the *START* of the timeline list if it is supplied. - * @param timelineEventList - A list of timeline events, including threaded. Lower index - * is earlier in time. Higher index is later. - * @param fromCache - whether the sync response came from cache - */ - injectRoomEvents(room, stateEventList, timelineEventList, fromCache = false) { - return __awaiter(this, void 0, void 0, function* () { - // If there are no events in the timeline yet, initialise it with - // the given state events - const liveTimeline = room.getLiveTimeline(); - const timelineWasEmpty = liveTimeline.getEvents().length == 0; - if (timelineWasEmpty) { - // Passing these events into initialiseState will freeze them, so we need - // to compute and cache the push actions for them now, otherwise sync dies - // with an attempt to assign to read only property. - // XXX: This is pretty horrible and is assuming all sorts of behaviour from - // these functions that it shouldn't be. We should probably either store the - // push actions cache elsewhere so we can freeze MatrixEvents, or otherwise - // find some solution where MatrixEvents are immutable but allow for a cache - // field. - for (const ev of stateEventList) { - this.client.getPushActionsForEvent(ev); - } - liveTimeline.initialiseState(stateEventList, { - timelineWasEmpty, - }); - } - this.resolveInvites(room); - // recalculate the room name at this point as adding events to the timeline - // may make notifications appear which should have the right name. - // XXX: This looks suspect: we'll end up recalculating the room once here - // and then again after adding events (processSyncResponse calls it after - // calling us) even if no state events were added. It also means that if - // one of the room events in timelineEventList is something that needs - // a recalculation (like m.room.name) we won't recalculate until we've - // finished adding all the events, which will cause the notification to have - // the old room name rather than the new one. - room.recalculate(); - // If the timeline wasn't empty, we process the state events here: they're - // defined as updates to the state before the start of the timeline, so this - // starts to roll the state forward. - // XXX: That's what we *should* do, but this can happen if we were previously - // peeking in a room, in which case we obviously do *not* want to add the - // state events here onto the end of the timeline. Historically, the js-sdk - // has just set these new state events on the old and new state. This seems - // very wrong because there could be events in the timeline that diverge the - // state, in which case this is going to leave things out of sync. However, - // for now I think it;s best to behave the same as the code has done previously. - if (!timelineWasEmpty) { - // XXX: As above, don't do this... - //room.addLiveEvents(stateEventList || []); - // Do this instead... - room.oldState.setStateEvents(stateEventList || []); - room.currentState.setStateEvents(stateEventList || []); - } - // Execute the timeline events. This will continue to diverge the current state - // if the timeline has any state events in it. - // This also needs to be done before running push rules on the events as they need - // to be decorated with sender etc. - room.addLiveEvents(timelineEventList || [], { - fromCache, - timelineWasEmpty, - }); - this.client.processBeaconEvents(room, timelineEventList); - }); - } - /** - * Takes a list of timelineEvents and adds and adds to notifEvents - * as appropriate. - * This must be called after the room the events belong to has been stored. - * - * @param timelineEventList - A list of timeline events. Lower index - * is earlier in time. Higher index is later. - */ - processEventsForNotifs(room, timelineEventList) { - var _a; - // gather our notifications into this.notifEvents - if (this.client.getNotifTimelineSet()) { - for (const event of timelineEventList) { - const pushActions = this.client.getPushActionsForEvent(event); - if ((pushActions === null || pushActions === void 0 ? void 0 : pushActions.notify) && ((_a = pushActions.tweaks) === null || _a === void 0 ? void 0 : _a.highlight)) { - this.notifEvents.push(event); - } - } - } - } - getGuestFilter() { - // Dev note: This used to be conditional to return a filter of 20 events maximum, but - // the condition never went to the other branch. This is now hardcoded. - return "{}"; - } - /** - * Sets the sync state and emits an event to say so - * @param newState - The new state string - * @param data - Object of additional data to emit in the event - */ - updateSyncState(newState, data) { - const old = this.syncState; - this.syncState = newState; - this.syncStateData = data; - this.client.emit(client_1.ClientEvent.Sync, this.syncState, old, data); - } -} -exports.SyncApi = SyncApi; -function createNewUser(client, userId) { - const user = new user_1.User(userId); - client.reEmitter.reEmit(user, [ - user_1.UserEvent.AvatarUrl, - user_1.UserEvent.DisplayName, - user_1.UserEvent.Presence, - user_1.UserEvent.CurrentlyActive, - user_1.UserEvent.LastPresenceTs, - ]); - return user; -} -// /!\ This function is not intended for public use! It's only exported from -// here in order to share some common logic with sliding-sync-sdk.ts. -function _createAndReEmitRoom(client, roomId, opts) { - const { timelineSupport } = client; - const room = new room_1.Room(roomId, client, client.getUserId(), { - lazyLoadMembers: opts.lazyLoadMembers, - pendingEventOrdering: opts.pendingEventOrdering, - timelineSupport, - }); - client.reEmitter.reEmit(room, [ - room_1.RoomEvent.Name, - room_1.RoomEvent.Redaction, - room_1.RoomEvent.RedactionCancelled, - room_1.RoomEvent.Receipt, - room_1.RoomEvent.Tags, - room_1.RoomEvent.LocalEchoUpdated, - room_1.RoomEvent.AccountData, - room_1.RoomEvent.MyMembership, - room_1.RoomEvent.Timeline, - room_1.RoomEvent.TimelineReset, - room_state_1.RoomStateEvent.Events, - room_state_1.RoomStateEvent.Members, - room_state_1.RoomStateEvent.NewMember, - room_state_1.RoomStateEvent.Update, - beacon_1.BeaconEvent.New, - beacon_1.BeaconEvent.Update, - beacon_1.BeaconEvent.Destroy, - beacon_1.BeaconEvent.LivenessChange, - ]); - // We need to add a listener for RoomState.members in order to hook them - // correctly. - room.on(room_state_1.RoomStateEvent.NewMember, (event, state, member) => { - var _a; - member.user = (_a = client.getUser(member.userId)) !== null && _a !== void 0 ? _a : undefined; - client.reEmitter.reEmit(member, [ - room_member_1.RoomMemberEvent.Name, - room_member_1.RoomMemberEvent.Typing, - room_member_1.RoomMemberEvent.PowerLevel, - room_member_1.RoomMemberEvent.Membership, - ]); - }); - return room; -} -exports._createAndReEmitRoom = _createAndReEmitRoom; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./@types/event":306,"./@types/sync":314,"./client":321,"./errors":359,"./feature":362,"./filter":364,"./http-api":367,"./logger":374,"./models/beacon":378,"./models/event-timeline":382,"./models/room":392,"./models/room-member":389,"./models/room-state":390,"./models/user":396,"./utils":416}],415:[function(require,module,exports){ -"use strict"; -/* -Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TimelineIndex = exports.TimelineWindow = void 0; -const event_timeline_1 = require("./models/event-timeline"); -const logger_1 = require("./logger"); -/** - * @internal - */ -const DEBUG = false; -/** - * @internal - */ -/* istanbul ignore next */ -const debuglog = DEBUG ? logger_1.logger.log.bind(logger_1.logger) : function () { }; -/** - * the number of times we ask the server for more events before giving up - * - * @internal - */ -const DEFAULT_PAGINATE_LOOP_LIMIT = 5; -class TimelineWindow { - /** - * Construct a TimelineWindow. - * - *

This abstracts the separate timelines in a Matrix {@link Room} into a single iterable thing. - * It keeps track of the start and endpoints of the window, which can be advanced with the help - * of pagination requests. - * - *

Before the window is useful, it must be initialised by calling {@link TimelineWindow#load}. - * - *

Note that the window will not automatically extend itself when new events - * are received from /sync; you should arrange to call {@link TimelineWindow#paginate} - * on {@link RoomEvent.Timeline} events. - * - * @param client - MatrixClient to be used for context/pagination - * requests. - * - * @param timelineSet - The timelineSet to track - * - * @param opts - Configuration options for this window - */ - constructor(client, timelineSet, opts = {}) { - this.client = client; - this.timelineSet = timelineSet; - this.eventCount = 0; - this.windowLimit = opts.windowLimit || 1000; - } - /** - * Initialise the window to point at a given event, or the live timeline - * - * @param initialEventId - If given, the window will contain the - * given event - * @param initialWindowSize - Size of the initial window - */ - load(initialEventId, initialWindowSize = 20) { - // given an EventTimeline, find the event we were looking for, and initialise our - // fields so that the event in question is in the middle of the window. - const initFields = (timeline) => { - if (!timeline) { - throw new Error("No timeline given to initFields"); - } - let eventIndex; - const events = timeline.getEvents(); - if (!initialEventId) { - // we were looking for the live timeline: initialise to the end - eventIndex = events.length; - } - else { - eventIndex = events.findIndex((e) => e.getId() === initialEventId); - if (eventIndex < 0) { - throw new Error("getEventTimeline result didn't include requested event"); - } - } - const endIndex = Math.min(events.length, eventIndex + Math.ceil(initialWindowSize / 2)); - const startIndex = Math.max(0, endIndex - initialWindowSize); - this.start = new TimelineIndex(timeline, startIndex - timeline.getBaseIndex()); - this.end = new TimelineIndex(timeline, endIndex - timeline.getBaseIndex()); - this.eventCount = endIndex - startIndex; - }; - // We avoid delaying the resolution of the promise by a reactor tick if we already have the data we need, - // which is important to keep room-switching feeling snappy. - if (this.timelineSet.getTimelineForEvent(initialEventId)) { - initFields(this.timelineSet.getTimelineForEvent(initialEventId)); - return Promise.resolve(); - } - else if (initialEventId) { - return this.client.getEventTimeline(this.timelineSet, initialEventId).then(initFields); - } - else { - initFields(this.timelineSet.getLiveTimeline()); - return Promise.resolve(); - } - } - /** - * Get the TimelineIndex of the window in the given direction. - * - * @param direction - EventTimeline.BACKWARDS to get the TimelineIndex - * at the start of the window; EventTimeline.FORWARDS to get the TimelineIndex at - * the end. - * - * @returns The requested timeline index if one exists, null - * otherwise. - */ - getTimelineIndex(direction) { - var _a, _b; - if (direction == event_timeline_1.EventTimeline.BACKWARDS) { - return (_a = this.start) !== null && _a !== void 0 ? _a : null; - } - else if (direction == event_timeline_1.EventTimeline.FORWARDS) { - return (_b = this.end) !== null && _b !== void 0 ? _b : null; - } - else { - throw new Error("Invalid direction '" + direction + "'"); - } - } - /** - * Try to extend the window using events that are already in the underlying - * TimelineIndex. - * - * @param direction - EventTimeline.BACKWARDS to try extending it - * backwards; EventTimeline.FORWARDS to try extending it forwards. - * @param size - number of events to try to extend by. - * - * @returns true if the window was extended, false otherwise. - */ - extend(direction, size) { - const tl = this.getTimelineIndex(direction); - if (!tl) { - debuglog("TimelineWindow: no timeline yet"); - return false; - } - const count = direction == event_timeline_1.EventTimeline.BACKWARDS ? tl.retreat(size) : tl.advance(size); - if (count) { - this.eventCount += count; - debuglog("TimelineWindow: increased cap by " + count + " (now " + this.eventCount + ")"); - // remove some events from the other end, if necessary - const excess = this.eventCount - this.windowLimit; - if (excess > 0) { - this.unpaginate(excess, direction != event_timeline_1.EventTimeline.BACKWARDS); - } - return true; - } - return false; - } - /** - * Check if this window can be extended - * - *

This returns true if we either have more events, or if we have a - * pagination token which means we can paginate in that direction. It does not - * necessarily mean that there are more events available in that direction at - * this time. - * - * @param direction - EventTimeline.BACKWARDS to check if we can - * paginate backwards; EventTimeline.FORWARDS to check if we can go forwards - * - * @returns true if we can paginate in the given direction - */ - canPaginate(direction) { - const tl = this.getTimelineIndex(direction); - if (!tl) { - debuglog("TimelineWindow: no timeline yet"); - return false; - } - if (direction == event_timeline_1.EventTimeline.BACKWARDS) { - if (tl.index > tl.minIndex()) { - return true; - } - } - else { - if (tl.index < tl.maxIndex()) { - return true; - } - } - const hasNeighbouringTimeline = tl.timeline.getNeighbouringTimeline(direction); - const paginationToken = tl.timeline.getPaginationToken(direction); - return Boolean(hasNeighbouringTimeline) || Boolean(paginationToken); - } - /** - * Attempt to extend the window - * - * @param direction - EventTimeline.BACKWARDS to extend the window - * backwards (towards older events); EventTimeline.FORWARDS to go forwards. - * - * @param size - number of events to try to extend by. If fewer than this - * number are immediately available, then we return immediately rather than - * making an API call. - * - * @param makeRequest - whether we should make API calls to - * fetch further events if we don't have any at all. (This has no effect if - * the room already knows about additional events in the relevant direction, - * even if there are fewer than 'size' of them, as we will just return those - * we already know about.) - * - * @param requestLimit - limit for the number of API requests we - * should make. - * - * @returns Promise which resolves to a boolean which is true if more events - * were successfully retrieved. - */ - paginate(direction, size, makeRequest = true, requestLimit = DEFAULT_PAGINATE_LOOP_LIMIT) { - return __awaiter(this, void 0, void 0, function* () { - // Either wind back the message cap (if there are enough events in the - // timeline to do so), or fire off a pagination request. - const tl = this.getTimelineIndex(direction); - if (!tl) { - debuglog("TimelineWindow: no timeline yet"); - return false; - } - if (tl.pendingPaginate) { - return tl.pendingPaginate; - } - // try moving the cap - if (this.extend(direction, size)) { - return true; - } - if (!makeRequest || requestLimit === 0) { - // todo: should we return something different to indicate that there - // might be more events out there, but we haven't found them yet? - return false; - } - // try making a pagination request - const token = tl.timeline.getPaginationToken(direction); - if (!token) { - debuglog("TimelineWindow: no token"); - return false; - } - debuglog("TimelineWindow: starting request"); - const prom = this.client - .paginateEventTimeline(tl.timeline, { - backwards: direction == event_timeline_1.EventTimeline.BACKWARDS, - limit: size, - }) - .finally(function () { - tl.pendingPaginate = undefined; - }) - .then((r) => { - debuglog("TimelineWindow: request completed with result " + r); - if (!r) { - return this.paginate(direction, size, false, 0); - } - // recurse to advance the index into the results. - // - // If we don't get any new events, we want to make sure we keep asking - // the server for events for as long as we have a valid pagination - // token. In particular, we want to know if we've actually hit the - // start of the timeline, or if we just happened to know about all of - // the events thanks to https://matrix.org/jira/browse/SYN-645. - // - // On the other hand, we necessarily want to wait forever for the - // server to make its mind up about whether there are other events, - // because it gives a bad user experience - // (https://github.com/vector-im/vector-web/issues/1204). - return this.paginate(direction, size, true, requestLimit - 1); - }); - tl.pendingPaginate = prom; - return prom; - }); - } - /** - * Remove `delta` events from the start or end of the timeline. - * - * @param delta - number of events to remove from the timeline - * @param startOfTimeline - if events should be removed from the start - * of the timeline. - */ - unpaginate(delta, startOfTimeline) { - const tl = startOfTimeline ? this.start : this.end; - if (!tl) { - throw new Error(`Attempting to unpaginate startOfTimeline=${startOfTimeline} but don't have this direction`); - } - // sanity-check the delta - if (delta > this.eventCount || delta < 0) { - throw new Error(`Attemting to unpaginate ${delta} events, but only have ${this.eventCount} in the timeline`); - } - while (delta > 0) { - const count = startOfTimeline ? tl.advance(delta) : tl.retreat(delta); - if (count <= 0) { - // sadness. This shouldn't be possible. - throw new Error("Unable to unpaginate any further, but still have " + this.eventCount + " events"); - } - delta -= count; - this.eventCount -= count; - debuglog("TimelineWindow.unpaginate: dropped " + count + " (now " + this.eventCount + ")"); - } - } - /** - * Get a list of the events currently in the window - * - * @returns the events in the window - */ - getEvents() { - var _a, _b; - if (!this.start) { - // not yet loaded - return []; - } - const result = []; - // iterate through each timeline between this.start and this.end - // (inclusive). - let timeline = this.start.timeline; - // eslint-disable-next-line no-constant-condition - while (true) { - const events = timeline.getEvents(); - // For the first timeline in the chain, we want to start at - // this.start.index. For the last timeline in the chain, we want to - // stop before this.end.index. Otherwise, we want to copy all of the - // events in the timeline. - // - // (Note that both this.start.index and this.end.index are relative - // to their respective timelines' BaseIndex). - // - let startIndex = 0; - let endIndex = events.length; - if (timeline === this.start.timeline) { - startIndex = this.start.index + timeline.getBaseIndex(); - } - if (timeline === ((_a = this.end) === null || _a === void 0 ? void 0 : _a.timeline)) { - endIndex = this.end.index + timeline.getBaseIndex(); - } - for (let i = startIndex; i < endIndex; i++) { - result.push(events[i]); - } - // if we're not done, iterate to the next timeline. - if (timeline === ((_b = this.end) === null || _b === void 0 ? void 0 : _b.timeline)) { - break; - } - else { - timeline = timeline.getNeighbouringTimeline(event_timeline_1.EventTimeline.FORWARDS); - } - } - return result; - } -} -exports.TimelineWindow = TimelineWindow; -/** - * A thing which contains a timeline reference, and an index into it. - * @internal - */ -class TimelineIndex { - // index: the indexes are relative to BaseIndex, so could well be negative. - constructor(timeline, index) { - this.timeline = timeline; - this.index = index; - } - /** - * @returns the minimum possible value for the index in the current - * timeline - */ - minIndex() { - return this.timeline.getBaseIndex() * -1; - } - /** - * @returns the maximum possible value for the index in the current - * timeline (exclusive - ie, it actually returns one more than the index - * of the last element). - */ - maxIndex() { - return this.timeline.getEvents().length - this.timeline.getBaseIndex(); - } - /** - * Try move the index forward, or into the neighbouring timeline - * - * @param delta - number of events to advance by - * @returns number of events successfully advanced by - */ - advance(delta) { - if (!delta) { - return 0; - } - // first try moving the index in the current timeline. See if there is room - // to do so. - let cappedDelta; - if (delta < 0) { - // we want to wind the index backwards. - // - // (this.minIndex() - this.index) is a negative number whose magnitude - // is the amount of room we have to wind back the index in the current - // timeline. We cap delta to this quantity. - cappedDelta = Math.max(delta, this.minIndex() - this.index); - if (cappedDelta < 0) { - this.index += cappedDelta; - return cappedDelta; - } - } - else { - // we want to wind the index forwards. - // - // (this.maxIndex() - this.index) is a (positive) number whose magnitude - // is the amount of room we have to wind forward the index in the current - // timeline. We cap delta to this quantity. - cappedDelta = Math.min(delta, this.maxIndex() - this.index); - if (cappedDelta > 0) { - this.index += cappedDelta; - return cappedDelta; - } - } - // the index is already at the start/end of the current timeline. - // - // next see if there is a neighbouring timeline to switch to. - const neighbour = this.timeline.getNeighbouringTimeline(delta < 0 ? event_timeline_1.EventTimeline.BACKWARDS : event_timeline_1.EventTimeline.FORWARDS); - if (neighbour) { - this.timeline = neighbour; - if (delta < 0) { - this.index = this.maxIndex(); - } - else { - this.index = this.minIndex(); - } - debuglog("paginate: switched to new neighbour"); - // recurse, using the next timeline - return this.advance(delta); - } - return 0; - } - /** - * Try move the index backwards, or into the neighbouring timeline - * - * @param delta - number of events to retreat by - * @returns number of events successfully retreated by - */ - retreat(delta) { - return this.advance(delta * -1) * -1; - } -} -exports.TimelineIndex = TimelineIndex; - -},{"./logger":374,"./models/event-timeline":382}],416:[function(require,module,exports){ -(function (setImmediate){(function (){ -"use strict"; -/* -Copyright 2015, 2016, 2019, 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MapWithDefault = exports.noUnsafeEventProps = exports.safeSet = exports.unsafeProp = exports.recursiveMapToObject = exports.mapsEqual = exports.isSupportedReceiptType = exports.sortEventsByLatestContentTimestamp = exports.recursivelyAssign = exports.compare = exports.lexicographicCompare = exports.prevString = exports.nextString = exports.averageBetweenStrings = exports.stringToBase = exports.baseToString = exports.alphabetPad = exports.DEFAULT_ALPHABET = exports.simpleRetryOperation = exports.chunkPromises = exports.promiseTry = exports.promiseMapSeries = exports.defer = exports.isNullOrUndefined = exports.immediate = exports.sleep = exports.ensureNoTrailingSlash = exports.globToRegexp = exports.escapeRegExp = exports.normalize = exports.removeDirectionOverrideChars = exports.removeHiddenChars = exports.isNumber = exports.deepSortedObjectEntries = exports.deepCompare = exports.deepCopy = exports.checkObjectHasKeys = exports.isFunction = exports.removeElement = exports.encodeUri = exports.decodeParams = exports.replaceParam = exports.encodeParams = exports.internaliseString = void 0; -/** - * This is an internal module. - */ -const unhomoglyph_1 = __importDefault(require("unhomoglyph")); -const p_retry_1 = __importDefault(require("p-retry")); -const location_1 = require("./@types/location"); -const read_receipts_1 = require("./@types/read_receipts"); -const interns = new Map(); -/** - * Internalises a string, reusing a known pointer or storing the pointer - * if needed for future strings. - * @param str - The string to internalise. - * @returns The internalised string. - */ -function internaliseString(str) { - // Unwrap strings before entering the map, if we somehow got a wrapped - // string as our input. This should only happen from tests. - if (str instanceof String) { - str = str.toString(); - } - // Check the map to see if we can store the value - if (!interns.has(str)) { - interns.set(str, str); - } - // Return any cached string reference - return interns.get(str); -} -exports.internaliseString = internaliseString; -/** - * Encode a dictionary of query parameters. - * Omits any undefined/null values. - * @param params - A dict of key/values to encode e.g. - * `{"foo": "bar", "baz": "taz"}` - * @returns The encoded string e.g. foo=bar&baz=taz - */ -function encodeParams(params, urlSearchParams) { - const searchParams = urlSearchParams !== null && urlSearchParams !== void 0 ? urlSearchParams : new URLSearchParams(); - for (const [key, val] of Object.entries(params)) { - if (val !== undefined && val !== null) { - if (Array.isArray(val)) { - val.forEach((v) => { - searchParams.append(key, String(v)); - }); - } - else { - searchParams.append(key, String(val)); - } - } - } - return searchParams; -} -exports.encodeParams = encodeParams; -/** - * Replace a stable parameter with the unstable naming for params - */ -function replaceParam(stable, unstable, dict) { - const result = Object.assign(Object.assign({}, dict), { [unstable]: dict[stable] }); - delete result[stable]; - return result; -} -exports.replaceParam = replaceParam; -/** - * Decode a query string in `application/x-www-form-urlencoded` format. - * @param query - A query string to decode e.g. - * foo=bar&via=server1&server2 - * @returns The decoded object, if any keys occurred multiple times - * then the value will be an array of strings, else it will be an array. - * This behaviour matches Node's qs.parse but is built on URLSearchParams - * for native web compatibility - */ -function decodeParams(query) { - const o = {}; - const params = new URLSearchParams(query); - for (const key of params.keys()) { - const val = params.getAll(key); - o[key] = val.length === 1 ? val[0] : val; - } - return o; -} -exports.decodeParams = decodeParams; -/** - * Encodes a URI according to a set of template variables. Variables will be - * passed through encodeURIComponent. - * @param pathTemplate - The path with template variables e.g. '/foo/$bar'. - * @param variables - The key/value pairs to replace the template - * variables with. E.g. `{ "$bar": "baz" }`. - * @returns The result of replacing all template variables e.g. '/foo/baz'. - */ -function encodeUri(pathTemplate, variables) { - for (const key in variables) { - if (!variables.hasOwnProperty(key)) { - continue; - } - const value = variables[key]; - if (value === undefined || value === null) { - continue; - } - pathTemplate = pathTemplate.replace(key, encodeURIComponent(value)); - } - return pathTemplate; -} -exports.encodeUri = encodeUri; -/** - * The removeElement() method removes the first element in the array that - * satisfies (returns true) the provided testing function. - * @param array - The array. - * @param fn - Function to execute on each value in the array, with the - * function signature `fn(element, index, array)`. Return true to - * remove this element and break. - * @param reverse - True to search in reverse order. - * @returns True if an element was removed. - */ -function removeElement(array, fn, reverse) { - let i; - if (reverse) { - for (i = array.length - 1; i >= 0; i--) { - if (fn(array[i], i, array)) { - array.splice(i, 1); - return true; - } - } - } - else { - for (i = 0; i < array.length; i++) { - if (fn(array[i], i, array)) { - array.splice(i, 1); - return true; - } - } - } - return false; -} -exports.removeElement = removeElement; -/** - * Checks if the given thing is a function. - * @param value - The thing to check. - * @returns True if it is a function. - */ -function isFunction(value) { - return Object.prototype.toString.call(value) === "[object Function]"; -} -exports.isFunction = isFunction; -/** - * Checks that the given object has the specified keys. - * @param obj - The object to check. - * @param keys - The list of keys that 'obj' must have. - * @throws If the object is missing keys. - */ -// note using 'keys' here would shadow the 'keys' function defined above -function checkObjectHasKeys(obj, keys) { - for (const key of keys) { - if (!obj.hasOwnProperty(key)) { - throw new Error("Missing required key: " + key); - } - } -} -exports.checkObjectHasKeys = checkObjectHasKeys; -/** - * Deep copy the given object. The object MUST NOT have circular references and - * MUST NOT have functions. - * @param obj - The object to deep copy. - * @returns A copy of the object without any references to the original. - */ -function deepCopy(obj) { - return JSON.parse(JSON.stringify(obj)); -} -exports.deepCopy = deepCopy; -/** - * Compare two objects for equality. The objects MUST NOT have circular references. - * - * @param x - The first object to compare. - * @param y - The second object to compare. - * - * @returns true if the two objects are equal - */ -function deepCompare(x, y) { - // Inspired by - // http://stackoverflow.com/questions/1068834/object-comparison-in-javascript#1144249 - // Compare primitives and functions. - // Also check if both arguments link to the same object. - if (x === y) { - return true; - } - if (typeof x !== typeof y) { - return false; - } - // special-case NaN (since NaN !== NaN) - if (typeof x === "number" && isNaN(x) && isNaN(y)) { - return true; - } - // special-case null (since typeof null == 'object', but null.constructor - // throws) - if (x === null || y === null) { - return x === y; - } - // everything else is either an unequal primitive, or an object - if (!(x instanceof Object)) { - return false; - } - // check they are the same type of object - if (x.constructor !== y.constructor || x.prototype !== y.prototype) { - return false; - } - // special-casing for some special types of object - if (x instanceof RegExp || x instanceof Date) { - return x.toString() === y.toString(); - } - // the object algorithm works for Array, but it's sub-optimal. - if (Array.isArray(x)) { - if (x.length !== y.length) { - return false; - } - for (let i = 0; i < x.length; i++) { - if (!deepCompare(x[i], y[i])) { - return false; - } - } - } - else { - // check that all of y's direct keys are in x - for (const p in y) { - if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { - return false; - } - } - // finally, compare each of x's keys with y - for (const p in x) { - if (y.hasOwnProperty(p) !== x.hasOwnProperty(p) || !deepCompare(x[p], y[p])) { - return false; - } - } - } - return true; -} -exports.deepCompare = deepCompare; -// Dev note: This returns an array of tuples, but jsdoc doesn't like that. https://github.com/jsdoc/jsdoc/issues/1703 -/** - * Creates an array of object properties/values (entries) then - * sorts the result by key, recursively. The input object must - * ensure it does not have loops. If the input is not an object - * then it will be returned as-is. - * @param obj - The object to get entries of - * @returns The entries, sorted by key. - */ -function deepSortedObjectEntries(obj) { - if (typeof obj !== "object") - return obj; - // Apparently these are object types... - if (obj === null || obj === undefined || Array.isArray(obj)) - return obj; - const pairs = []; - for (const [k, v] of Object.entries(obj)) { - pairs.push([k, deepSortedObjectEntries(v)]); - } - // lexicographicCompare is faster than localeCompare, so let's use that. - pairs.sort((a, b) => lexicographicCompare(a[0], b[0])); - return pairs; -} -exports.deepSortedObjectEntries = deepSortedObjectEntries; -/** - * Returns whether the given value is a finite number without type-coercion - * - * @param value - the value to test - * @returns whether or not value is a finite number without type-coercion - */ -function isNumber(value) { - return typeof value === "number" && isFinite(value); -} -exports.isNumber = isNumber; -/** - * Removes zero width chars, diacritics and whitespace from the string - * Also applies an unhomoglyph on the string, to prevent similar looking chars - * @param str - the string to remove hidden characters from - * @returns a string with the hidden characters removed - */ -function removeHiddenChars(str) { - if (typeof str === "string") { - return (0, unhomoglyph_1.default)(str.normalize("NFD").replace(removeHiddenCharsRegex, "")); - } - return ""; -} -exports.removeHiddenChars = removeHiddenChars; -/** - * Removes the direction override characters from a string - * @returns string with chars removed - */ -function removeDirectionOverrideChars(str) { - if (typeof str === "string") { - return str.replace(/[\u202d-\u202e]/g, ""); - } - return ""; -} -exports.removeDirectionOverrideChars = removeDirectionOverrideChars; -function normalize(str) { - // Note: we have to match the filter with the removeHiddenChars() because the - // function strips spaces and other characters (M becomes RN for example, in lowercase). - return (removeHiddenChars(str.toLowerCase()) - // Strip all punctuation - .replace(/[\\'!"#$%&()*+,\-./:;<=>?@[\]^_`{|}~\u2000-\u206f\u2e00-\u2e7f]/g, "") - // We also doubly convert to lowercase to work around oddities of the library. - .toLowerCase()); -} -exports.normalize = normalize; -// Regex matching bunch of unicode control characters and otherwise misleading/invisible characters. -// Includes: -// various width spaces U+2000 - U+200D -// LTR and RTL marks U+200E and U+200F -// LTR/RTL and other directional formatting marks U+202A - U+202F -// Arabic Letter RTL mark U+061C -// Combining characters U+0300 - U+036F -// Zero width no-break space (BOM) U+FEFF -// Blank/invisible characters (U2800, U2062-U2063) -// eslint-disable-next-line no-misleading-character-class -const removeHiddenCharsRegex = /[\u2000-\u200F\u202A-\u202F\u0300-\u036F\uFEFF\u061C\u2800\u2062-\u2063\s]/g; -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} -exports.escapeRegExp = escapeRegExp; -function globToRegexp(glob, extended = false) { - // From - // https://github.com/matrix-org/synapse/blob/abbee6b29be80a77e05730707602f3bbfc3f38cb/synapse/push/__init__.py#L132 - // Because micromatch is about 130KB with dependencies, - // and minimatch is not much better. - const replacements = [ - [/\\\*/g, ".*"], - [/\?/g, "."], - ]; - if (!extended) { - replacements.push([ - /\\\[(!|)(.*)\\]/g, - (_match, neg, pat) => ["[", neg ? "^" : "", pat.replace(/\\-/, "-"), "]"].join(""), - ]); - } - return replacements.reduce( - // https://github.com/microsoft/TypeScript/issues/30134 - (pat, args) => (args ? pat.replace(args[0], args[1]) : pat), escapeRegExp(glob)); -} -exports.globToRegexp = globToRegexp; -function ensureNoTrailingSlash(url) { - if (url === null || url === void 0 ? void 0 : url.endsWith("/")) { - return url.slice(0, -1); - } - else { - return url; - } -} -exports.ensureNoTrailingSlash = ensureNoTrailingSlash; -/** - * Returns a promise which resolves with a given value after the given number of ms - */ -function sleep(ms, value) { - return new Promise((resolve) => { - setTimeout(resolve, ms, value); - }); -} -exports.sleep = sleep; -/** - * Promise/async version of {@link setImmediate}. - */ -function immediate() { - return new Promise(setImmediate); -} -exports.immediate = immediate; -function isNullOrUndefined(val) { - return val === null || val === undefined; -} -exports.isNullOrUndefined = isNullOrUndefined; -// Returns a Deferred -function defer() { - let resolve; - let reject; - const promise = new Promise((_resolve, _reject) => { - resolve = _resolve; - reject = _reject; - }); - return { resolve, reject, promise }; -} -exports.defer = defer; -function promiseMapSeries(promises, fn) { - return __awaiter(this, void 0, void 0, function* () { - for (const o of promises) { - yield fn(yield o); - } - }); -} -exports.promiseMapSeries = promiseMapSeries; -function promiseTry(fn) { - return Promise.resolve(fn()); -} -exports.promiseTry = promiseTry; -// Creates and awaits all promises, running no more than `chunkSize` at the same time -function chunkPromises(fns, chunkSize) { - return __awaiter(this, void 0, void 0, function* () { - const results = []; - for (let i = 0; i < fns.length; i += chunkSize) { - results.push(...(yield Promise.all(fns.slice(i, i + chunkSize).map((fn) => fn())))); - } - return results; - }); -} -exports.chunkPromises = chunkPromises; -/** - * Retries the function until it succeeds or is interrupted. The given function must return - * a promise which throws/rejects on error, otherwise the retry will assume the request - * succeeded. The promise chain returned will contain the successful promise. The given function - * should always return a new promise. - * @param promiseFn - The function to call to get a fresh promise instance. Takes an - * attempt count as an argument, for logging/debugging purposes. - * @returns The promise for the retried operation. - */ -function simpleRetryOperation(promiseFn) { - return (0, p_retry_1.default)((attempt) => { - return promiseFn(attempt); - }, { - forever: true, - factor: 2, - minTimeout: 3000, - maxTimeout: 15000, // ms - }); -} -exports.simpleRetryOperation = simpleRetryOperation; -// String averaging inspired by https://stackoverflow.com/a/2510816 -// Dev note: We make the alphabet a string because it's easier to write syntactically -// than arrays. Thankfully, strings implement the useful parts of the Array interface -// anyhow. -/** - * The default alphabet used by string averaging in this SDK. This matches - * all usefully printable ASCII characters (0x20-0x7E, inclusive). - */ -exports.DEFAULT_ALPHABET = (() => { - let str = ""; - for (let c = 0x20; c <= 0x7e; c++) { - str += String.fromCharCode(c); - } - return str; -})(); -/** - * Pads a string using the given alphabet as a base. The returned string will be - * padded at the end with the first character in the alphabet. - * - * This is intended for use with string averaging. - * @param s - The string to pad. - * @param n - The length to pad to. - * @param alphabet - The alphabet to use as a single string. - * @returns The padded string. - */ -function alphabetPad(s, n, alphabet = exports.DEFAULT_ALPHABET) { - return s.padEnd(n, alphabet[0]); -} -exports.alphabetPad = alphabetPad; -/** - * Converts a baseN number to a string, where N is the alphabet's length. - * - * This is intended for use with string averaging. - * @param n - The baseN number. - * @param alphabet - The alphabet to use as a single string. - * @returns The baseN number encoded as a string from the alphabet. - */ -function baseToString(n, alphabet = exports.DEFAULT_ALPHABET) { - // Developer note: the stringToBase() function offsets the character set by 1 so that repeated - // characters (ie: "aaaaaa" in a..z) don't come out as zero. We have to reverse this here as - // otherwise we'll be wrong in our conversion. Undoing a +1 before an exponent isn't very fun - // though, so we rely on a lengthy amount of `x - 1` and integer division rules to reach a - // sane state. This also means we have to do rollover detection: see below. - var _a; - const len = BigInt(alphabet.length); - if (n <= len) { - return (_a = alphabet[Number(n) - 1]) !== null && _a !== void 0 ? _a : ""; - } - let d = n / len; - let r = Number(n % len) - 1; - // Rollover detection: if the remainder is negative, it means that the string needs - // to roll over by 1 character downwards (ie: in a..z, the previous to "aaa" would be - // "zz"). - if (r < 0) { - d -= BigInt(Math.abs(r)); // abs() is just to be clear what we're doing. Could also `+= r`. - r = Number(len) - 1; - } - return baseToString(d, alphabet) + alphabet[r]; -} -exports.baseToString = baseToString; -/** - * Converts a string to a baseN number, where N is the alphabet's length. - * - * This is intended for use with string averaging. - * @param s - The string to convert to a number. - * @param alphabet - The alphabet to use as a single string. - * @returns The baseN number. - */ -function stringToBase(s, alphabet = exports.DEFAULT_ALPHABET) { - const len = BigInt(alphabet.length); - // In our conversion to baseN we do a couple performance optimizations to avoid using - // excess CPU and such. To create baseN numbers, the input string needs to be reversed - // so the exponents stack up appropriately, as the last character in the unreversed - // string has less impact than the first character (in "abc" the A is a lot more important - // for lexicographic sorts). We also do a trick with the character codes to optimize the - // alphabet lookup, avoiding an index scan of `alphabet.indexOf(reversedStr[i])` - we know - // that the alphabet and (theoretically) the input string are constrained on character sets - // and thus can do simple subtraction to end up with the same result. - // Developer caution: we carefully cast to BigInt here to avoid losing precision. We cannot - // rely on Math.pow() (for example) to be capable of handling our insane numbers. - let result = BigInt(0); - for (let i = s.length - 1, j = BigInt(0); i >= 0; i--, j++) { - const charIndex = s.charCodeAt(i) - alphabet.charCodeAt(0); - // We add 1 to the char index to offset the whole numbering scheme. We unpack this in - // the baseToString() function. - result += BigInt(1 + charIndex) * len ** j; - } - return result; -} -exports.stringToBase = stringToBase; -/** - * Averages two strings, returning the midpoint between them. This is accomplished by - * converting both to baseN numbers (where N is the alphabet's length) then averaging - * those before re-encoding as a string. - * @param a - The first string. - * @param b - The second string. - * @param alphabet - The alphabet to use as a single string. - * @returns The midpoint between the strings, as a string. - */ -function averageBetweenStrings(a, b, alphabet = exports.DEFAULT_ALPHABET) { - const padN = Math.max(a.length, b.length); - const baseA = stringToBase(alphabetPad(a, padN, alphabet), alphabet); - const baseB = stringToBase(alphabetPad(b, padN, alphabet), alphabet); - const avg = (baseA + baseB) / BigInt(2); - // Detect integer division conflicts. This happens when two numbers are divided too close so - // we lose a .5 precision. We need to add a padding character in these cases. - if (avg === baseA || avg == baseB) { - return baseToString(avg, alphabet) + alphabet[0]; - } - return baseToString(avg, alphabet); -} -exports.averageBetweenStrings = averageBetweenStrings; -/** - * Finds the next string using the alphabet provided. This is done by converting the - * string to a baseN number, where N is the alphabet's length, then adding 1 before - * converting back to a string. - * @param s - The string to start at. - * @param alphabet - The alphabet to use as a single string. - * @returns The string which follows the input string. - */ -function nextString(s, alphabet = exports.DEFAULT_ALPHABET) { - return baseToString(stringToBase(s, alphabet) + BigInt(1), alphabet); -} -exports.nextString = nextString; -/** - * Finds the previous string using the alphabet provided. This is done by converting the - * string to a baseN number, where N is the alphabet's length, then subtracting 1 before - * converting back to a string. - * @param s - The string to start at. - * @param alphabet - The alphabet to use as a single string. - * @returns The string which precedes the input string. - */ -function prevString(s, alphabet = exports.DEFAULT_ALPHABET) { - return baseToString(stringToBase(s, alphabet) - BigInt(1), alphabet); -} -exports.prevString = prevString; -/** - * Compares strings lexicographically as a sort-safe function. - * @param a - The first (reference) string. - * @param b - The second (compare) string. - * @returns Negative if the reference string is before the compare string; - * positive if the reference string is after; and zero if equal. - */ -function lexicographicCompare(a, b) { - // Dev note: this exists because I'm sad that you can use math operators on strings, so I've - // hidden the operation in this function. - if (a < b) { - return -1; - } - else if (a > b) { - return 1; - } - else { - return 0; - } -} -exports.lexicographicCompare = lexicographicCompare; -const collator = new Intl.Collator(); -/** - * Performant language-sensitive string comparison - * @param a - the first string to compare - * @param b - the second string to compare - */ -function compare(a, b) { - return collator.compare(a, b); -} -exports.compare = compare; -/** - * This function is similar to Object.assign() but it assigns recursively and - * allows you to ignore nullish values from the source - * - * @returns the target object - */ -function recursivelyAssign(target, source, ignoreNullish = false) { - for (const [sourceKey, sourceValue] of Object.entries(source)) { - if (target[sourceKey] instanceof Object && sourceValue) { - recursivelyAssign(target[sourceKey], sourceValue); - continue; - } - if ((sourceValue !== null && sourceValue !== undefined) || !ignoreNullish) { - target[sourceKey] = sourceValue; - continue; - } - } - return target; -} -exports.recursivelyAssign = recursivelyAssign; -function getContentTimestampWithFallback(event) { - var _a; - return (_a = location_1.M_TIMESTAMP.findIn(event.getContent())) !== null && _a !== void 0 ? _a : -1; -} -/** - * Sort events by their content m.ts property - * Latest timestamp first - */ -function sortEventsByLatestContentTimestamp(left, right) { - return getContentTimestampWithFallback(right) - getContentTimestampWithFallback(left); -} -exports.sortEventsByLatestContentTimestamp = sortEventsByLatestContentTimestamp; -function isSupportedReceiptType(receiptType) { - return [read_receipts_1.ReceiptType.Read, read_receipts_1.ReceiptType.ReadPrivate].includes(receiptType); -} -exports.isSupportedReceiptType = isSupportedReceiptType; -/** - * Determines whether two maps are equal. - * @param eq - The equivalence relation to compare values by. Defaults to strict equality. - */ -function mapsEqual(x, y, eq = (v1, v2) => v1 === v2) { - if (x.size !== y.size) - return false; - for (const [k, v1] of x) { - const v2 = y.get(k); - if (v2 === undefined || !eq(v1, v2)) - return false; - } - return true; -} -exports.mapsEqual = mapsEqual; -function processMapToObjectValue(value) { - if (value instanceof Map) { - // Value is a Map. Recursively map it to an object. - return recursiveMapToObject(value); - } - else if (Array.isArray(value)) { - // Value is an Array. Recursively map the value (e.g. to cover Array of Arrays). - return value.map((v) => processMapToObjectValue(v)); - } - else { - return value; - } -} -/** - * Recursively converts Maps to plain objects. - * Also supports sub-lists of Maps. - */ -function recursiveMapToObject(map) { - const targetMap = new Map(); - for (const [key, value] of map) { - targetMap.set(key, processMapToObjectValue(value)); - } - return Object.fromEntries(targetMap.entries()); -} -exports.recursiveMapToObject = recursiveMapToObject; -function unsafeProp(prop) { - return prop === "__proto__" || prop === "prototype" || prop === "constructor"; -} -exports.unsafeProp = unsafeProp; -function safeSet(obj, prop, value) { - if (unsafeProp(prop)) { - throw new Error("Trying to modify prototype or constructor"); - } - obj[prop] = value; -} -exports.safeSet = safeSet; -function noUnsafeEventProps(event) { - return !(unsafeProp(event.room_id) || - unsafeProp(event.sender) || - unsafeProp(event.user_id) || - unsafeProp(event.event_id)); -} -exports.noUnsafeEventProps = noUnsafeEventProps; -class MapWithDefault extends Map { - constructor(createDefault) { - super(); - this.createDefault = createDefault; - } - /** - * Returns the value if the key already exists. - * If not, it creates a new value under that key using the ctor callback and returns it. - */ - getOrCreate(key) { - if (!this.has(key)) { - this.set(key, this.createDefault()); - } - return this.get(key); - } -} -exports.MapWithDefault = MapWithDefault; - -}).call(this)}).call(this,require("timers").setImmediate) - -},{"./@types/location":308,"./@types/read_receipts":311,"p-retry":225,"timers":280,"unhomoglyph":282}],417:[function(require,module,exports){ -"use strict"; -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.releaseContext = exports.acquireContext = void 0; -let audioContext = null; -let refCount = 0; -/** - * Acquires a reference to the shared AudioContext. - * It's highly recommended to reuse this AudioContext rather than creating your - * own, because multiple AudioContexts can be problematic in some browsers. - * Make sure to call releaseContext when you're done using it. - * @returns The shared AudioContext - */ -const acquireContext = () => { - if (audioContext === null) - audioContext = new AudioContext(); - refCount++; - return audioContext; -}; -exports.acquireContext = acquireContext; -/** - * Signals that one of the references to the shared AudioContext has been - * released, allowing the context and associated hardware resources to be - * cleaned up if nothing else is using it. - */ -const releaseContext = () => { - refCount--; - if (refCount === 0) { - audioContext === null || audioContext === void 0 ? void 0 : audioContext.close(); - audioContext = null; - } -}; -exports.releaseContext = releaseContext; - -},{}],418:[function(require,module,exports){ -(function (process){(function (){ -"use strict"; -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 New Vector Ltd -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. -Copyright 2021 - 2022 Šimon Brandner - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createNewMatrixCall = exports.supportsMatrixCall = exports.setTracksEnabled = exports.MatrixCall = exports.genCallID = exports.CallError = exports.CallErrorCode = exports.CallEvent = exports.CallParty = exports.CallDirection = exports.CallType = exports.CallState = void 0; -/** - * This is an internal module. See {@link createNewMatrixCall} for the public API. - */ -const uuid_1 = require("uuid"); -const sdp_transform_1 = require("sdp-transform"); -const logger_1 = require("../logger"); -const utils = __importStar(require("../utils")); -const event_1 = require("../@types/event"); -const randomstring_1 = require("../randomstring"); -const callEventTypes_1 = require("./callEventTypes"); -const callFeed_1 = require("./callFeed"); -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -const deviceinfo_1 = require("../crypto/deviceinfo"); -const groupCall_1 = require("./groupCall"); -const http_api_1 = require("../http-api"); -var MediaType; -(function (MediaType) { - MediaType["AUDIO"] = "audio"; - MediaType["VIDEO"] = "video"; -})(MediaType || (MediaType = {})); -var CodecName; -(function (CodecName) { - CodecName["OPUS"] = "opus"; - // add more as needed -})(CodecName || (CodecName = {})); -var CallState; -(function (CallState) { - CallState["Fledgling"] = "fledgling"; - CallState["InviteSent"] = "invite_sent"; - CallState["WaitLocalMedia"] = "wait_local_media"; - CallState["CreateOffer"] = "create_offer"; - CallState["CreateAnswer"] = "create_answer"; - CallState["Connecting"] = "connecting"; - CallState["Connected"] = "connected"; - CallState["Ringing"] = "ringing"; - CallState["Ended"] = "ended"; -})(CallState = exports.CallState || (exports.CallState = {})); -var CallType; -(function (CallType) { - CallType["Voice"] = "voice"; - CallType["Video"] = "video"; -})(CallType = exports.CallType || (exports.CallType = {})); -var CallDirection; -(function (CallDirection) { - CallDirection["Inbound"] = "inbound"; - CallDirection["Outbound"] = "outbound"; -})(CallDirection = exports.CallDirection || (exports.CallDirection = {})); -var CallParty; -(function (CallParty) { - CallParty["Local"] = "local"; - CallParty["Remote"] = "remote"; -})(CallParty = exports.CallParty || (exports.CallParty = {})); -var CallEvent; -(function (CallEvent) { - CallEvent["Hangup"] = "hangup"; - CallEvent["State"] = "state"; - CallEvent["Error"] = "error"; - CallEvent["Replaced"] = "replaced"; - // The value of isLocalOnHold() has changed - CallEvent["LocalHoldUnhold"] = "local_hold_unhold"; - // The value of isRemoteOnHold() has changed - CallEvent["RemoteHoldUnhold"] = "remote_hold_unhold"; - // backwards compat alias for LocalHoldUnhold: remove in a major version bump - CallEvent["HoldUnhold"] = "hold_unhold"; - // Feeds have changed - CallEvent["FeedsChanged"] = "feeds_changed"; - CallEvent["AssertedIdentityChanged"] = "asserted_identity_changed"; - CallEvent["LengthChanged"] = "length_changed"; - CallEvent["DataChannel"] = "datachannel"; - CallEvent["SendVoipEvent"] = "send_voip_event"; -})(CallEvent = exports.CallEvent || (exports.CallEvent = {})); -var CallErrorCode; -(function (CallErrorCode) { - /** The user chose to end the call */ - CallErrorCode["UserHangup"] = "user_hangup"; - /** An error code when the local client failed to create an offer. */ - CallErrorCode["LocalOfferFailed"] = "local_offer_failed"; - /** - * An error code when there is no local mic/camera to use. This may be because - * the hardware isn't plugged in, or the user has explicitly denied access. - */ - CallErrorCode["NoUserMedia"] = "no_user_media"; - /** - * Error code used when a call event failed to send - * because unknown devices were present in the room - */ - CallErrorCode["UnknownDevices"] = "unknown_devices"; - /** - * Error code used when we fail to send the invite - * for some reason other than there being unknown devices - */ - CallErrorCode["SendInvite"] = "send_invite"; - /** - * An answer could not be created - */ - CallErrorCode["CreateAnswer"] = "create_answer"; - /** - * An offer could not be created - */ - CallErrorCode["CreateOffer"] = "create_offer"; - /** - * Error code used when we fail to send the answer - * for some reason other than there being unknown devices - */ - CallErrorCode["SendAnswer"] = "send_answer"; - /** - * The session description from the other side could not be set - */ - CallErrorCode["SetRemoteDescription"] = "set_remote_description"; - /** - * The session description from this side could not be set - */ - CallErrorCode["SetLocalDescription"] = "set_local_description"; - /** - * A different device answered the call - */ - CallErrorCode["AnsweredElsewhere"] = "answered_elsewhere"; - /** - * No media connection could be established to the other party - */ - CallErrorCode["IceFailed"] = "ice_failed"; - /** - * The invite timed out whilst waiting for an answer - */ - CallErrorCode["InviteTimeout"] = "invite_timeout"; - /** - * The call was replaced by another call - */ - CallErrorCode["Replaced"] = "replaced"; - /** - * Signalling for the call could not be sent (other than the initial invite) - */ - CallErrorCode["SignallingFailed"] = "signalling_timeout"; - /** - * The remote party is busy - */ - CallErrorCode["UserBusy"] = "user_busy"; - /** - * We transferred the call off to somewhere else - */ - CallErrorCode["Transferred"] = "transferred"; - /** - * A call from the same user was found with a new session id - */ - CallErrorCode["NewSession"] = "new_session"; -})(CallErrorCode = exports.CallErrorCode || (exports.CallErrorCode = {})); -/** - * The version field that we set in m.call.* events - */ -const VOIP_PROTO_VERSION = "1"; -/** The fallback ICE server to use for STUN or TURN protocols. */ -const FALLBACK_ICE_SERVER = "stun:turn.matrix.org"; -/** The length of time a call can be ringing for. */ -const CALL_TIMEOUT_MS = 60 * 1000; // ms -/** The time after which we increment callLength */ -const CALL_LENGTH_INTERVAL = 1000; // ms -/** The time after which we end the call, if ICE got disconnected */ -const ICE_DISCONNECTED_TIMEOUT = 30 * 1000; // ms -class CallError extends Error { - constructor(code, msg, err) { - // Still don't think there's any way to have proper nested errors - super(msg + ": " + err); - this.code = code; - } -} -exports.CallError = CallError; -function genCallID() { - return Date.now().toString() + (0, randomstring_1.randomString)(16); -} -exports.genCallID = genCallID; -function getCodecParamMods(isPtt) { - const mods = [ - { - mediaType: "audio", - codec: "opus", - enableDtx: true, - maxAverageBitrate: isPtt ? 12000 : undefined, - }, - ]; - return mods; -} -// generates keys for the map of transceivers -// kind is unfortunately a string rather than MediaType as this is the type of -// track.kind -function getTransceiverKey(purpose, kind) { - return purpose + ":" + kind; -} -class MatrixCall extends typed_event_emitter_1.TypedEventEmitter { - /** - * Construct a new Matrix Call. - * @param opts - Config options. - */ - constructor(opts) { - var _a; - super(); - this.toDeviceSeq = 0; - // whether this call should have push-to-talk semantics - // This should be set by the consumer on incoming & outgoing calls. - this.isPtt = false; - this._state = CallState.Fledgling; - // A queue for candidates waiting to go out. - // We try to amalgamate candidates into a single candidate message where - // possible - this.candidateSendQueue = []; - this.candidateSendTries = 0; - this.candidatesEnded = false; - this.feeds = []; - // our transceivers for each purpose and type of media - this.transceivers = new Map(); - this.inviteOrAnswerSent = false; - this.waitForLocalAVStream = false; - this.removeTrackListeners = new Map(); - // The logic of when & if a call is on hold is nontrivial and explained in is*OnHold - // This flag represents whether we want the other party to be on hold - this.remoteOnHold = false; - // Perfect negotiation state: https://www.w3.org/TR/webrtc/#perfect-negotiation-example - this.makingOffer = false; - this.ignoreOffer = false; - // If candidates arrive before we've picked an opponent (which, in particular, - // will happen if the opponent sends candidates eagerly before the user answers - // the call) we buffer them up here so we can then add the ones from the party we pick - this.remoteCandidateBuffer = new Map(); - /** - * Internal - */ - this.gotLocalIceCandidate = (event) => { - if (event.candidate) { - if (this.candidatesEnded) { - logger_1.logger.warn(`Call ${this.callId} gotLocalIceCandidate() got candidate after candidates have ended - ignoring!`); - return; - } - logger_1.logger.debug(`Call ${this.callId} got local ICE ${event.candidate.sdpMid} ${event.candidate.candidate}`); - if (this.callHasEnded()) - return; - // As with the offer, note we need to make a copy of this object, not - // pass the original: that broke in Chrome ~m43. - if (event.candidate.candidate === "") { - this.queueCandidate(null); - } - else { - this.queueCandidate(event.candidate); - } - } - }; - this.onIceGatheringStateChange = (event) => { - var _a; - logger_1.logger.debug(`Call ${this.callId} onIceGatheringStateChange() ice gathering state changed to ${this.peerConn.iceGatheringState}`); - if (((_a = this.peerConn) === null || _a === void 0 ? void 0 : _a.iceGatheringState) === "complete") { - this.queueCandidate(null); - } - }; - this.getLocalOfferFailed = (err) => { - logger_1.logger.error(`Call ${this.callId} getLocalOfferFailed() running`, err); - this.emit(CallEvent.Error, new CallError(CallErrorCode.LocalOfferFailed, "Failed to get local offer!", err), this); - this.terminate(CallParty.Local, CallErrorCode.LocalOfferFailed, false); - }; - this.getUserMediaFailed = (err) => { - if (this.successor) { - this.successor.getUserMediaFailed(err); - return; - } - logger_1.logger.warn(`Call ${this.callId} getUserMediaFailed() failed to get user media - ending call`, err); - this.emit(CallEvent.Error, new CallError(CallErrorCode.NoUserMedia, "Couldn't start capturing media! Is your microphone set up and " + "does this app have permission?", err), this); - this.terminate(CallParty.Local, CallErrorCode.NoUserMedia, false); - }; - this.onIceConnectionStateChanged = () => { - var _a, _b, _c, _d, _e, _f; - if (this.callHasEnded()) { - return; // because ICE can still complete as we're ending the call - } - logger_1.logger.debug(`Call ${this.callId} onIceConnectionStateChanged() running (state=${(_a = this.peerConn) === null || _a === void 0 ? void 0 : _a.iceConnectionState})`); - // ideally we'd consider the call to be connected when we get media but - // chrome doesn't implement any of the 'onstarted' events yet - if (["connected", "completed"].includes((_c = (_b = this.peerConn) === null || _b === void 0 ? void 0 : _b.iceConnectionState) !== null && _c !== void 0 ? _c : "")) { - clearTimeout(this.iceDisconnectedTimeout); - this.iceDisconnectedTimeout = undefined; - this.state = CallState.Connected; - if (!this.callLengthInterval && !this.callStartTime) { - this.callStartTime = Date.now(); - this.callLengthInterval = setInterval(() => { - this.emit(CallEvent.LengthChanged, Math.round((Date.now() - this.callStartTime) / 1000), this); - }, CALL_LENGTH_INTERVAL); - } - } - else if (((_d = this.peerConn) === null || _d === void 0 ? void 0 : _d.iceConnectionState) == "failed") { - // Firefox for Android does not yet have support for restartIce() - // (the types say it's always defined though, so we have to cast - // to prevent typescript from warning). - if ((_e = this.peerConn) === null || _e === void 0 ? void 0 : _e.restartIce) { - this.candidatesEnded = false; - this.peerConn.restartIce(); - } - else { - logger_1.logger.info(`Call ${this.callId} onIceConnectionStateChanged() hanging up call (ICE failed and no ICE restart method)`); - this.hangup(CallErrorCode.IceFailed, false); - } - } - else if (((_f = this.peerConn) === null || _f === void 0 ? void 0 : _f.iceConnectionState) == "disconnected") { - this.iceDisconnectedTimeout = setTimeout(() => { - logger_1.logger.info(`Call ${this.callId} onIceConnectionStateChanged() hanging up call (ICE disconnected for too long)`); - this.hangup(CallErrorCode.IceFailed, false); - }, ICE_DISCONNECTED_TIMEOUT); - this.state = CallState.Connecting; - } - // In PTT mode, override feed status to muted when we lose connection to - // the peer, since we don't want to block the line if they're not saying anything. - // Experimenting in Chrome, this happens after 5 or 6 seconds, which is probably - // fast enough. - if (this.isPtt && ["failed", "disconnected"].includes(this.peerConn.iceConnectionState)) { - for (const feed of this.getRemoteFeeds()) { - feed.setAudioVideoMuted(true, true); - } - } - }; - this.onSignallingStateChanged = () => { - var _a; - logger_1.logger.debug(`Call ${this.callId} onSignallingStateChanged() running (state=${(_a = this.peerConn) === null || _a === void 0 ? void 0 : _a.signalingState})`); - }; - this.onTrack = (ev) => { - if (ev.streams.length === 0) { - logger_1.logger.warn(`Call ${this.callId} onTrack() called with streamless track streamless (kind=${ev.track.kind})`); - return; - } - const stream = ev.streams[0]; - this.pushRemoteFeed(stream); - if (!this.removeTrackListeners.has(stream)) { - const onRemoveTrack = () => { - if (stream.getTracks().length === 0) { - logger_1.logger.info(`Call ${this.callId} onTrack() removing track (streamId=${stream.id})`); - this.deleteFeedByStream(stream); - stream.removeEventListener("removetrack", onRemoveTrack); - this.removeTrackListeners.delete(stream); - } - }; - stream.addEventListener("removetrack", onRemoveTrack); - this.removeTrackListeners.set(stream, onRemoveTrack); - } - }; - this.onDataChannel = (ev) => { - this.emit(CallEvent.DataChannel, ev.channel, this); - }; - this.onNegotiationNeeded = () => __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info(`Call ${this.callId} onNegotiationNeeded() negotiation is needed!`); - if (this.state !== CallState.CreateOffer && this.opponentVersion === 0) { - logger_1.logger.info(`Call ${this.callId} onNegotiationNeeded() opponent does not support renegotiation: ignoring negotiationneeded event`); - return; - } - this.queueGotLocalOffer(); - }); - this.onHangupReceived = (msg) => { - logger_1.logger.debug(`Call ${this.callId} onHangupReceived() running`); - // party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen - // a partner yet but we're treating the hangup as a reject as per VoIP v0) - if (this.partyIdMatches(msg) || this.state === CallState.Ringing) { - // default reason is user_hangup - this.terminate(CallParty.Remote, msg.reason || CallErrorCode.UserHangup, true); - } - else { - logger_1.logger.info(`Call ${this.callId} onHangupReceived() ignoring message from party ID ${msg.party_id}: our partner is ${this.opponentPartyId}`); - } - }; - this.onRejectReceived = (msg) => { - logger_1.logger.debug(`Call ${this.callId} onRejectReceived() running`); - // No need to check party_id for reject because if we'd received either - // an answer or reject, we wouldn't be in state InviteSent - const shouldTerminate = - // reject events also end the call if it's ringing: it's another of - // our devices rejecting the call. - [CallState.InviteSent, CallState.Ringing].includes(this.state) || - // also if we're in the init state and it's an inbound call, since - // this means we just haven't entered the ringing state yet - (this.state === CallState.Fledgling && this.direction === CallDirection.Inbound); - if (shouldTerminate) { - this.terminate(CallParty.Remote, msg.reason || CallErrorCode.UserHangup, true); - } - else { - logger_1.logger.debug(`Call ${this.callId} onRejectReceived() called in wrong state (state=${this.state})`); - } - }; - this.onAnsweredElsewhere = (msg) => { - logger_1.logger.debug(`Call ${this.callId} onAnsweredElsewhere() running`); - this.terminate(CallParty.Remote, CallErrorCode.AnsweredElsewhere, true); - }; - this.roomId = opts.roomId; - this.invitee = opts.invitee; - this.client = opts.client; - if (!this.client.deviceId) - throw new Error("Client must have a device ID to start calls"); - this.forceTURN = (_a = opts.forceTURN) !== null && _a !== void 0 ? _a : false; - this.ourPartyId = this.client.deviceId; - this.opponentDeviceId = opts.opponentDeviceId; - this.opponentSessionId = opts.opponentSessionId; - this.groupCallId = opts.groupCallId; - // Array of Objects with urls, username, credential keys - this.turnServers = opts.turnServers || []; - if (this.turnServers.length === 0 && this.client.isFallbackICEServerAllowed()) { - this.turnServers.push({ - urls: [FALLBACK_ICE_SERVER], - }); - } - for (const server of this.turnServers) { - utils.checkObjectHasKeys(server, ["urls"]); - } - this.callId = genCallID(); - // If the Client provides calls without audio and video we need a datachannel for a webrtc connection - this.isOnlyDataChannelAllowed = this.client.isVoipWithNoMediaAllowed; - } - /** - * Place a voice call to this room. - * @throws If you have not specified a listener for 'error' events. - */ - placeVoiceCall() { - return __awaiter(this, void 0, void 0, function* () { - yield this.placeCall(true, false); - }); - } - /** - * Place a video call to this room. - * @throws If you have not specified a listener for 'error' events. - */ - placeVideoCall() { - return __awaiter(this, void 0, void 0, function* () { - yield this.placeCall(true, true); - }); - } - /** - * Create a datachannel using this call's peer connection. - * @param label - A human readable label for this datachannel - * @param options - An object providing configuration options for the data channel. - */ - createDataChannel(label, options) { - const dataChannel = this.peerConn.createDataChannel(label, options); - this.emit(CallEvent.DataChannel, dataChannel, this); - return dataChannel; - } - getOpponentMember() { - return this.opponentMember; - } - getOpponentDeviceId() { - return this.opponentDeviceId; - } - getOpponentSessionId() { - return this.opponentSessionId; - } - opponentCanBeTransferred() { - return Boolean(this.opponentCaps && this.opponentCaps["m.call.transferee"]); - } - opponentSupportsDTMF() { - return Boolean(this.opponentCaps && this.opponentCaps["m.call.dtmf"]); - } - getRemoteAssertedIdentity() { - return this.remoteAssertedIdentity; - } - get state() { - return this._state; - } - set state(state) { - const oldState = this._state; - this._state = state; - this.emit(CallEvent.State, state, oldState, this); - } - get type() { - // we may want to look for a video receiver here rather than a track to match the - // sender behaviour, although in practice they should be the same thing - return this.hasUserMediaVideoSender || this.hasRemoteUserMediaVideoTrack ? CallType.Video : CallType.Voice; - } - get hasLocalUserMediaVideoTrack() { - var _a; - return !!((_a = this.localUsermediaStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks().length); - } - get hasRemoteUserMediaVideoTrack() { - return this.getRemoteFeeds().some((feed) => { - var _a; - return feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Usermedia && ((_a = feed.stream) === null || _a === void 0 ? void 0 : _a.getVideoTracks().length); - }); - } - get hasLocalUserMediaAudioTrack() { - var _a; - return !!((_a = this.localUsermediaStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().length); - } - get hasRemoteUserMediaAudioTrack() { - return this.getRemoteFeeds().some((feed) => { - var _a; - return feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Usermedia && !!((_a = feed.stream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().length); - }); - } - get hasUserMediaAudioSender() { - var _a; - return Boolean((_a = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, "audio"))) === null || _a === void 0 ? void 0 : _a.sender); - } - get hasUserMediaVideoSender() { - var _a; - return Boolean((_a = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, "video"))) === null || _a === void 0 ? void 0 : _a.sender); - } - get localUsermediaFeed() { - return this.getLocalFeeds().find((feed) => feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Usermedia); - } - get localScreensharingFeed() { - return this.getLocalFeeds().find((feed) => feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare); - } - get localUsermediaStream() { - var _a; - return (_a = this.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.stream; - } - get localScreensharingStream() { - var _a; - return (_a = this.localScreensharingFeed) === null || _a === void 0 ? void 0 : _a.stream; - } - get remoteUsermediaFeed() { - return this.getRemoteFeeds().find((feed) => feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Usermedia); - } - get remoteScreensharingFeed() { - return this.getRemoteFeeds().find((feed) => feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare); - } - get remoteUsermediaStream() { - var _a; - return (_a = this.remoteUsermediaFeed) === null || _a === void 0 ? void 0 : _a.stream; - } - get remoteScreensharingStream() { - var _a; - return (_a = this.remoteScreensharingFeed) === null || _a === void 0 ? void 0 : _a.stream; - } - getFeedByStreamId(streamId) { - return this.getFeeds().find((feed) => feed.stream.id === streamId); - } - /** - * Returns an array of all CallFeeds - * @returns CallFeeds - */ - getFeeds() { - return this.feeds; - } - /** - * Returns an array of all local CallFeeds - * @returns local CallFeeds - */ - getLocalFeeds() { - return this.feeds.filter((feed) => feed.isLocal()); - } - /** - * Returns an array of all remote CallFeeds - * @returns remote CallFeeds - */ - getRemoteFeeds() { - return this.feeds.filter((feed) => !feed.isLocal()); - } - initOpponentCrypto() { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - if (!this.opponentDeviceId) - return; - if (!this.client.getUseE2eForGroupCall()) - return; - // It's possible to want E2EE and yet not have the means to manage E2EE - // ourselves (for example if the client is a RoomWidgetClient) - if (!this.client.isCryptoEnabled()) { - // All we know is the device ID - this.opponentDeviceInfo = new deviceinfo_1.DeviceInfo(this.opponentDeviceId); - return; - } - // if we've got to this point, we do want to init crypto, so throw if we can't - if (!this.client.crypto) - throw new Error("Crypto is not initialised."); - const userId = this.invitee || ((_a = this.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId); - if (!userId) - throw new Error("Couldn't find opponent user ID to init crypto"); - const deviceInfoMap = yield this.client.crypto.deviceList.downloadKeys([userId], false); - this.opponentDeviceInfo = (_b = deviceInfoMap.get(userId)) === null || _b === void 0 ? void 0 : _b.get(this.opponentDeviceId); - if (this.opponentDeviceInfo === undefined) { - throw new groupCall_1.GroupCallUnknownDeviceError(userId); - } - }); - } - /** - * Generates and returns localSDPStreamMetadata - * @returns localSDPStreamMetadata - */ - getLocalSDPStreamMetadata(updateStreamIds = false) { - const metadata = {}; - for (const localFeed of this.getLocalFeeds()) { - if (updateStreamIds) { - localFeed.sdpMetadataStreamId = localFeed.stream.id; - } - metadata[localFeed.sdpMetadataStreamId] = { - purpose: localFeed.purpose, - audio_muted: localFeed.isAudioMuted(), - video_muted: localFeed.isVideoMuted(), - }; - } - return metadata; - } - /** - * Returns true if there are no incoming feeds, - * otherwise returns false - * @returns no incoming feeds - */ - noIncomingFeeds() { - return !this.feeds.some((feed) => !feed.isLocal()); - } - pushRemoteFeed(stream) { - // Fallback to old behavior if the other side doesn't support SDPStreamMetadata - if (!this.opponentSupportsSDPStreamMetadata()) { - this.pushRemoteFeedWithoutMetadata(stream); - return; - } - const userId = this.getOpponentMember().userId; - const purpose = this.remoteSDPStreamMetadata[stream.id].purpose; - const audioMuted = this.remoteSDPStreamMetadata[stream.id].audio_muted; - const videoMuted = this.remoteSDPStreamMetadata[stream.id].video_muted; - if (!purpose) { - logger_1.logger.warn(`Call ${this.callId} pushRemoteFeed() ignoring stream because we didn't get any metadata about it (streamId=${stream.id})`); - return; - } - if (this.getFeedByStreamId(stream.id)) { - logger_1.logger.warn(`Call ${this.callId} pushRemoteFeed() ignoring stream because we already have a feed for it (streamId=${stream.id})`); - return; - } - this.feeds.push(new callFeed_1.CallFeed({ - client: this.client, - call: this, - roomId: this.roomId, - userId, - deviceId: this.getOpponentDeviceId(), - stream, - purpose, - audioMuted, - videoMuted, - })); - this.emit(CallEvent.FeedsChanged, this.feeds, this); - logger_1.logger.info(`Call ${this.callId} pushRemoteFeed() pushed stream (streamId=${stream.id}, active=${stream.active}, purpose=${purpose})`); - } - /** - * This method is used ONLY if the other client doesn't support sending SDPStreamMetadata - */ - pushRemoteFeedWithoutMetadata(stream) { - var _a; - const userId = this.getOpponentMember().userId; - // We can guess the purpose here since the other client can only send one stream - const purpose = callEventTypes_1.SDPStreamMetadataPurpose.Usermedia; - const oldRemoteStream = (_a = this.feeds.find((feed) => !feed.isLocal())) === null || _a === void 0 ? void 0 : _a.stream; - // Note that we check by ID and always set the remote stream: Chrome appears - // to make new stream objects when transceiver directionality is changed and the 'active' - // status of streams change - Dave - // If we already have a stream, check this stream has the same id - if (oldRemoteStream && stream.id !== oldRemoteStream.id) { - logger_1.logger.warn(`Call ${this.callId} pushRemoteFeedWithoutMetadata() ignoring new stream because we already have stream (streamId=${stream.id})`); - return; - } - if (this.getFeedByStreamId(stream.id)) { - logger_1.logger.warn(`Call ${this.callId} pushRemoteFeedWithoutMetadata() ignoring stream because we already have a feed for it (streamId=${stream.id})`); - return; - } - this.feeds.push(new callFeed_1.CallFeed({ - client: this.client, - call: this, - roomId: this.roomId, - audioMuted: false, - videoMuted: false, - userId, - deviceId: this.getOpponentDeviceId(), - stream, - purpose, - })); - this.emit(CallEvent.FeedsChanged, this.feeds, this); - logger_1.logger.info(`Call ${this.callId} pushRemoteFeedWithoutMetadata() pushed stream (streamId=${stream.id}, active=${stream.active})`); - } - pushNewLocalFeed(stream, purpose, addToPeerConnection = true) { - const userId = this.client.getUserId(); - // Tracks don't always start off enabled, eg. chrome will give a disabled - // audio track if you ask for user media audio and already had one that - // you'd set to disabled (presumably because it clones them internally). - setTracksEnabled(stream.getAudioTracks(), true); - setTracksEnabled(stream.getVideoTracks(), true); - if (this.getFeedByStreamId(stream.id)) { - logger_1.logger.warn(`Call ${this.callId} pushNewLocalFeed() ignoring stream because we already have a feed for it (streamId=${stream.id})`); - return; - } - this.pushLocalFeed(new callFeed_1.CallFeed({ - client: this.client, - roomId: this.roomId, - audioMuted: false, - videoMuted: false, - userId, - deviceId: this.getOpponentDeviceId(), - stream, - purpose, - }), addToPeerConnection); - } - /** - * Pushes supplied feed to the call - * @param callFeed - to push - * @param addToPeerConnection - whether to add the tracks to the peer connection - */ - pushLocalFeed(callFeed, addToPeerConnection = true) { - if (this.feeds.some((feed) => callFeed.stream.id === feed.stream.id)) { - logger_1.logger.info(`Call ${this.callId} pushLocalFeed() ignoring duplicate local stream (streamId=${callFeed.stream.id})`); - return; - } - this.feeds.push(callFeed); - if (addToPeerConnection) { - for (const track of callFeed.stream.getTracks()) { - logger_1.logger.info(`Call ${this.callId} pushLocalFeed() adding track to peer connection (id=${track.id}, kind=${track.kind}, streamId=${callFeed.stream.id}, streamPurpose=${callFeed.purpose}, enabled=${track.enabled})`); - const tKey = getTransceiverKey(callFeed.purpose, track.kind); - if (this.transceivers.has(tKey)) { - // we already have a sender, so we re-use it. We try to re-use transceivers as much - // as possible because they can't be removed once added, so otherwise they just - // accumulate which makes the SDP very large very quickly: in fact it only takes - // about 6 video tracks to exceed the maximum size of an Olm-encrypted - // Matrix event. - const transceiver = this.transceivers.get(tKey); - transceiver.sender.replaceTrack(track); - // set the direction to indicate we're going to start sending again - // (this will trigger the re-negotiation) - transceiver.direction = transceiver.direction === "inactive" ? "sendonly" : "sendrecv"; - } - else { - // create a new one. We need to use addTrack rather addTransceiver for this because firefox - // doesn't yet implement RTCRTPSender.setStreams() - // (https://bugzilla.mozilla.org/show_bug.cgi?id=1510802) so we'd have no way to group the - // two tracks together into a stream. - const newSender = this.peerConn.addTrack(track, callFeed.stream); - // now go & fish for the new transceiver - const newTransceiver = this.peerConn.getTransceivers().find((t) => t.sender === newSender); - if (newTransceiver) { - this.transceivers.set(tKey, newTransceiver); - } - else { - logger_1.logger.warn(`Call ${this.callId} pushLocalFeed() didn't find a matching transceiver after adding track!`); - } - } - } - } - logger_1.logger.info(`Call ${this.callId} pushLocalFeed() pushed stream (id=${callFeed.stream.id}, active=${callFeed.stream.active}, purpose=${callFeed.purpose})`); - this.emit(CallEvent.FeedsChanged, this.feeds, this); - } - /** - * Removes local call feed from the call and its tracks from the peer - * connection - * @param callFeed - to remove - */ - removeLocalFeed(callFeed) { - const audioTransceiverKey = getTransceiverKey(callFeed.purpose, "audio"); - const videoTransceiverKey = getTransceiverKey(callFeed.purpose, "video"); - for (const transceiverKey of [audioTransceiverKey, videoTransceiverKey]) { - // this is slightly mixing the track and transceiver API but is basically just shorthand. - // There is no way to actually remove a transceiver, so this just sets it to inactive - // (or recvonly) and replaces the source with nothing. - if (this.transceivers.has(transceiverKey)) { - const transceiver = this.transceivers.get(transceiverKey); - if (transceiver.sender) - this.peerConn.removeTrack(transceiver.sender); - } - } - if (callFeed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare) { - this.client.getMediaHandler().stopScreensharingStream(callFeed.stream); - } - this.deleteFeed(callFeed); - } - deleteAllFeeds() { - for (const feed of this.feeds) { - if (!feed.isLocal() || !this.groupCallId) { - feed.dispose(); - } - } - this.feeds = []; - this.emit(CallEvent.FeedsChanged, this.feeds, this); - } - deleteFeedByStream(stream) { - const feed = this.getFeedByStreamId(stream.id); - if (!feed) { - logger_1.logger.warn(`Call ${this.callId} deleteFeedByStream() didn't find the feed to delete (streamId=${stream.id})`); - return; - } - this.deleteFeed(feed); - } - deleteFeed(feed) { - feed.dispose(); - this.feeds.splice(this.feeds.indexOf(feed), 1); - this.emit(CallEvent.FeedsChanged, this.feeds, this); - } - // The typescript definitions have this type as 'any' :( - getCurrentCallStats() { - return __awaiter(this, void 0, void 0, function* () { - if (this.callHasEnded()) { - return this.callStatsAtEnd; - } - return this.collectCallStats(); - }); - } - collectCallStats() { - return __awaiter(this, void 0, void 0, function* () { - // This happens when the call fails before it starts. - // For example when we fail to get capture sources - if (!this.peerConn) - return; - const statsReport = yield this.peerConn.getStats(); - const stats = []; - statsReport.forEach((item) => { - stats.push(item); - }); - return stats; - }); - } - /** - * Configure this call from an invite event. Used by MatrixClient. - * @param event - The m.call.invite event - */ - initWithInvite(event) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const invite = event.getContent(); - this.direction = CallDirection.Inbound; - // make sure we have valid turn creds. Unless something's gone wrong, it should - // poll and keep the credentials valid so this should be instant. - const haveTurnCreds = yield this.client.checkTurnServers(); - if (!haveTurnCreds) { - logger_1.logger.warn(`Call ${this.callId} initWithInvite() failed to get TURN credentials! Proceeding with call anyway...`); - } - const sdpStreamMetadata = invite[callEventTypes_1.SDPStreamMetadataKey]; - if (sdpStreamMetadata) { - this.updateRemoteSDPStreamMetadata(sdpStreamMetadata); - } - else { - logger_1.logger.debug(`Call ${this.callId} initWithInvite() did not get any SDPStreamMetadata! Can not send/receive multiple streams`); - } - this.peerConn = this.createPeerConnection(); - // we must set the party ID before await-ing on anything: the call event - // handler will start giving us more call events (eg. candidates) so if - // we haven't set the party ID, we'll ignore them. - this.chooseOpponent(event); - yield this.initOpponentCrypto(); - try { - yield this.peerConn.setRemoteDescription(invite.offer); - yield this.addBufferedIceCandidates(); - } - catch (e) { - logger_1.logger.debug(`Call ${this.callId} initWithInvite() failed to set remote description`, e); - this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false); - return; - } - const remoteStream = (_a = this.feeds.find((feed) => !feed.isLocal())) === null || _a === void 0 ? void 0 : _a.stream; - // According to previous comments in this file, firefox at some point did not - // add streams until media started arriving on them. Testing latest firefox - // (81 at time of writing), this is no longer a problem, so let's do it the correct way. - // - // For example in case of no media webrtc connections like screen share only call we have to allow webrtc - // connections without remote media. In this case we always use a data channel. At the moment we allow as well - // only data channel as media in the WebRTC connection with this setup here. - if (!this.isOnlyDataChannelAllowed && (!remoteStream || remoteStream.getTracks().length === 0)) { - logger_1.logger.error(`Call ${this.callId} initWithInvite() no remote stream or no tracks after setting remote description!`); - this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false); - return; - } - this.state = CallState.Ringing; - if (event.getLocalAge()) { - // Time out the call if it's ringing for too long - const ringingTimer = setTimeout(() => { - var _a; - if (this.state == CallState.Ringing) { - logger_1.logger.debug(`Call ${this.callId} initWithInvite() invite has expired. Hanging up.`); - this.hangupParty = CallParty.Remote; // effectively - this.state = CallState.Ended; - this.stopAllMedia(); - if (this.peerConn.signalingState != "closed") { - this.peerConn.close(); - } - (_a = this.stats) === null || _a === void 0 ? void 0 : _a.removeStatsReportGatherer(this.callId); - this.emit(CallEvent.Hangup, this); - } - }, invite.lifetime - event.getLocalAge()); - const onState = (state) => { - if (state !== CallState.Ringing) { - clearTimeout(ringingTimer); - this.off(CallEvent.State, onState); - } - }; - this.on(CallEvent.State, onState); - } - }); - } - /** - * Configure this call from a hangup or reject event. Used by MatrixClient. - * @param event - The m.call.hangup event - */ - initWithHangup(event) { - // perverse as it may seem, sometimes we want to instantiate a call with a - // hangup message (because when getting the state of the room on load, events - // come in reverse order and we want to remember that a call has been hung up) - this.state = CallState.Ended; - } - shouldAnswerWithMediaType(wantedValue, valueOfTheOtherSide, type) { - if (wantedValue && !valueOfTheOtherSide) { - // TODO: Figure out how to do this - logger_1.logger.warn(`Call ${this.callId} shouldAnswerWithMediaType() unable to answer with ${type} because the other side isn't sending it either.`); - return false; - } - else if (!utils.isNullOrUndefined(wantedValue) && - wantedValue !== valueOfTheOtherSide && - !this.opponentSupportsSDPStreamMetadata()) { - logger_1.logger.warn(`Call ${this.callId} shouldAnswerWithMediaType() unable to answer with ${type}=${wantedValue} because the other side doesn't support it. Answering with ${type}=${valueOfTheOtherSide}.`); - return valueOfTheOtherSide; - } - return wantedValue !== null && wantedValue !== void 0 ? wantedValue : valueOfTheOtherSide; - } - /** - * Answer a call. - */ - answer(audio, video) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (this.inviteOrAnswerSent) - return; - // TODO: Figure out how to do this - if (audio === false && video === false) - throw new Error("You CANNOT answer a call without media"); - if (!this.localUsermediaStream && !this.waitForLocalAVStream) { - const prevState = this.state; - const answerWithAudio = this.shouldAnswerWithMediaType(audio, this.hasRemoteUserMediaAudioTrack, "audio"); - const answerWithVideo = this.shouldAnswerWithMediaType(video, this.hasRemoteUserMediaVideoTrack, "video"); - this.state = CallState.WaitLocalMedia; - this.waitForLocalAVStream = true; - try { - const stream = yield this.client.getMediaHandler().getUserMediaStream(answerWithAudio, answerWithVideo); - this.waitForLocalAVStream = false; - const usermediaFeed = new callFeed_1.CallFeed({ - client: this.client, - roomId: this.roomId, - userId: this.client.getUserId(), - deviceId: (_a = this.client.getDeviceId()) !== null && _a !== void 0 ? _a : undefined, - stream, - purpose: callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, - audioMuted: false, - videoMuted: false, - }); - const feeds = [usermediaFeed]; - if (this.localScreensharingFeed) { - feeds.push(this.localScreensharingFeed); - } - this.answerWithCallFeeds(feeds); - } - catch (e) { - if (answerWithVideo) { - // Try to answer without video - logger_1.logger.warn(`Call ${this.callId} answer() failed to getUserMedia(), trying to getUserMedia() without video`); - this.state = prevState; - this.waitForLocalAVStream = false; - yield this.answer(answerWithAudio, false); - } - else { - this.getUserMediaFailed(e); - return; - } - } - } - else if (this.waitForLocalAVStream) { - this.state = CallState.WaitLocalMedia; - } - }); - } - answerWithCallFeeds(callFeeds) { - if (this.inviteOrAnswerSent) - return; - this.queueGotCallFeedsForAnswer(callFeeds); - } - /** - * Replace this call with a new call, e.g. for glare resolution. Used by - * MatrixClient. - * @param newCall - The new call. - */ - replacedBy(newCall) { - logger_1.logger.debug(`Call ${this.callId} replacedBy() running (newCallId=${newCall.callId})`); - if (this.state === CallState.WaitLocalMedia) { - logger_1.logger.debug(`Call ${this.callId} replacedBy() telling new call to wait for local media (newCallId=${newCall.callId})`); - newCall.waitForLocalAVStream = true; - } - else if ([CallState.CreateOffer, CallState.InviteSent].includes(this.state)) { - if (newCall.direction === CallDirection.Outbound) { - newCall.queueGotCallFeedsForAnswer([]); - } - else { - logger_1.logger.debug(`Call ${this.callId} replacedBy() handing local stream to new call(newCallId=${newCall.callId})`); - newCall.queueGotCallFeedsForAnswer(this.getLocalFeeds().map((feed) => feed.clone())); - } - } - this.successor = newCall; - this.emit(CallEvent.Replaced, newCall, this); - this.hangup(CallErrorCode.Replaced, true); - } - /** - * Hangup a call. - * @param reason - The reason why the call is being hung up. - * @param suppressEvent - True to suppress emitting an event. - */ - hangup(reason, suppressEvent) { - if (this.callHasEnded()) - return; - logger_1.logger.debug(`Call ${this.callId} hangup() ending call (reason=${reason})`); - this.terminate(CallParty.Local, reason, !suppressEvent); - // We don't want to send hangup here if we didn't even get to sending an invite - if ([CallState.Fledgling, CallState.WaitLocalMedia].includes(this.state)) - return; - const content = {}; - // Don't send UserHangup reason to older clients - if ((this.opponentVersion && this.opponentVersion !== 0) || reason !== CallErrorCode.UserHangup) { - content["reason"] = reason; - } - this.sendVoipEvent(event_1.EventType.CallHangup, content); - } - /** - * Reject a call - * This used to be done by calling hangup, but is a separate method and protocol - * event as of MSC2746. - */ - reject() { - if (this.state !== CallState.Ringing) { - throw Error("Call must be in 'ringing' state to reject!"); - } - if (this.opponentVersion === 0) { - logger_1.logger.info(`Call ${this.callId} reject() opponent version is less than 1: sending hangup instead of reject (opponentVersion=${this.opponentVersion})`); - this.hangup(CallErrorCode.UserHangup, true); - return; - } - logger_1.logger.debug("Rejecting call: " + this.callId); - this.terminate(CallParty.Local, CallErrorCode.UserHangup, true); - this.sendVoipEvent(event_1.EventType.CallReject, {}); - } - /** - * Adds an audio and/or video track - upgrades the call - * @param audio - should add an audio track - * @param video - should add an video track - */ - upgradeCall(audio, video) { - return __awaiter(this, void 0, void 0, function* () { - // We don't do call downgrades - if (!audio && !video) - return; - if (!this.opponentSupportsSDPStreamMetadata()) - return; - try { - logger_1.logger.debug(`Call ${this.callId} upgradeCall() upgrading call (audio=${audio}, video=${video})`); - const getAudio = audio || this.hasLocalUserMediaAudioTrack; - const getVideo = video || this.hasLocalUserMediaVideoTrack; - // updateLocalUsermediaStream() will take the tracks, use them as - // replacement and throw the stream away, so it isn't reusable - const stream = yield this.client.getMediaHandler().getUserMediaStream(getAudio, getVideo, false); - yield this.updateLocalUsermediaStream(stream, audio, video); - } - catch (error) { - logger_1.logger.error(`Call ${this.callId} upgradeCall() failed to upgrade the call`, error); - this.emit(CallEvent.Error, new CallError(CallErrorCode.NoUserMedia, "Failed to get camera access: ", error), this); - } - }); - } - /** - * Returns true if this.remoteSDPStreamMetadata is defined, otherwise returns false - * @returns can screenshare - */ - opponentSupportsSDPStreamMetadata() { - return Boolean(this.remoteSDPStreamMetadata); - } - /** - * If there is a screensharing stream returns true, otherwise returns false - * @returns is screensharing - */ - isScreensharing() { - return Boolean(this.localScreensharingStream); - } - /** - * Starts/stops screensharing - * @param enabled - the desired screensharing state - * @param desktopCapturerSourceId - optional id of the desktop capturer source to use - * @returns new screensharing state - */ - setScreensharingEnabled(enabled, opts) { - return __awaiter(this, void 0, void 0, function* () { - // Skip if there is nothing to do - if (enabled && this.isScreensharing()) { - logger_1.logger.warn(`Call ${this.callId} setScreensharingEnabled() there is already a screensharing stream - there is nothing to do!`); - return true; - } - else if (!enabled && !this.isScreensharing()) { - logger_1.logger.warn(`Call ${this.callId} setScreensharingEnabled() there already isn't a screensharing stream - there is nothing to do!`); - return false; - } - // Fallback to replaceTrack() - if (!this.opponentSupportsSDPStreamMetadata()) { - return this.setScreensharingEnabledWithoutMetadataSupport(enabled, opts); - } - logger_1.logger.debug(`Call ${this.callId} setScreensharingEnabled() running (enabled=${enabled})`); - if (enabled) { - try { - const stream = yield this.client.getMediaHandler().getScreensharingStream(opts); - if (!stream) - return false; - this.pushNewLocalFeed(stream, callEventTypes_1.SDPStreamMetadataPurpose.Screenshare); - return true; - } - catch (err) { - logger_1.logger.error(`Call ${this.callId} setScreensharingEnabled() failed to get screen-sharing stream:`, err); - return false; - } - } - else { - const audioTransceiver = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Screenshare, "audio")); - const videoTransceiver = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Screenshare, "video")); - for (const transceiver of [audioTransceiver, videoTransceiver]) { - // this is slightly mixing the track and transceiver API but is basically just shorthand - // for removing the sender. - if (transceiver && transceiver.sender) - this.peerConn.removeTrack(transceiver.sender); - } - this.client.getMediaHandler().stopScreensharingStream(this.localScreensharingStream); - this.deleteFeedByStream(this.localScreensharingStream); - return false; - } - }); - } - /** - * Starts/stops screensharing - * Should be used ONLY if the opponent doesn't support SDPStreamMetadata - * @param enabled - the desired screensharing state - * @param desktopCapturerSourceId - optional id of the desktop capturer source to use - * @returns new screensharing state - */ - setScreensharingEnabledWithoutMetadataSupport(enabled, opts) { - var _a, _b, _c; - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.debug(`Call ${this.callId} setScreensharingEnabledWithoutMetadataSupport() running (enabled=${enabled})`); - if (enabled) { - try { - const stream = yield this.client.getMediaHandler().getScreensharingStream(opts); - if (!stream) - return false; - const track = stream.getTracks().find((track) => track.kind === "video"); - const sender = (_a = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, "video"))) === null || _a === void 0 ? void 0 : _a.sender; - sender === null || sender === void 0 ? void 0 : sender.replaceTrack(track !== null && track !== void 0 ? track : null); - this.pushNewLocalFeed(stream, callEventTypes_1.SDPStreamMetadataPurpose.Screenshare, false); - return true; - } - catch (err) { - logger_1.logger.error(`Call ${this.callId} setScreensharingEnabledWithoutMetadataSupport() failed to get screen-sharing stream:`, err); - return false; - } - } - else { - const track = (_b = this.localUsermediaStream) === null || _b === void 0 ? void 0 : _b.getTracks().find((track) => track.kind === "video"); - const sender = (_c = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, "video"))) === null || _c === void 0 ? void 0 : _c.sender; - sender === null || sender === void 0 ? void 0 : sender.replaceTrack(track !== null && track !== void 0 ? track : null); - this.client.getMediaHandler().stopScreensharingStream(this.localScreensharingStream); - this.deleteFeedByStream(this.localScreensharingStream); - return false; - } - }); - } - /** - * Replaces/adds the tracks from the passed stream to the localUsermediaStream - * @param stream - to use a replacement for the local usermedia stream - */ - updateLocalUsermediaStream(stream, forceAudio = false, forceVideo = false) { - return __awaiter(this, void 0, void 0, function* () { - const callFeed = this.localUsermediaFeed; - const audioEnabled = forceAudio || (!callFeed.isAudioMuted() && !this.remoteOnHold); - const videoEnabled = forceVideo || (!callFeed.isVideoMuted() && !this.remoteOnHold); - logger_1.logger.log(`Call ${this.callId} updateLocalUsermediaStream() running (streamId=${stream.id}, audio=${audioEnabled}, video=${videoEnabled})`); - setTracksEnabled(stream.getAudioTracks(), audioEnabled); - setTracksEnabled(stream.getVideoTracks(), videoEnabled); - // We want to keep the same stream id, so we replace the tracks rather - // than the whole stream. - // Firstly, we replace the tracks in our localUsermediaStream. - for (const track of this.localUsermediaStream.getTracks()) { - this.localUsermediaStream.removeTrack(track); - track.stop(); - } - for (const track of stream.getTracks()) { - this.localUsermediaStream.addTrack(track); - } - // Then replace the old tracks, if possible. - for (const track of stream.getTracks()) { - const tKey = getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, track.kind); - const transceiver = this.transceivers.get(tKey); - const oldSender = transceiver === null || transceiver === void 0 ? void 0 : transceiver.sender; - let added = false; - if (oldSender) { - try { - logger_1.logger.info(`Call ${this.callId} updateLocalUsermediaStream() replacing track (id=${track.id}, kind=${track.kind}, streamId=${stream.id}, streamPurpose=${callFeed.purpose})`); - yield oldSender.replaceTrack(track); - // Set the direction to indicate we're going to be sending. - // This is only necessary in the cases where we're upgrading - // the call to video after downgrading it. - transceiver.direction = transceiver.direction === "inactive" ? "sendonly" : "sendrecv"; - added = true; - } - catch (error) { - logger_1.logger.warn(`Call ${this.callId} updateLocalUsermediaStream() replaceTrack failed: adding new transceiver instead`, error); - } - } - if (!added) { - logger_1.logger.info(`Call ${this.callId} updateLocalUsermediaStream() adding track to peer connection (id=${track.id}, kind=${track.kind}, streamId=${stream.id}, streamPurpose=${callFeed.purpose})`); - const newSender = this.peerConn.addTrack(track, this.localUsermediaStream); - const newTransceiver = this.peerConn.getTransceivers().find((t) => t.sender === newSender); - if (newTransceiver) { - this.transceivers.set(tKey, newTransceiver); - } - else { - logger_1.logger.warn(`Call ${this.callId} updateLocalUsermediaStream() couldn't find matching transceiver for newly added track!`); - } - } - } - }); - } - /** - * Set whether our outbound video should be muted or not. - * @param muted - True to mute the outbound video. - * @returns the new mute state - */ - setLocalVideoMuted(muted) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`Call ${this.callId} setLocalVideoMuted() running ${muted}`); - // if we were still thinking about stopping and removing the video - // track: don't, because we want it back. - if (!muted && this.stopVideoTrackTimer !== undefined) { - clearTimeout(this.stopVideoTrackTimer); - this.stopVideoTrackTimer = undefined; - } - if (!(yield this.client.getMediaHandler().hasVideoDevice())) { - return this.isLocalVideoMuted(); - } - if (!this.hasUserMediaVideoSender && !muted) { - (_a = this.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.setAudioVideoMuted(null, muted); - yield this.upgradeCall(false, true); - return this.isLocalVideoMuted(); - } - // we may not have a video track - if not, re-request usermedia - if (!muted && this.localUsermediaStream.getVideoTracks().length === 0) { - const stream = yield this.client.getMediaHandler().getUserMediaStream(true, true); - yield this.updateLocalUsermediaStream(stream); - } - (_b = this.localUsermediaFeed) === null || _b === void 0 ? void 0 : _b.setAudioVideoMuted(null, muted); - this.updateMuteStatus(); - yield this.sendMetadataUpdate(); - // if we're muting video, set a timeout to stop & remove the video track so we release - // the camera. We wait a short time to do this because when we disable a track, WebRTC - // will send black video for it. If we just stop and remove it straight away, the video - // will just freeze which means that when we unmute video, the other side will briefly - // get a static frame of us from before we muted. This way, the still frame is just black. - // A very small delay is not always enough so the theory here is that it needs to be long - // enough for WebRTC to encode a frame: 120ms should be long enough even if we're only - // doing 10fps. - if (muted) { - this.stopVideoTrackTimer = setTimeout(() => { - for (const t of this.localUsermediaStream.getVideoTracks()) { - t.stop(); - this.localUsermediaStream.removeTrack(t); - } - }, 120); - } - return this.isLocalVideoMuted(); - }); - } - /** - * Check if local video is muted. - * - * If there are multiple video tracks, all of the tracks need to be muted - * for this to return true. This means if there are no video tracks, this will - * return true. - * @returns True if the local preview video is muted, else false - * (including if the call is not set up yet). - */ - isLocalVideoMuted() { - var _a, _b; - return (_b = (_a = this.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.isVideoMuted()) !== null && _b !== void 0 ? _b : false; - } - /** - * Set whether the microphone should be muted or not. - * @param muted - True to mute the mic. - * @returns the new mute state - */ - setMicrophoneMuted(muted) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`Call ${this.callId} setMicrophoneMuted() running ${muted}`); - if (!(yield this.client.getMediaHandler().hasAudioDevice())) { - return this.isMicrophoneMuted(); - } - if (!muted && (!this.hasUserMediaAudioSender || !this.hasLocalUserMediaAudioTrack)) { - yield this.upgradeCall(true, false); - return this.isMicrophoneMuted(); - } - (_a = this.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.setAudioVideoMuted(muted, null); - this.updateMuteStatus(); - yield this.sendMetadataUpdate(); - return this.isMicrophoneMuted(); - }); - } - /** - * Check if the microphone is muted. - * - * If there are multiple audio tracks, all of the tracks need to be muted - * for this to return true. This means if there are no audio tracks, this will - * return true. - * @returns True if the mic is muted, else false (including if the call - * is not set up yet). - */ - isMicrophoneMuted() { - var _a, _b; - return (_b = (_a = this.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.isAudioMuted()) !== null && _b !== void 0 ? _b : false; - } - /** - * @returns true if we have put the party on the other side of the call on hold - * (that is, we are signalling to them that we are not listening) - */ - isRemoteOnHold() { - return this.remoteOnHold; - } - setRemoteOnHold(onHold) { - if (this.isRemoteOnHold() === onHold) - return; - this.remoteOnHold = onHold; - for (const transceiver of this.peerConn.getTransceivers()) { - // We don't send hold music or anything so we're not actually - // sending anything, but sendrecv is fairly standard for hold and - // it makes it a lot easier to figure out who's put who on hold. - transceiver.direction = onHold ? "sendonly" : "sendrecv"; - } - this.updateMuteStatus(); - this.sendMetadataUpdate(); - this.emit(CallEvent.RemoteHoldUnhold, this.remoteOnHold, this); - } - /** - * Indicates whether we are 'on hold' to the remote party (ie. if true, - * they cannot hear us). - * @returns true if the other party has put us on hold - */ - isLocalOnHold() { - if (this.state !== CallState.Connected) - return false; - let callOnHold = true; - // We consider a call to be on hold only if *all* the tracks are on hold - // (is this the right thing to do?) - for (const transceiver of this.peerConn.getTransceivers()) { - const trackOnHold = ["inactive", "recvonly"].includes(transceiver.currentDirection); - if (!trackOnHold) - callOnHold = false; - } - return callOnHold; - } - /** - * Sends a DTMF digit to the other party - * @param digit - The digit (nb. string - '#' and '*' are dtmf too) - */ - sendDtmfDigit(digit) { - var _a; - for (const sender of this.peerConn.getSenders()) { - if (((_a = sender.track) === null || _a === void 0 ? void 0 : _a.kind) === "audio" && sender.dtmf) { - sender.dtmf.insertDTMF(digit); - return; - } - } - throw new Error("Unable to find a track to send DTMF on"); - } - updateMuteStatus() { - const micShouldBeMuted = this.isMicrophoneMuted() || this.remoteOnHold; - const vidShouldBeMuted = this.isLocalVideoMuted() || this.remoteOnHold; - logger_1.logger.log(`Call ${this.callId} updateMuteStatus stream ${this.localUsermediaStream.id} micShouldBeMuted ${micShouldBeMuted} vidShouldBeMuted ${vidShouldBeMuted}`); - setTracksEnabled(this.localUsermediaStream.getAudioTracks(), !micShouldBeMuted); - setTracksEnabled(this.localUsermediaStream.getVideoTracks(), !vidShouldBeMuted); - } - sendMetadataUpdate() { - return __awaiter(this, void 0, void 0, function* () { - yield this.sendVoipEvent(event_1.EventType.CallSDPStreamMetadataChangedPrefix, { - [callEventTypes_1.SDPStreamMetadataKey]: this.getLocalSDPStreamMetadata(), - }); - }); - } - gotCallFeedsForInvite(callFeeds, requestScreenshareFeed = false) { - if (this.successor) { - this.successor.queueGotCallFeedsForAnswer(callFeeds); - return; - } - if (this.callHasEnded()) { - this.stopAllMedia(); - return; - } - for (const feed of callFeeds) { - this.pushLocalFeed(feed); - } - if (requestScreenshareFeed) { - this.peerConn.addTransceiver("video", { - direction: "recvonly", - }); - } - this.state = CallState.CreateOffer; - logger_1.logger.debug(`Call ${this.callId} gotUserMediaForInvite() run`); - // Now we wait for the negotiationneeded event - } - sendAnswer() { - return __awaiter(this, void 0, void 0, function* () { - const answerContent = { - answer: { - sdp: this.peerConn.localDescription.sdp, - // type is now deprecated as of Matrix VoIP v1, but - // required to still be sent for backwards compat - type: this.peerConn.localDescription.type, - }, - [callEventTypes_1.SDPStreamMetadataKey]: this.getLocalSDPStreamMetadata(true), - }; - answerContent.capabilities = { - "m.call.transferee": this.client.supportsCallTransfer, - "m.call.dtmf": false, - }; - // We have just taken the local description from the peerConn which will - // contain all the local candidates added so far, so we can discard any candidates - // we had queued up because they'll be in the answer. - const discardCount = this.discardDuplicateCandidates(); - logger_1.logger.info(`Call ${this.callId} sendAnswer() discarding ${discardCount} candidates that will be sent in answer`); - try { - yield this.sendVoipEvent(event_1.EventType.CallAnswer, answerContent); - // If this isn't the first time we've tried to send the answer, - // we may have candidates queued up, so send them now. - this.inviteOrAnswerSent = true; - } - catch (error) { - // We've failed to answer: back to the ringing state - this.state = CallState.Ringing; - if (error instanceof http_api_1.MatrixError && error.event) - this.client.cancelPendingEvent(error.event); - let code = CallErrorCode.SendAnswer; - let message = "Failed to send answer"; - if (error.name == "UnknownDeviceError") { - code = CallErrorCode.UnknownDevices; - message = "Unknown devices present in the room"; - } - this.emit(CallEvent.Error, new CallError(code, message, error), this); - throw error; - } - // error handler re-throws so this won't happen on error, but - // we don't want the same error handling on the candidate queue - this.sendCandidateQueue(); - }); - } - queueGotCallFeedsForAnswer(callFeeds) { - // Ensure only one negotiate/answer event is being processed at a time. - if (this.responsePromiseChain) { - this.responsePromiseChain = this.responsePromiseChain.then(() => this.gotCallFeedsForAnswer(callFeeds)); - } - else { - this.responsePromiseChain = this.gotCallFeedsForAnswer(callFeeds); - } - } - // Enables DTX (discontinuous transmission) on the given session to reduce - // bandwidth when transmitting silence - mungeSdp(description, mods) { - // The only way to enable DTX at this time is through SDP munging - const sdp = (0, sdp_transform_1.parse)(description.sdp); - sdp.media.forEach((media) => { - const payloadTypeToCodecMap = new Map(); - const codecToPayloadTypeMap = new Map(); - for (const rtp of media.rtp) { - payloadTypeToCodecMap.set(rtp.payload, rtp.codec); - codecToPayloadTypeMap.set(rtp.codec, rtp.payload); - } - for (const mod of mods) { - if (mod.mediaType !== media.type) - continue; - if (!codecToPayloadTypeMap.has(mod.codec)) { - logger_1.logger.info(`Call ${this.callId} mungeSdp() ignoring SDP modifications for ${mod.codec} as it's not present.`); - continue; - } - const extraConfig = []; - if (mod.enableDtx !== undefined) { - extraConfig.push(`usedtx=${mod.enableDtx ? "1" : "0"}`); - } - if (mod.maxAverageBitrate !== undefined) { - extraConfig.push(`maxaveragebitrate=${mod.maxAverageBitrate}`); - } - let found = false; - for (const fmtp of media.fmtp) { - if (payloadTypeToCodecMap.get(fmtp.payload) === mod.codec) { - found = true; - fmtp.config += ";" + extraConfig.join(";"); - } - } - if (!found) { - media.fmtp.push({ - payload: codecToPayloadTypeMap.get(mod.codec), - config: extraConfig.join(";"), - }); - } - } - }); - description.sdp = (0, sdp_transform_1.write)(sdp); - } - createOffer() { - return __awaiter(this, void 0, void 0, function* () { - const offer = yield this.peerConn.createOffer(); - this.mungeSdp(offer, getCodecParamMods(this.isPtt)); - return offer; - }); - } - createAnswer() { - return __awaiter(this, void 0, void 0, function* () { - const answer = yield this.peerConn.createAnswer(); - this.mungeSdp(answer, getCodecParamMods(this.isPtt)); - return answer; - }); - } - gotCallFeedsForAnswer(callFeeds) { - return __awaiter(this, void 0, void 0, function* () { - if (this.callHasEnded()) - return; - this.waitForLocalAVStream = false; - for (const feed of callFeeds) { - this.pushLocalFeed(feed); - } - this.state = CallState.CreateAnswer; - let answer; - try { - this.getRidOfRTXCodecs(); - answer = yield this.createAnswer(); - } - catch (err) { - logger_1.logger.debug(`Call ${this.callId} gotCallFeedsForAnswer() failed to create answer: `, err); - this.terminate(CallParty.Local, CallErrorCode.CreateAnswer, true); - return; - } - try { - yield this.peerConn.setLocalDescription(answer); - // make sure we're still going - if (this.callHasEnded()) - return; - this.state = CallState.Connecting; - // Allow a short time for initial candidates to be gathered - yield new Promise((resolve) => { - setTimeout(resolve, 200); - }); - // make sure the call hasn't ended before we continue - if (this.callHasEnded()) - return; - this.sendAnswer(); - } - catch (err) { - logger_1.logger.debug(`Call ${this.callId} gotCallFeedsForAnswer() error setting local description!`, err); - this.terminate(CallParty.Local, CallErrorCode.SetLocalDescription, true); - return; - } - }); - } - onRemoteIceCandidatesReceived(ev) { - return __awaiter(this, void 0, void 0, function* () { - if (this.callHasEnded()) { - //debuglog("Ignoring remote ICE candidate because call has ended"); - return; - } - const content = ev.getContent(); - const candidates = content.candidates; - if (!candidates) { - logger_1.logger.info(`Call ${this.callId} onRemoteIceCandidatesReceived() ignoring candidates event with no candidates!`); - return; - } - const fromPartyId = content.version === 0 ? null : content.party_id || null; - if (this.opponentPartyId === undefined) { - // we haven't picked an opponent yet so save the candidates - if (fromPartyId) { - logger_1.logger.info(`Call ${this.callId} onRemoteIceCandidatesReceived() buffering ${candidates.length} candidates until we pick an opponent`); - const bufferedCandidates = this.remoteCandidateBuffer.get(fromPartyId) || []; - bufferedCandidates.push(...candidates); - this.remoteCandidateBuffer.set(fromPartyId, bufferedCandidates); - } - return; - } - if (!this.partyIdMatches(content)) { - logger_1.logger.info(`Call ${this.callId} onRemoteIceCandidatesReceived() ignoring candidates from party ID ${content.party_id}: we have chosen party ID ${this.opponentPartyId}`); - return; - } - yield this.addIceCandidates(candidates); - }); - } - /** - * Used by MatrixClient. - */ - onAnswerReceived(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getContent(); - logger_1.logger.debug(`Call ${this.callId} onAnswerReceived() running (hangupParty=${content.party_id})`); - if (this.callHasEnded()) { - logger_1.logger.debug(`Call ${this.callId} onAnswerReceived() ignoring answer because call has ended`); - return; - } - if (this.opponentPartyId !== undefined) { - logger_1.logger.info(`Call ${this.callId} onAnswerReceived() ignoring answer from party ID ${content.party_id}: we already have an answer/reject from ${this.opponentPartyId}`); - return; - } - this.chooseOpponent(event); - yield this.addBufferedIceCandidates(); - this.state = CallState.Connecting; - const sdpStreamMetadata = content[callEventTypes_1.SDPStreamMetadataKey]; - if (sdpStreamMetadata) { - this.updateRemoteSDPStreamMetadata(sdpStreamMetadata); - } - else { - logger_1.logger.warn(`Call ${this.callId} onAnswerReceived() did not get any SDPStreamMetadata! Can not send/receive multiple streams`); - } - try { - yield this.peerConn.setRemoteDescription(content.answer); - } - catch (e) { - logger_1.logger.debug(`Call ${this.callId} onAnswerReceived() failed to set remote description`, e); - this.terminate(CallParty.Local, CallErrorCode.SetRemoteDescription, false); - return; - } - // If the answer we selected has a party_id, send a select_answer event - // We do this after setting the remote description since otherwise we'd block - // call setup on it - if (this.opponentPartyId !== null) { - try { - yield this.sendVoipEvent(event_1.EventType.CallSelectAnswer, { - selected_party_id: this.opponentPartyId, - }); - } - catch (err) { - // This isn't fatal, and will just mean that if another party has raced to answer - // the call, they won't know they got rejected, so we carry on & don't retry. - logger_1.logger.warn(`Call ${this.callId} onAnswerReceived() failed to send select_answer event`, err); - } - } - }); - } - onSelectAnswerReceived(event) { - return __awaiter(this, void 0, void 0, function* () { - if (this.direction !== CallDirection.Inbound) { - logger_1.logger.warn(`Call ${this.callId} onSelectAnswerReceived() got select_answer for an outbound call: ignoring`); - return; - } - const selectedPartyId = event.getContent().selected_party_id; - if (selectedPartyId === undefined || selectedPartyId === null) { - logger_1.logger.warn(`Call ${this.callId} onSelectAnswerReceived() got nonsensical select_answer with null/undefined selected_party_id: ignoring`); - return; - } - if (selectedPartyId !== this.ourPartyId) { - logger_1.logger.info(`Call ${this.callId} onSelectAnswerReceived() got select_answer for party ID ${selectedPartyId}: we are party ID ${this.ourPartyId}.`); - // The other party has picked somebody else's answer - yield this.terminate(CallParty.Remote, CallErrorCode.AnsweredElsewhere, true); - } - }); - } - onNegotiateReceived(event) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const content = event.getContent(); - const description = content.description; - if (!description || !description.sdp || !description.type) { - logger_1.logger.info(`Call ${this.callId} onNegotiateReceived() ignoring invalid m.call.negotiate event`); - return; - } - // Politeness always follows the direction of the call: in a glare situation, - // we pick either the inbound or outbound call, so one side will always be - // inbound and one outbound - const polite = this.direction === CallDirection.Inbound; - // Here we follow the perfect negotiation logic from - // https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation - const offerCollision = description.type === "offer" && (this.makingOffer || this.peerConn.signalingState !== "stable"); - this.ignoreOffer = !polite && offerCollision; - if (this.ignoreOffer) { - logger_1.logger.info(`Call ${this.callId} onNegotiateReceived() ignoring colliding negotiate event because we're impolite`); - return; - } - const prevLocalOnHold = this.isLocalOnHold(); - const sdpStreamMetadata = content[callEventTypes_1.SDPStreamMetadataKey]; - if (sdpStreamMetadata) { - this.updateRemoteSDPStreamMetadata(sdpStreamMetadata); - } - else { - logger_1.logger.warn(`Call ${this.callId} onNegotiateReceived() received negotiation event without SDPStreamMetadata!`); - } - try { - yield this.peerConn.setRemoteDescription(description); - if (description.type === "offer") { - let answer; - try { - this.getRidOfRTXCodecs(); - answer = yield this.createAnswer(); - } - catch (err) { - logger_1.logger.debug(`Call ${this.callId} onNegotiateReceived() failed to create answer: `, err); - this.terminate(CallParty.Local, CallErrorCode.CreateAnswer, true); - return; - } - yield this.peerConn.setLocalDescription(answer); - this.sendVoipEvent(event_1.EventType.CallNegotiate, { - description: (_a = this.peerConn.localDescription) === null || _a === void 0 ? void 0 : _a.toJSON(), - [callEventTypes_1.SDPStreamMetadataKey]: this.getLocalSDPStreamMetadata(true), - }); - } - } - catch (err) { - logger_1.logger.warn(`Call ${this.callId} onNegotiateReceived() failed to complete negotiation`, err); - } - const newLocalOnHold = this.isLocalOnHold(); - if (prevLocalOnHold !== newLocalOnHold) { - this.emit(CallEvent.LocalHoldUnhold, newLocalOnHold, this); - // also this one for backwards compat - this.emit(CallEvent.HoldUnhold, newLocalOnHold); - } - }); - } - updateRemoteSDPStreamMetadata(metadata) { - var _a; - this.remoteSDPStreamMetadata = utils.recursivelyAssign(this.remoteSDPStreamMetadata || {}, metadata, true); - for (const feed of this.getRemoteFeeds()) { - const streamId = feed.stream.id; - const metadata = this.remoteSDPStreamMetadata[streamId]; - feed.setAudioVideoMuted(metadata === null || metadata === void 0 ? void 0 : metadata.audio_muted, metadata === null || metadata === void 0 ? void 0 : metadata.video_muted); - feed.purpose = (_a = this.remoteSDPStreamMetadata[streamId]) === null || _a === void 0 ? void 0 : _a.purpose; - } - } - onSDPStreamMetadataChangedReceived(event) { - const content = event.getContent(); - const metadata = content[callEventTypes_1.SDPStreamMetadataKey]; - this.updateRemoteSDPStreamMetadata(metadata); - } - onAssertedIdentityReceived(event) { - return __awaiter(this, void 0, void 0, function* () { - const content = event.getContent(); - if (!content.asserted_identity) - return; - this.remoteAssertedIdentity = { - id: content.asserted_identity.id, - displayName: content.asserted_identity.display_name, - }; - this.emit(CallEvent.AssertedIdentityChanged, this); - }); - } - callHasEnded() { - // This exists as workaround to typescript trying to be clever and erroring - // when putting if (this.state === CallState.Ended) return; twice in the same - // function, even though that function is async. - return this.state === CallState.Ended; - } - queueGotLocalOffer() { - // Ensure only one negotiate/answer event is being processed at a time. - if (this.responsePromiseChain) { - this.responsePromiseChain = this.responsePromiseChain.then(() => this.wrappedGotLocalOffer()); - } - else { - this.responsePromiseChain = this.wrappedGotLocalOffer(); - } - } - wrappedGotLocalOffer() { - return __awaiter(this, void 0, void 0, function* () { - this.makingOffer = true; - try { - // XXX: in what situations do we believe gotLocalOffer actually throws? It appears - // to handle most of its exceptions itself and terminate the call. I'm not entirely - // sure it would ever throw, so I can't add a test for these lines. - // Also the tense is different between "gotLocalOffer" and "getLocalOfferFailed" so - // it's not entirely clear whether getLocalOfferFailed is just misnamed or whether - // they've been cross-polinated somehow at some point. - yield this.gotLocalOffer(); - } - catch (e) { - this.getLocalOfferFailed(e); - return; - } - finally { - this.makingOffer = false; - } - }); - } - gotLocalOffer() { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.debug(`Call ${this.callId} gotLocalOffer() running`); - if (this.callHasEnded()) { - logger_1.logger.debug(`Call ${this.callId} gotLocalOffer() ignoring newly created offer because the call has ended"`); - return; - } - let offer; - try { - this.getRidOfRTXCodecs(); - offer = yield this.createOffer(); - } - catch (err) { - logger_1.logger.debug(`Call ${this.callId} gotLocalOffer() failed to create offer: `, err); - this.terminate(CallParty.Local, CallErrorCode.CreateOffer, true); - return; - } - try { - yield this.peerConn.setLocalDescription(offer); - } - catch (err) { - logger_1.logger.debug(`Call ${this.callId} gotLocalOffer() error setting local description!`, err); - this.terminate(CallParty.Local, CallErrorCode.SetLocalDescription, true); - return; - } - if (this.peerConn.iceGatheringState === "gathering") { - // Allow a short time for initial candidates to be gathered - yield new Promise((resolve) => { - setTimeout(resolve, 200); - }); - } - if (this.callHasEnded()) - return; - const eventType = this.state === CallState.CreateOffer ? event_1.EventType.CallInvite : event_1.EventType.CallNegotiate; - const content = { - lifetime: CALL_TIMEOUT_MS, - }; - if (eventType === event_1.EventType.CallInvite && this.invitee) { - content.invitee = this.invitee; - } - // clunky because TypeScript can't follow the types through if we use an expression as the key - if (this.state === CallState.CreateOffer) { - content.offer = (_a = this.peerConn.localDescription) === null || _a === void 0 ? void 0 : _a.toJSON(); - } - else { - content.description = (_b = this.peerConn.localDescription) === null || _b === void 0 ? void 0 : _b.toJSON(); - } - content.capabilities = { - "m.call.transferee": this.client.supportsCallTransfer, - "m.call.dtmf": false, - }; - content[callEventTypes_1.SDPStreamMetadataKey] = this.getLocalSDPStreamMetadata(true); - // Get rid of any candidates waiting to be sent: they'll be included in the local - // description we just got and will send in the offer. - const discardCount = this.discardDuplicateCandidates(); - logger_1.logger.info(`Call ${this.callId} gotLocalOffer() discarding ${discardCount} candidates that will be sent in offer`); - try { - yield this.sendVoipEvent(eventType, content); - } - catch (error) { - logger_1.logger.error(`Call ${this.callId} gotLocalOffer() failed to send invite`, error); - if (error instanceof http_api_1.MatrixError && error.event) - this.client.cancelPendingEvent(error.event); - let code = CallErrorCode.SignallingFailed; - let message = "Signalling failed"; - if (this.state === CallState.CreateOffer) { - code = CallErrorCode.SendInvite; - message = "Failed to send invite"; - } - if (error.name == "UnknownDeviceError") { - code = CallErrorCode.UnknownDevices; - message = "Unknown devices present in the room"; - } - this.emit(CallEvent.Error, new CallError(code, message, error), this); - this.terminate(CallParty.Local, code, false); - // no need to carry on & send the candidate queue, but we also - // don't want to rethrow the error - return; - } - this.sendCandidateQueue(); - if (this.state === CallState.CreateOffer) { - this.inviteOrAnswerSent = true; - this.state = CallState.InviteSent; - this.inviteTimeout = setTimeout(() => { - this.inviteTimeout = undefined; - if (this.state === CallState.InviteSent) { - this.hangup(CallErrorCode.InviteTimeout, false); - } - }, CALL_TIMEOUT_MS); - } - }); - } - /** - * This method removes all video/rtx codecs from screensharing video - * transceivers. This is necessary since they can cause problems. Without - * this the following steps should produce an error: - * Chromium calls Firefox - * Firefox answers - * Firefox starts screen-sharing - * Chromium starts screen-sharing - * Call crashes for Chromium with: - * [96685:23:0518/162603.933321:ERROR:webrtc_video_engine.cc(3296)] RTX codec (PT=97) mapped to PT=96 which is not in the codec list. - * [96685:23:0518/162603.933377:ERROR:webrtc_video_engine.cc(1171)] GetChangedRecvParameters called without any video codecs. - * [96685:23:0518/162603.933430:ERROR:sdp_offer_answer.cc(4302)] Failed to set local video description recv parameters for m-section with mid='2'. (INVALID_PARAMETER) - */ - getRidOfRTXCodecs() { - // RTCRtpReceiver.getCapabilities and RTCRtpSender.getCapabilities don't seem to be supported on FF - if (!RTCRtpReceiver.getCapabilities || !RTCRtpSender.getCapabilities) - return; - const recvCodecs = RTCRtpReceiver.getCapabilities("video").codecs; - const sendCodecs = RTCRtpSender.getCapabilities("video").codecs; - const codecs = [...sendCodecs, ...recvCodecs]; - for (const codec of codecs) { - if (codec.mimeType === "video/rtx") { - const rtxCodecIndex = codecs.indexOf(codec); - codecs.splice(rtxCodecIndex, 1); - } - } - const screenshareVideoTransceiver = this.transceivers.get(getTransceiverKey(callEventTypes_1.SDPStreamMetadataPurpose.Screenshare, "video")); - if (screenshareVideoTransceiver) - screenshareVideoTransceiver.setCodecPreferences(codecs); - } - /** - * @internal - */ - sendVoipEvent(eventType, content) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - const realContent = Object.assign({}, content, { - version: VOIP_PROTO_VERSION, - call_id: this.callId, - party_id: this.ourPartyId, - conf_id: this.groupCallId, - }); - if (this.opponentDeviceId) { - const toDeviceSeq = this.toDeviceSeq++; - const content = Object.assign(Object.assign({}, realContent), { device_id: this.client.deviceId, sender_session_id: this.client.getSessionId(), dest_session_id: this.opponentSessionId, seq: toDeviceSeq, [event_1.ToDeviceMessageId]: (0, uuid_1.v4)() }); - this.emit(CallEvent.SendVoipEvent, { - type: "toDevice", - eventType, - userId: this.invitee || ((_a = this.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId), - opponentDeviceId: this.opponentDeviceId, - content, - }, this); - const userId = this.invitee || this.getOpponentMember().userId; - if (this.client.getUseE2eForGroupCall()) { - if (!this.opponentDeviceInfo) { - logger_1.logger.warn(`Call ${this.callId} sendVoipEvent() failed: we do not have opponentDeviceInfo`); - return; - } - yield this.client.encryptAndSendToDevices([ - { - userId, - deviceInfo: this.opponentDeviceInfo, - }, - ], { - type: eventType, - content, - }); - } - else { - yield this.client.sendToDevice(eventType, new Map([[userId, new Map([[this.opponentDeviceId, content]])]])); - } - } - else { - this.emit(CallEvent.SendVoipEvent, { - type: "sendEvent", - eventType, - roomId: this.roomId, - content: realContent, - userId: this.invitee || ((_b = this.getOpponentMember()) === null || _b === void 0 ? void 0 : _b.userId), - }, this); - yield this.client.sendEvent(this.roomId, eventType, realContent); - } - }); - } - /** - * Queue a candidate to be sent - * @param content - The candidate to queue up, or null if candidates have finished being generated - * and end-of-candidates should be signalled - */ - queueCandidate(content) { - // We partially de-trickle candidates by waiting for `delay` before sending them - // amalgamated, in order to avoid sending too many m.call.candidates events and hitting - // rate limits in Matrix. - // In practice, it'd be better to remove rate limits for m.call.* - // N.B. this deliberately lets you queue and send blank candidates, which MSC2746 - // currently proposes as the way to indicate that candidate gathering is complete. - // This will hopefully be changed to an explicit rather than implicit notification - // shortly. - if (content) { - this.candidateSendQueue.push(content); - } - else { - this.candidatesEnded = true; - } - // Don't send the ICE candidates yet if the call is in the ringing state: this - // means we tried to pick (ie. started generating candidates) and then failed to - // send the answer and went back to the ringing state. Queue up the candidates - // to send if we successfully send the answer. - // Equally don't send if we haven't yet sent the answer because we can send the - // first batch of candidates along with the answer - if (this.state === CallState.Ringing || !this.inviteOrAnswerSent) - return; - // MSC2746 recommends these values (can be quite long when calling because the - // callee will need a while to answer the call) - const delay = this.direction === CallDirection.Inbound ? 500 : 2000; - if (this.candidateSendTries === 0) { - setTimeout(() => { - this.sendCandidateQueue(); - }, delay); - } - } - // Discard all non-end-of-candidates messages - // Return the number of candidate messages that were discarded. - // Call this method before sending an invite or answer message - discardDuplicateCandidates() { - let discardCount = 0; - const newQueue = []; - for (let i = 0; i < this.candidateSendQueue.length; i++) { - const candidate = this.candidateSendQueue[i]; - if (candidate.candidate === "") { - newQueue.push(candidate); - } - else { - discardCount++; - } - } - this.candidateSendQueue = newQueue; - return discardCount; - } - /* - * Transfers this call to another user - */ - transfer(targetUserId) { - return __awaiter(this, void 0, void 0, function* () { - // Fetch the target user's global profile info: their room avatar / displayname - // could be different in whatever room we share with them. - const profileInfo = yield this.client.getProfileInfo(targetUserId); - const replacementId = genCallID(); - const body = { - replacement_id: genCallID(), - target_user: { - id: targetUserId, - display_name: profileInfo.displayname, - avatar_url: profileInfo.avatar_url, - }, - create_call: replacementId, - }; - yield this.sendVoipEvent(event_1.EventType.CallReplaces, body); - yield this.terminate(CallParty.Local, CallErrorCode.Transferred, true); - }); - } - /* - * Transfers this call to the target call, effectively 'joining' the - * two calls (so the remote parties on each call are connected together). - */ - transferToCall(transferTargetCall) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - const targetUserId = (_a = transferTargetCall.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId; - const targetProfileInfo = targetUserId ? yield this.client.getProfileInfo(targetUserId) : undefined; - const opponentUserId = (_b = this.getOpponentMember()) === null || _b === void 0 ? void 0 : _b.userId; - const transfereeProfileInfo = opponentUserId ? yield this.client.getProfileInfo(opponentUserId) : undefined; - const newCallId = genCallID(); - const bodyToTransferTarget = { - // the replacements on each side have their own ID, and it's distinct from the - // ID of the new call (but we can use the same function to generate it) - replacement_id: genCallID(), - target_user: { - id: opponentUserId, - display_name: transfereeProfileInfo === null || transfereeProfileInfo === void 0 ? void 0 : transfereeProfileInfo.displayname, - avatar_url: transfereeProfileInfo === null || transfereeProfileInfo === void 0 ? void 0 : transfereeProfileInfo.avatar_url, - }, - await_call: newCallId, - }; - yield transferTargetCall.sendVoipEvent(event_1.EventType.CallReplaces, bodyToTransferTarget); - const bodyToTransferee = { - replacement_id: genCallID(), - target_user: { - id: targetUserId, - display_name: targetProfileInfo === null || targetProfileInfo === void 0 ? void 0 : targetProfileInfo.displayname, - avatar_url: targetProfileInfo === null || targetProfileInfo === void 0 ? void 0 : targetProfileInfo.avatar_url, - }, - create_call: newCallId, - }; - yield this.sendVoipEvent(event_1.EventType.CallReplaces, bodyToTransferee); - yield this.terminate(CallParty.Local, CallErrorCode.Transferred, true); - yield transferTargetCall.terminate(CallParty.Local, CallErrorCode.Transferred, true); - }); - } - terminate(hangupParty, hangupReason, shouldEmit) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (this.callHasEnded()) - return; - this.hangupParty = hangupParty; - this.hangupReason = hangupReason; - this.state = CallState.Ended; - if (this.inviteTimeout) { - clearTimeout(this.inviteTimeout); - this.inviteTimeout = undefined; - } - if (this.iceDisconnectedTimeout !== undefined) { - clearTimeout(this.iceDisconnectedTimeout); - this.iceDisconnectedTimeout = undefined; - } - if (this.callLengthInterval) { - clearInterval(this.callLengthInterval); - this.callLengthInterval = undefined; - } - if (this.stopVideoTrackTimer !== undefined) { - clearTimeout(this.stopVideoTrackTimer); - this.stopVideoTrackTimer = undefined; - } - for (const [stream, listener] of this.removeTrackListeners) { - stream.removeEventListener("removetrack", listener); - } - this.removeTrackListeners.clear(); - this.callStatsAtEnd = yield this.collectCallStats(); - // Order is important here: first we stopAllMedia() and only then we can deleteAllFeeds() - this.stopAllMedia(); - this.deleteAllFeeds(); - if (this.peerConn && this.peerConn.signalingState !== "closed") { - this.peerConn.close(); - } - (_a = this.stats) === null || _a === void 0 ? void 0 : _a.removeStatsReportGatherer(this.callId); - if (shouldEmit) { - this.emit(CallEvent.Hangup, this); - } - this.client.callEventHandler.calls.delete(this.callId); - }); - } - stopAllMedia() { - logger_1.logger.debug(`Call ${this.callId} stopAllMedia() running`); - for (const feed of this.feeds) { - // Slightly awkward as local feed need to go via the correct method on - // the MediaHandler so they get removed from MediaHandler (remote tracks - // don't) - // NB. We clone local streams when passing them to individual calls in a group - // call, so we can (and should) stop the clones once we no longer need them: - // the other clones will continue fine. - if (feed.isLocal() && feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Usermedia) { - this.client.getMediaHandler().stopUserMediaStream(feed.stream); - } - else if (feed.isLocal() && feed.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare) { - this.client.getMediaHandler().stopScreensharingStream(feed.stream); - } - else if (!feed.isLocal()) { - logger_1.logger.debug(`Call ${this.callId} stopAllMedia() stopping stream (streamId=${feed.stream.id})`); - for (const track of feed.stream.getTracks()) { - track.stop(); - } - } - } - } - checkForErrorListener() { - if (this.listeners(typed_event_emitter_1.EventEmitterEvents.Error).length === 0) { - throw new Error("You MUST attach an error listener using call.on('error', function() {})"); - } - } - sendCandidateQueue() { - return __awaiter(this, void 0, void 0, function* () { - if (this.candidateSendQueue.length === 0 || this.callHasEnded()) { - return; - } - const candidates = this.candidateSendQueue; - this.candidateSendQueue = []; - ++this.candidateSendTries; - const content = { candidates: candidates.map((candidate) => candidate.toJSON()) }; - if (this.candidatesEnded) { - // If there are no more candidates, signal this by adding an empty string candidate - content.candidates.push({ - candidate: "", - }); - } - logger_1.logger.debug(`Call ${this.callId} sendCandidateQueue() attempting to send ${candidates.length} candidates`); - try { - yield this.sendVoipEvent(event_1.EventType.CallCandidates, content); - // reset our retry count if we have successfully sent our candidates - // otherwise queueCandidate() will refuse to try to flush the queue - this.candidateSendTries = 0; - // Try to send candidates again just in case we received more candidates while sending. - this.sendCandidateQueue(); - } - catch (error) { - // don't retry this event: we'll send another one later as we might - // have more candidates by then. - if (error instanceof http_api_1.MatrixError && error.event) - this.client.cancelPendingEvent(error.event); - // put all the candidates we failed to send back in the queue - this.candidateSendQueue.push(...candidates); - if (this.candidateSendTries > 5) { - logger_1.logger.debug(`Call ${this.callId} sendCandidateQueue() failed to send candidates on attempt ${this.candidateSendTries}. Giving up on this call.`, error); - const code = CallErrorCode.SignallingFailed; - const message = "Signalling failed"; - this.emit(CallEvent.Error, new CallError(code, message, error), this); - this.hangup(code, false); - return; - } - const delayMs = 500 * Math.pow(2, this.candidateSendTries); - ++this.candidateSendTries; - logger_1.logger.debug(`Call ${this.callId} sendCandidateQueue() failed to send candidates. Retrying in ${delayMs}ms`, error); - setTimeout(() => { - this.sendCandidateQueue(); - }, delayMs); - } - }); - } - /** - * Place a call to this room. - * @throws if you have not specified a listener for 'error' events. - * @throws if have passed audio=false. - */ - placeCall(audio, video) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - if (!audio) { - throw new Error("You CANNOT start a call without audio"); - } - this.state = CallState.WaitLocalMedia; - try { - const stream = yield this.client.getMediaHandler().getUserMediaStream(audio, video); - // make sure all the tracks are enabled (same as pushNewLocalFeed - - // we probably ought to just have one code path for adding streams) - setTracksEnabled(stream.getAudioTracks(), true); - setTracksEnabled(stream.getVideoTracks(), true); - const callFeed = new callFeed_1.CallFeed({ - client: this.client, - roomId: this.roomId, - userId: this.client.getUserId(), - deviceId: (_a = this.client.getDeviceId()) !== null && _a !== void 0 ? _a : undefined, - stream, - purpose: callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, - audioMuted: false, - videoMuted: false, - }); - yield this.placeCallWithCallFeeds([callFeed]); - } - catch (e) { - this.getUserMediaFailed(e); - return; - } - }); - } - /** - * Place a call to this room with call feed. - * @param callFeeds - to use - * @throws if you have not specified a listener for 'error' events. - * @throws if have passed audio=false. - */ - placeCallWithCallFeeds(callFeeds, requestScreenshareFeed = false) { - return __awaiter(this, void 0, void 0, function* () { - this.checkForErrorListener(); - this.direction = CallDirection.Outbound; - yield this.initOpponentCrypto(); - // XXX Find a better way to do this - this.client.callEventHandler.calls.set(this.callId, this); - // make sure we have valid turn creds. Unless something's gone wrong, it should - // poll and keep the credentials valid so this should be instant. - const haveTurnCreds = yield this.client.checkTurnServers(); - if (!haveTurnCreds) { - logger_1.logger.warn(`Call ${this.callId} placeCallWithCallFeeds() failed to get TURN credentials! Proceeding with call anyway...`); - } - // create the peer connection now so it can be gathering candidates while we get user - // media (assuming a candidate pool size is configured) - this.peerConn = this.createPeerConnection(); - this.gotCallFeedsForInvite(callFeeds, requestScreenshareFeed); - }); - } - createPeerConnection() { - var _a; - const pc = new window.RTCPeerConnection({ - iceTransportPolicy: this.forceTURN ? "relay" : undefined, - iceServers: this.turnServers, - iceCandidatePoolSize: this.client.iceCandidatePoolSize, - bundlePolicy: "max-bundle", - }); - // 'connectionstatechange' would be better, but firefox doesn't implement that. - pc.addEventListener("iceconnectionstatechange", this.onIceConnectionStateChanged); - pc.addEventListener("signalingstatechange", this.onSignallingStateChanged); - pc.addEventListener("icecandidate", this.gotLocalIceCandidate); - pc.addEventListener("icegatheringstatechange", this.onIceGatheringStateChange); - pc.addEventListener("track", this.onTrack); - pc.addEventListener("negotiationneeded", this.onNegotiationNeeded); - pc.addEventListener("datachannel", this.onDataChannel); - (_a = this.stats) === null || _a === void 0 ? void 0 : _a.addStatsReportGatherer(this.callId, "unknown", pc); - return pc; - } - partyIdMatches(msg) { - // They must either match or both be absent (in which case opponentPartyId will be null) - // Also we ignore party IDs on the invite/offer if the version is 0, so we must do the same - // here and use null if the version is 0 (woe betide any opponent sending messages in the - // same call with different versions) - const msgPartyId = msg.version === 0 ? null : msg.party_id || null; - return msgPartyId === this.opponentPartyId; - } - // Commits to an opponent for the call - // ev: An invite or answer event - chooseOpponent(ev) { - var _a; - // I choo-choo-choose you - const msg = ev.getContent(); - logger_1.logger.debug(`Call ${this.callId} chooseOpponent() running (partyId=${msg.party_id})`); - this.opponentVersion = msg.version; - if (this.opponentVersion === 0) { - // set to null to indicate that we've chosen an opponent, but because - // they're v0 they have no party ID (even if they sent one, we're ignoring it) - this.opponentPartyId = null; - } - else { - // set to their party ID, or if they're naughty and didn't send one despite - // not being v0, set it to null to indicate we picked an opponent with no - // party ID - this.opponentPartyId = msg.party_id || null; - } - this.opponentCaps = msg.capabilities || {}; - this.opponentMember = (_a = this.client.getRoom(this.roomId).getMember(ev.getSender())) !== null && _a !== void 0 ? _a : undefined; - } - addBufferedIceCandidates() { - return __awaiter(this, void 0, void 0, function* () { - const bufferedCandidates = this.remoteCandidateBuffer.get(this.opponentPartyId); - if (bufferedCandidates) { - logger_1.logger.info(`Call ${this.callId} addBufferedIceCandidates() adding ${bufferedCandidates.length} buffered candidates for opponent ${this.opponentPartyId}`); - yield this.addIceCandidates(bufferedCandidates); - } - this.remoteCandidateBuffer.clear(); - }); - } - addIceCandidates(candidates) { - return __awaiter(this, void 0, void 0, function* () { - for (const candidate of candidates) { - if ((candidate.sdpMid === null || candidate.sdpMid === undefined) && - (candidate.sdpMLineIndex === null || candidate.sdpMLineIndex === undefined)) { - logger_1.logger.debug(`Call ${this.callId} addIceCandidates() got remote ICE end-of-candidates`); - } - else { - logger_1.logger.debug(`Call ${this.callId} addIceCandidates() got remote ICE candidate (sdpMid=${candidate.sdpMid}, candidate=${candidate.candidate})`); - } - try { - yield this.peerConn.addIceCandidate(candidate); - } - catch (err) { - if (!this.ignoreOffer) { - logger_1.logger.info(`Call ${this.callId} addIceCandidates() failed to add remote ICE candidate`, err); - } - } - } - }); - } - get hasPeerConnection() { - return Boolean(this.peerConn); - } - initStats(stats, peerId = "unknown") { - this.stats = stats; - this.stats.start(); - } -} -exports.MatrixCall = MatrixCall; -function setTracksEnabled(tracks, enabled) { - for (const track of tracks) { - track.enabled = enabled; - } -} -exports.setTracksEnabled = setTracksEnabled; -function supportsMatrixCall() { - // typeof prevents Node from erroring on an undefined reference - if (typeof window === "undefined" || typeof document === "undefined") { - // NB. We don't log here as apps try to create a call object as a test for - // whether calls are supported, so we shouldn't fill the logs up. - return false; - } - // Firefox throws on so little as accessing the RTCPeerConnection when operating in a secure mode. - // There's some information at https://bugzilla.mozilla.org/show_bug.cgi?id=1542616 though the concern - // is that the browser throwing a SecurityError will brick the client creation process. - try { - const supported = Boolean(window.RTCPeerConnection || - window.RTCSessionDescription || - window.RTCIceCandidate || - navigator.mediaDevices); - if (!supported) { - /* istanbul ignore if */ // Adds a lot of noise to test runs, so disable logging there. - if (process.env.NODE_ENV !== "test") { - logger_1.logger.error("WebRTC is not supported in this browser / environment"); - } - return false; - } - } - catch (e) { - logger_1.logger.error("Exception thrown when trying to access WebRTC", e); - return false; - } - return true; -} -exports.supportsMatrixCall = supportsMatrixCall; -/** - * DEPRECATED - * Use client.createCall() - * - * Create a new Matrix call for the browser. - * @param client - The client instance to use. - * @param roomId - The room the call is in. - * @param options - DEPRECATED optional options map. - * @returns the call or null if the browser doesn't support calling. - */ -function createNewMatrixCall(client, roomId, options) { - if (!supportsMatrixCall()) - return null; - const optionsForceTURN = options ? options.forceTURN : false; - const opts = { - client: client, - roomId: roomId, - invitee: options === null || options === void 0 ? void 0 : options.invitee, - turnServers: client.getTurnServers(), - // call level options - forceTURN: client.forceTURN || optionsForceTURN, - opponentDeviceId: options === null || options === void 0 ? void 0 : options.opponentDeviceId, - opponentSessionId: options === null || options === void 0 ? void 0 : options.opponentSessionId, - groupCallId: options === null || options === void 0 ? void 0 : options.groupCallId, - }; - const call = new MatrixCall(opts); - client.reEmitter.reEmit(call, Object.values(CallEvent)); - return call; -} -exports.createNewMatrixCall = createNewMatrixCall; - -}).call(this)}).call(this,require('_process')) - -},{"../@types/event":306,"../crypto/deviceinfo":340,"../http-api":367,"../logger":374,"../models/typed-event-emitter":395,"../randomstring":398,"../utils":416,"./callEventTypes":420,"./callFeed":421,"./groupCall":422,"_process":237,"sdp-transform":253,"uuid":287}],419:[function(require,module,exports){ -"use strict"; -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CallEventHandler = exports.CallEventHandlerEvent = void 0; -const logger_1 = require("../logger"); -const call_1 = require("./call"); -const event_1 = require("../@types/event"); -const client_1 = require("../client"); -const groupCall_1 = require("./groupCall"); -const room_1 = require("../models/room"); -// Don't ring unless we'd be ringing for at least 3 seconds: the user needs some -// time to press the 'accept' button -const RING_GRACE_PERIOD = 3000; -var CallEventHandlerEvent; -(function (CallEventHandlerEvent) { - CallEventHandlerEvent["Incoming"] = "Call.incoming"; -})(CallEventHandlerEvent = exports.CallEventHandlerEvent || (exports.CallEventHandlerEvent = {})); -class CallEventHandler { - constructor(client) { - this.nextSeqByCall = new Map(); - this.toDeviceEventBuffers = new Map(); - this.onSync = () => { - // Process the current event buffer and start queuing into a new one. - const currentEventBuffer = this.callEventBuffer; - this.callEventBuffer = []; - // Ensure correct ordering by only processing this queue after the previous one has finished processing - if (this.eventBufferPromiseChain) { - this.eventBufferPromiseChain = this.eventBufferPromiseChain.then(() => this.evaluateEventBuffer(currentEventBuffer)); - } - else { - this.eventBufferPromiseChain = this.evaluateEventBuffer(currentEventBuffer); - } - }; - this.onRoomTimeline = (event) => { - this.callEventBuffer.push(event); - }; - this.onToDeviceEvent = (event) => { - const content = event.getContent(); - if (!content.call_id) { - this.callEventBuffer.push(event); - return; - } - if (!this.nextSeqByCall.has(content.call_id)) { - this.nextSeqByCall.set(content.call_id, 0); - } - if (content.seq === undefined) { - this.callEventBuffer.push(event); - return; - } - const nextSeq = this.nextSeqByCall.get(content.call_id) || 0; - if (content.seq !== nextSeq) { - if (!this.toDeviceEventBuffers.has(content.call_id)) { - this.toDeviceEventBuffers.set(content.call_id, []); - } - const buffer = this.toDeviceEventBuffers.get(content.call_id); - const index = buffer.findIndex((e) => e.getContent().seq > content.seq); - if (index === -1) { - buffer.push(event); - } - else { - buffer.splice(index, 0, event); - } - } - else { - const callId = content.call_id; - this.callEventBuffer.push(event); - this.nextSeqByCall.set(callId, content.seq + 1); - const buffer = this.toDeviceEventBuffers.get(callId); - let nextEvent = buffer && buffer.shift(); - while (nextEvent && nextEvent.getContent().seq === this.nextSeqByCall.get(callId)) { - this.callEventBuffer.push(nextEvent); - this.nextSeqByCall.set(callId, nextEvent.getContent().seq + 1); - nextEvent = buffer.shift(); - } - } - }; - this.client = client; - this.calls = new Map(); - // The sync code always emits one event at a time, so it will patiently - // wait for us to finish processing a call invite before delivering the - // next event, even if that next event is a hangup. We therefore accumulate - // all our call events and then process them on the 'sync' event, ie. - // each time a sync has completed. This way, we can avoid emitting incoming - // call events if we get both the invite and answer/hangup in the same sync. - // This happens quite often, eg. replaying sync from storage, catchup sync - // after loading and after we've been offline for a bit. - this.callEventBuffer = []; - this.candidateEventsByCall = new Map(); - } - start() { - this.client.on(client_1.ClientEvent.Sync, this.onSync); - this.client.on(room_1.RoomEvent.Timeline, this.onRoomTimeline); - this.client.on(client_1.ClientEvent.ToDeviceEvent, this.onToDeviceEvent); - } - stop() { - this.client.removeListener(client_1.ClientEvent.Sync, this.onSync); - this.client.removeListener(room_1.RoomEvent.Timeline, this.onRoomTimeline); - this.client.removeListener(client_1.ClientEvent.ToDeviceEvent, this.onToDeviceEvent); - } - evaluateEventBuffer(eventBuffer) { - return __awaiter(this, void 0, void 0, function* () { - yield Promise.all(eventBuffer.map((event) => this.client.decryptEventIfNeeded(event))); - const callEvents = eventBuffer.filter((event) => { - const eventType = event.getType(); - return eventType.startsWith("m.call.") || eventType.startsWith("org.matrix.call."); - }); - const ignoreCallIds = new Set(); - // inspect the buffer and mark all calls which have been answered - // or hung up before passing them to the call event handler. - for (const event of callEvents) { - const eventType = event.getType(); - if (eventType === event_1.EventType.CallAnswer || eventType === event_1.EventType.CallHangup) { - ignoreCallIds.add(event.getContent().call_id); - } - } - // Process call events in the order that they were received - for (const event of callEvents) { - const eventType = event.getType(); - const callId = event.getContent().call_id; - if (eventType === event_1.EventType.CallInvite && ignoreCallIds.has(callId)) { - // This call has previously been answered or hung up: ignore it - continue; - } - try { - yield this.handleCallEvent(event); - } - catch (e) { - logger_1.logger.error("CallEventHandler evaluateEventBuffer() caught exception handling call event", e); - } - } - }); - } - handleCallEvent(event) { - var _a, _b, _c, _d, _e, _f; - return __awaiter(this, void 0, void 0, function* () { - this.client.emit(client_1.ClientEvent.ReceivedVoipEvent, event); - const content = event.getContent(); - const callRoomId = event.getRoomId() || ((_b = (_a = this.client.groupCallEventHandler.getGroupCallById(content.conf_id)) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.roomId); - const groupCallId = content.conf_id; - const type = event.getType(); - const senderId = event.getSender(); - let call = content.call_id ? this.calls.get(content.call_id) : undefined; - let opponentDeviceId; - let groupCall; - if (groupCallId) { - groupCall = this.client.groupCallEventHandler.getGroupCallById(groupCallId); - if (!groupCall) { - logger_1.logger.warn(`CallEventHandler handleCallEvent() could not find a group call - ignoring event (groupCallId=${groupCallId}, type=${type})`); - return; - } - opponentDeviceId = content.device_id; - if (!opponentDeviceId) { - logger_1.logger.warn(`CallEventHandler handleCallEvent() could not find a device id - ignoring event (senderId=${senderId})`); - groupCall.emit(groupCall_1.GroupCallEvent.Error, new groupCall_1.GroupCallUnknownDeviceError(senderId)); - return; - } - if (content.dest_session_id !== this.client.getSessionId()) { - logger_1.logger.warn("CallEventHandler handleCallEvent() call event does not match current session id - ignoring"); - return; - } - } - const weSentTheEvent = senderId === this.client.credentials.userId && - (opponentDeviceId === undefined || opponentDeviceId === this.client.getDeviceId()); - if (!callRoomId) - return; - if (type === event_1.EventType.CallInvite) { - // ignore invites you send - if (weSentTheEvent) - return; - // expired call - if (event.getLocalAge() > content.lifetime - RING_GRACE_PERIOD) - return; - // stale/old invite event - if (call && call.state === call_1.CallState.Ended) - return; - if (call) { - logger_1.logger.warn(`CallEventHandler handleCallEvent() already has a call but got an invite - clobbering (callId=${content.call_id})`); - } - if (content.invitee && content.invitee !== this.client.getUserId()) { - return; // This invite was meant for another user in the room - } - const timeUntilTurnCresExpire = ((_c = this.client.getTurnServersExpiry()) !== null && _c !== void 0 ? _c : 0) - Date.now(); - logger_1.logger.info("CallEventHandler handleCallEvent() current turn creds expire in " + timeUntilTurnCresExpire + " ms"); - call = - (_d = (0, call_1.createNewMatrixCall)(this.client, callRoomId, { - forceTURN: this.client.forceTURN, - opponentDeviceId, - groupCallId, - opponentSessionId: content.sender_session_id, - })) !== null && _d !== void 0 ? _d : undefined; - if (!call) { - logger_1.logger.log(`CallEventHandler handleCallEvent() this client does not support WebRTC (callId=${content.call_id})`); - // don't hang up the call: there could be other clients - // connected that do support WebRTC and declining the - // the call on their behalf would be really annoying. - return; - } - call.callId = content.call_id; - const stats = groupCall === null || groupCall === void 0 ? void 0 : groupCall.getGroupCallStats(); - if (stats) { - call.initStats(stats); - } - try { - yield call.initWithInvite(event); - } - catch (e) { - if (e instanceof call_1.CallError) { - if (e.code === groupCall_1.GroupCallErrorCode.UnknownDevice) { - groupCall === null || groupCall === void 0 ? void 0 : groupCall.emit(groupCall_1.GroupCallEvent.Error, e); - } - else { - logger_1.logger.error(e); - } - } - } - this.calls.set(call.callId, call); - // if we stashed candidate events for that call ID, play them back now - if (this.candidateEventsByCall.get(call.callId)) { - for (const ev of this.candidateEventsByCall.get(call.callId)) { - call.onRemoteIceCandidatesReceived(ev); - } - } - // Were we trying to call that user (room)? - let existingCall; - for (const thisCall of this.calls.values()) { - const isCalling = [call_1.CallState.WaitLocalMedia, call_1.CallState.CreateOffer, call_1.CallState.InviteSent].includes(thisCall.state); - if (call.roomId === thisCall.roomId && - thisCall.direction === call_1.CallDirection.Outbound && - ((_e = call.getOpponentMember()) === null || _e === void 0 ? void 0 : _e.userId) === thisCall.invitee && - isCalling) { - existingCall = thisCall; - break; - } - } - if (existingCall) { - if (existingCall.callId > call.callId) { - logger_1.logger.log(`CallEventHandler handleCallEvent() detected glare - answering incoming call and canceling outgoing call (incomingId=${call.callId}, outgoingId=${existingCall.callId})`); - existingCall.replacedBy(call); - } - else { - logger_1.logger.log(`CallEventHandler handleCallEvent() detected glare - hanging up incoming call (incomingId=${call.callId}, outgoingId=${existingCall.callId})`); - call.hangup(call_1.CallErrorCode.Replaced, true); - } - } - else { - this.client.emit(CallEventHandlerEvent.Incoming, call); - } - return; - } - else if (type === event_1.EventType.CallCandidates) { - if (weSentTheEvent) - return; - if (!call) { - // store the candidates; we may get a call eventually. - if (!this.candidateEventsByCall.has(content.call_id)) { - this.candidateEventsByCall.set(content.call_id, []); - } - this.candidateEventsByCall.get(content.call_id).push(event); - } - else { - call.onRemoteIceCandidatesReceived(event); - } - return; - } - else if ([event_1.EventType.CallHangup, event_1.EventType.CallReject].includes(type)) { - // Note that we also observe our own hangups here so we can see - // if we've already rejected a call that would otherwise be valid - if (!call) { - // if not live, store the fact that the call has ended because - // we're probably getting events backwards so - // the hangup will come before the invite - call = - (_f = (0, call_1.createNewMatrixCall)(this.client, callRoomId, { - opponentDeviceId, - opponentSessionId: content.sender_session_id, - })) !== null && _f !== void 0 ? _f : undefined; - if (call) { - call.callId = content.call_id; - call.initWithHangup(event); - this.calls.set(content.call_id, call); - } - } - else { - if (call.state !== call_1.CallState.Ended) { - if (type === event_1.EventType.CallHangup) { - call.onHangupReceived(content); - } - else { - call.onRejectReceived(content); - } - // @ts-expect-error typescript thinks the state can't be 'ended' because we're - // inside the if block where it wasn't, but it could have changed because - // on[Hangup|Reject]Received are side-effecty. - if (call.state === call_1.CallState.Ended) - this.calls.delete(content.call_id); - } - } - return; - } - // The following events need a call and a peer connection - if (!call || !call.hasPeerConnection) { - logger_1.logger.info(`CallEventHandler handleCallEvent() discarding possible call event as we don't have a call (type=${type})`); - return; - } - // Ignore remote echo - if (event.getContent().party_id === call.ourPartyId) - return; - switch (type) { - case event_1.EventType.CallAnswer: - if (weSentTheEvent) { - if (call.state === call_1.CallState.Ringing) { - call.onAnsweredElsewhere(content); - } - } - else { - call.onAnswerReceived(event); - } - break; - case event_1.EventType.CallSelectAnswer: - call.onSelectAnswerReceived(event); - break; - case event_1.EventType.CallNegotiate: - call.onNegotiateReceived(event); - break; - case event_1.EventType.CallAssertedIdentity: - case event_1.EventType.CallAssertedIdentityPrefix: - call.onAssertedIdentityReceived(event); - break; - case event_1.EventType.CallSDPStreamMetadataChanged: - case event_1.EventType.CallSDPStreamMetadataChangedPrefix: - call.onSDPStreamMetadataChangedReceived(event); - break; - } - }); - } -} -exports.CallEventHandler = CallEventHandler; - -},{"../@types/event":306,"../client":321,"../logger":374,"../models/room":392,"./call":418,"./groupCall":422}],420:[function(require,module,exports){ -"use strict"; -// allow non-camelcase as these are events type that go onto the wire -/* eslint-disable camelcase */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SDPStreamMetadataPurpose = exports.SDPStreamMetadataKey = void 0; -// TODO: Change to "sdp_stream_metadata" when MSC3077 is merged -exports.SDPStreamMetadataKey = "org.matrix.msc3077.sdp_stream_metadata"; -var SDPStreamMetadataPurpose; -(function (SDPStreamMetadataPurpose) { - SDPStreamMetadataPurpose["Usermedia"] = "m.usermedia"; - SDPStreamMetadataPurpose["Screenshare"] = "m.screenshare"; -})(SDPStreamMetadataPurpose = exports.SDPStreamMetadataPurpose || (exports.SDPStreamMetadataPurpose = {})); -/* eslint-enable camelcase */ - -},{}],421:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 Šimon Brandner - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CallFeed = exports.CallFeedEvent = exports.SPEAKING_THRESHOLD = void 0; -const callEventTypes_1 = require("./callEventTypes"); -const audioContext_1 = require("./audioContext"); -const logger_1 = require("../logger"); -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -const call_1 = require("./call"); -const POLLING_INTERVAL = 200; // ms -exports.SPEAKING_THRESHOLD = -60; // dB -const SPEAKING_SAMPLE_COUNT = 8; // samples -var CallFeedEvent; -(function (CallFeedEvent) { - CallFeedEvent["NewStream"] = "new_stream"; - CallFeedEvent["MuteStateChanged"] = "mute_state_changed"; - CallFeedEvent["LocalVolumeChanged"] = "local_volume_changed"; - CallFeedEvent["VolumeChanged"] = "volume_changed"; - CallFeedEvent["ConnectedChanged"] = "connected_changed"; - CallFeedEvent["Speaking"] = "speaking"; - CallFeedEvent["Disposed"] = "disposed"; -})(CallFeedEvent = exports.CallFeedEvent || (exports.CallFeedEvent = {})); -class CallFeed extends typed_event_emitter_1.TypedEventEmitter { - constructor(opts) { - super(); - this.localVolume = 1; - this.measuringVolumeActivity = false; - this.speakingThreshold = exports.SPEAKING_THRESHOLD; - this.speaking = false; - this._disposed = false; - this._connected = false; - this.onAddTrack = () => { - this.emit(CallFeedEvent.NewStream, this.stream); - }; - this.onCallState = (state) => { - if (state === call_1.CallState.Connected) { - this.connected = true; - } - else if (state === call_1.CallState.Connecting) { - this.connected = false; - } - }; - this.volumeLooper = () => { - if (!this.analyser) - return; - if (!this.measuringVolumeActivity) - return; - this.analyser.getFloatFrequencyData(this.frequencyBinCount); - let maxVolume = -Infinity; - for (const volume of this.frequencyBinCount) { - if (volume > maxVolume) { - maxVolume = volume; - } - } - this.speakingVolumeSamples.shift(); - this.speakingVolumeSamples.push(maxVolume); - this.emit(CallFeedEvent.VolumeChanged, maxVolume); - let newSpeaking = false; - for (const volume of this.speakingVolumeSamples) { - if (volume > this.speakingThreshold) { - newSpeaking = true; - break; - } - } - if (this.speaking !== newSpeaking) { - this.speaking = newSpeaking; - this.emit(CallFeedEvent.Speaking, this.speaking); - } - this.volumeLooperTimeout = setTimeout(this.volumeLooper, POLLING_INTERVAL); - }; - this.client = opts.client; - this.call = opts.call; - this.roomId = opts.roomId; - this.userId = opts.userId; - this.deviceId = opts.deviceId; - this.purpose = opts.purpose; - this.audioMuted = opts.audioMuted; - this.videoMuted = opts.videoMuted; - this.speakingVolumeSamples = new Array(SPEAKING_SAMPLE_COUNT).fill(-Infinity); - this.sdpMetadataStreamId = opts.stream.id; - this.updateStream(null, opts.stream); - this.stream = opts.stream; // updateStream does this, but this makes TS happier - if (this.hasAudioTrack) { - this.initVolumeMeasuring(); - } - if (opts.call) { - opts.call.addListener(call_1.CallEvent.State, this.onCallState); - this.onCallState(opts.call.state); - } - } - get connected() { - // Local feeds are always considered connected - return this.isLocal() || this._connected; - } - set connected(connected) { - this._connected = connected; - this.emit(CallFeedEvent.ConnectedChanged, this.connected); - } - get hasAudioTrack() { - return this.stream.getAudioTracks().length > 0; - } - updateStream(oldStream, newStream) { - if (newStream === oldStream) - return; - if (oldStream) { - oldStream.removeEventListener("addtrack", this.onAddTrack); - this.measureVolumeActivity(false); - } - this.stream = newStream; - newStream.addEventListener("addtrack", this.onAddTrack); - if (this.hasAudioTrack) { - this.initVolumeMeasuring(); - } - else { - this.measureVolumeActivity(false); - } - this.emit(CallFeedEvent.NewStream, this.stream); - } - initVolumeMeasuring() { - if (!this.hasAudioTrack) - return; - if (!this.audioContext) - this.audioContext = (0, audioContext_1.acquireContext)(); - this.analyser = this.audioContext.createAnalyser(); - this.analyser.fftSize = 512; - this.analyser.smoothingTimeConstant = 0.1; - const mediaStreamAudioSourceNode = this.audioContext.createMediaStreamSource(this.stream); - mediaStreamAudioSourceNode.connect(this.analyser); - this.frequencyBinCount = new Float32Array(this.analyser.frequencyBinCount); - } - /** - * Returns callRoom member - * @returns member of the callRoom - */ - getMember() { - var _a; - const callRoom = this.client.getRoom(this.roomId); - return (_a = callRoom === null || callRoom === void 0 ? void 0 : callRoom.getMember(this.userId)) !== null && _a !== void 0 ? _a : null; - } - /** - * Returns true if CallFeed is local, otherwise returns false - * @returns is local? - */ - isLocal() { - return (this.userId === this.client.getUserId() && - (this.deviceId === undefined || this.deviceId === this.client.getDeviceId())); - } - /** - * Returns true if audio is muted or if there are no audio - * tracks, otherwise returns false - * @returns is audio muted? - */ - isAudioMuted() { - return this.stream.getAudioTracks().length === 0 || this.audioMuted; - } - /** - * Returns true video is muted or if there are no video - * tracks, otherwise returns false - * @returns is video muted? - */ - isVideoMuted() { - // We assume only one video track - return this.stream.getVideoTracks().length === 0 || this.videoMuted; - } - isSpeaking() { - return this.speaking; - } - /** - * Replaces the current MediaStream with a new one. - * The stream will be different and new stream as remote parties are - * concerned, but this can be used for convenience locally to set up - * volume listeners automatically on the new stream etc. - * @param newStream - new stream with which to replace the current one - */ - setNewStream(newStream) { - this.updateStream(this.stream, newStream); - } - /** - * Set one or both of feed's internal audio and video video mute state - * Either value may be null to leave it as-is - * @param audioMuted - is the feed's audio muted? - * @param videoMuted - is the feed's video muted? - */ - setAudioVideoMuted(audioMuted, videoMuted) { - if (audioMuted !== null) { - if (this.audioMuted !== audioMuted) { - this.speakingVolumeSamples.fill(-Infinity); - } - this.audioMuted = audioMuted; - } - if (videoMuted !== null) - this.videoMuted = videoMuted; - this.emit(CallFeedEvent.MuteStateChanged, this.audioMuted, this.videoMuted); - } - /** - * Starts emitting volume_changed events where the emitter value is in decibels - * @param enabled - emit volume changes - */ - measureVolumeActivity(enabled) { - if (enabled) { - if (!this.analyser || !this.frequencyBinCount || !this.hasAudioTrack) - return; - this.measuringVolumeActivity = true; - this.volumeLooper(); - } - else { - this.measuringVolumeActivity = false; - this.speakingVolumeSamples.fill(-Infinity); - this.emit(CallFeedEvent.VolumeChanged, -Infinity); - } - } - setSpeakingThreshold(threshold) { - this.speakingThreshold = threshold; - } - clone() { - const mediaHandler = this.client.getMediaHandler(); - const stream = this.stream.clone(); - logger_1.logger.log(`CallFeed clone() cloning stream (originalStreamId=${this.stream.id}, newStreamId${stream.id})`); - if (this.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Usermedia) { - mediaHandler.userMediaStreams.push(stream); - } - else { - mediaHandler.screensharingStreams.push(stream); - } - return new CallFeed({ - client: this.client, - roomId: this.roomId, - userId: this.userId, - deviceId: this.deviceId, - stream, - purpose: this.purpose, - audioMuted: this.audioMuted, - videoMuted: this.videoMuted, - }); - } - dispose() { - var _a, _b; - clearTimeout(this.volumeLooperTimeout); - (_a = this.stream) === null || _a === void 0 ? void 0 : _a.removeEventListener("addtrack", this.onAddTrack); - (_b = this.call) === null || _b === void 0 ? void 0 : _b.removeListener(call_1.CallEvent.State, this.onCallState); - if (this.audioContext) { - this.audioContext = undefined; - this.analyser = undefined; - (0, audioContext_1.releaseContext)(); - } - this._disposed = true; - this.emit(CallFeedEvent.Disposed); - } - get disposed() { - return this._disposed; - } - set disposed(value) { - this._disposed = value; - } - getLocalVolume() { - return this.localVolume; - } - setLocalVolume(localVolume) { - this.localVolume = localVolume; - this.emit(CallFeedEvent.LocalVolumeChanged, localVolume); - } -} -exports.CallFeed = CallFeed; - -},{"../logger":374,"../models/typed-event-emitter":395,"./audioContext":417,"./call":418,"./callEventTypes":420}],422:[function(require,module,exports){ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GroupCall = exports.GroupCallState = exports.OtherUserSpeakingError = exports.GroupCallUnknownDeviceError = exports.GroupCallError = exports.GroupCallErrorCode = exports.GroupCallStatsReportEvent = exports.GroupCallEvent = exports.GroupCallTerminationReason = exports.GroupCallType = exports.GroupCallIntent = void 0; -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -const callFeed_1 = require("./callFeed"); -const call_1 = require("./call"); -const room_state_1 = require("../models/room-state"); -const logger_1 = require("../logger"); -const ReEmitter_1 = require("../ReEmitter"); -const callEventTypes_1 = require("./callEventTypes"); -const event_1 = require("../@types/event"); -const callEventHandler_1 = require("./callEventHandler"); -const groupCallEventHandler_1 = require("./groupCallEventHandler"); -const utils_1 = require("../utils"); -const groupCallStats_1 = require("./stats/groupCallStats"); -const statsReport_1 = require("./stats/statsReport"); -var GroupCallIntent; -(function (GroupCallIntent) { - GroupCallIntent["Ring"] = "m.ring"; - GroupCallIntent["Prompt"] = "m.prompt"; - GroupCallIntent["Room"] = "m.room"; -})(GroupCallIntent = exports.GroupCallIntent || (exports.GroupCallIntent = {})); -var GroupCallType; -(function (GroupCallType) { - GroupCallType["Video"] = "m.video"; - GroupCallType["Voice"] = "m.voice"; -})(GroupCallType = exports.GroupCallType || (exports.GroupCallType = {})); -var GroupCallTerminationReason; -(function (GroupCallTerminationReason) { - GroupCallTerminationReason["CallEnded"] = "call_ended"; -})(GroupCallTerminationReason = exports.GroupCallTerminationReason || (exports.GroupCallTerminationReason = {})); -/** - * Because event names are just strings, they do need - * to be unique over all event types of event emitter. - * Some objects could emit more then one set of events. - */ -var GroupCallEvent; -(function (GroupCallEvent) { - GroupCallEvent["GroupCallStateChanged"] = "group_call_state_changed"; - GroupCallEvent["ActiveSpeakerChanged"] = "active_speaker_changed"; - GroupCallEvent["CallsChanged"] = "calls_changed"; - GroupCallEvent["UserMediaFeedsChanged"] = "user_media_feeds_changed"; - GroupCallEvent["ScreenshareFeedsChanged"] = "screenshare_feeds_changed"; - GroupCallEvent["LocalScreenshareStateChanged"] = "local_screenshare_state_changed"; - GroupCallEvent["LocalMuteStateChanged"] = "local_mute_state_changed"; - GroupCallEvent["ParticipantsChanged"] = "participants_changed"; - GroupCallEvent["Error"] = "group_call_error"; -})(GroupCallEvent = exports.GroupCallEvent || (exports.GroupCallEvent = {})); -var GroupCallStatsReportEvent; -(function (GroupCallStatsReportEvent) { - GroupCallStatsReportEvent["ConnectionStats"] = "GroupCall.connection_stats"; - GroupCallStatsReportEvent["ByteSentStats"] = "GroupCall.byte_sent_stats"; -})(GroupCallStatsReportEvent = exports.GroupCallStatsReportEvent || (exports.GroupCallStatsReportEvent = {})); -var GroupCallErrorCode; -(function (GroupCallErrorCode) { - GroupCallErrorCode["NoUserMedia"] = "no_user_media"; - GroupCallErrorCode["UnknownDevice"] = "unknown_device"; - GroupCallErrorCode["PlaceCallFailed"] = "place_call_failed"; -})(GroupCallErrorCode = exports.GroupCallErrorCode || (exports.GroupCallErrorCode = {})); -class GroupCallError extends Error { - constructor(code, msg, err) { - // Still don't think there's any way to have proper nested errors - if (err) { - super(msg + ": " + err); - } - else { - super(msg); - } - this.code = code; - } -} -exports.GroupCallError = GroupCallError; -class GroupCallUnknownDeviceError extends GroupCallError { - constructor(userId) { - super(GroupCallErrorCode.UnknownDevice, "No device found for " + userId); - this.userId = userId; - } -} -exports.GroupCallUnknownDeviceError = GroupCallUnknownDeviceError; -class OtherUserSpeakingError extends Error { - constructor() { - super("Cannot unmute: another user is speaking"); - } -} -exports.OtherUserSpeakingError = OtherUserSpeakingError; -var GroupCallState; -(function (GroupCallState) { - GroupCallState["LocalCallFeedUninitialized"] = "local_call_feed_uninitialized"; - GroupCallState["InitializingLocalCallFeed"] = "initializing_local_call_feed"; - GroupCallState["LocalCallFeedInitialized"] = "local_call_feed_initialized"; - GroupCallState["Entered"] = "entered"; - GroupCallState["Ended"] = "ended"; -})(GroupCallState = exports.GroupCallState || (exports.GroupCallState = {})); -const DEVICE_TIMEOUT = 1000 * 60 * 60; // 1 hour -function getCallUserId(call) { - var _a; - return ((_a = call.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId) || call.invitee || null; -} -class GroupCall extends typed_event_emitter_1.TypedEventEmitter { - constructor(client, room, type, isPtt, intent, groupCallId, dataChannelsEnabled, dataChannelOptions, isCallWithoutVideoAndAudio) { - var _a, _b; - super(); - this.client = client; - this.room = room; - this.type = type; - this.isPtt = isPtt; - this.intent = intent; - this.dataChannelsEnabled = dataChannelsEnabled; - this.dataChannelOptions = dataChannelOptions; - // Config - this.activeSpeakerInterval = 1000; - this.retryCallInterval = 5000; - this.participantTimeout = 1000 * 15; - this.pttMaxTransmitTime = 1000 * 20; - this.userMediaFeeds = []; - this.screenshareFeeds = []; - this.calls = new Map(); // user_id -> device_id -> MatrixCall - this.callHandlers = new Map(); // user_id -> device_id -> ICallHandlers - this.retryCallCounts = new Map(); // user_id -> device_id -> count - this.transmitTimer = null; - this.participantsExpirationTimer = null; - this.resendMemberStateTimer = null; - this.initWithAudioMuted = false; - this.initWithVideoMuted = false; - this.onConnectionStats = (report) => { - // @TODO: Implement data argumentation - this.emit(GroupCallStatsReportEvent.ConnectionStats, { report }); - }; - this.onByteSentStats = (report) => { - // @TODO: Implement data argumentation - this.emit(GroupCallStatsReportEvent.ByteSentStats, { report }); - }; - this._state = GroupCallState.LocalCallFeedUninitialized; - this._participants = new Map(); - this._creationTs = null; - this._enteredViaAnotherSession = false; - /* - * Call Setup - * - * There are two different paths for calls to be created: - * 1. Incoming calls triggered by the Call.incoming event. - * 2. Outgoing calls to the initial members of a room or new members - * as they are observed by the RoomState.members event. - */ - this.onIncomingCall = (newCall) => { - var _a, _b; - // The incoming calls may be for another room, which we will ignore. - if (newCall.roomId !== this.room.roomId) { - return; - } - if (newCall.state !== call_1.CallState.Ringing) { - logger_1.logger.warn(`GroupCall ${this.groupCallId} onIncomingCall() incoming call no longer in ringing state - ignoring`); - return; - } - if (!newCall.groupCallId || newCall.groupCallId !== this.groupCallId) { - logger_1.logger.log(`GroupCall ${this.groupCallId} onIncomingCall() ignored because it doesn't match the current group call`); - newCall.reject(); - return; - } - const opponentUserId = (_a = newCall.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId; - if (opponentUserId === undefined) { - logger_1.logger.warn(`GroupCall ${this.groupCallId} onIncomingCall() incoming call with no member - ignoring`); - return; - } - const deviceMap = (_b = this.calls.get(opponentUserId)) !== null && _b !== void 0 ? _b : new Map(); - const prevCall = deviceMap.get(newCall.getOpponentDeviceId()); - if ((prevCall === null || prevCall === void 0 ? void 0 : prevCall.callId) === newCall.callId) - return; - logger_1.logger.log(`GroupCall ${this.groupCallId} onIncomingCall() incoming call (userId=${opponentUserId}, callId=${newCall.callId})`); - if (prevCall) - prevCall.hangup(call_1.CallErrorCode.Replaced, false); - this.initCall(newCall); - const feeds = this.getLocalFeeds().map((feed) => feed.clone()); - if (!this.callExpected(newCall)) { - // Disable our tracks for users not explicitly participating in the - // call but trying to receive the feeds - for (const feed of feeds) { - (0, call_1.setTracksEnabled)(feed.stream.getAudioTracks(), false); - (0, call_1.setTracksEnabled)(feed.stream.getVideoTracks(), false); - } - } - newCall.answerWithCallFeeds(feeds); - deviceMap.set(newCall.getOpponentDeviceId(), newCall); - this.calls.set(opponentUserId, deviceMap); - this.emit(GroupCallEvent.CallsChanged, this.calls); - }; - this.onRetryCallLoop = () => { - var _a; - let needsRetry = false; - for (const [{ userId }, participantMap] of this.participants) { - const callMap = this.calls.get(userId); - let retriesMap = this.retryCallCounts.get(userId); - for (const [deviceId, participant] of participantMap) { - const call = callMap === null || callMap === void 0 ? void 0 : callMap.get(deviceId); - const retries = (_a = retriesMap === null || retriesMap === void 0 ? void 0 : retriesMap.get(deviceId)) !== null && _a !== void 0 ? _a : 0; - if ((call === null || call === void 0 ? void 0 : call.getOpponentSessionId()) !== participant.sessionId && - this.wantsOutgoingCall(userId, deviceId) && - retries < 3) { - if (retriesMap === undefined) { - retriesMap = new Map(); - this.retryCallCounts.set(userId, retriesMap); - } - retriesMap.set(deviceId, retries + 1); - needsRetry = true; - } - } - } - if (needsRetry) - this.placeOutgoingCalls(); - }; - this.onCallFeedsChanged = (call) => { - const opponentMemberId = getCallUserId(call); - const opponentDeviceId = call.getOpponentDeviceId(); - if (!opponentMemberId) { - throw new Error("Cannot change call feeds without user id"); - } - const currentUserMediaFeed = this.getUserMediaFeed(opponentMemberId, opponentDeviceId); - const remoteUsermediaFeed = call.remoteUsermediaFeed; - const remoteFeedChanged = remoteUsermediaFeed !== currentUserMediaFeed; - if (remoteFeedChanged) { - if (!currentUserMediaFeed && remoteUsermediaFeed) { - this.addUserMediaFeed(remoteUsermediaFeed); - } - else if (currentUserMediaFeed && remoteUsermediaFeed) { - this.replaceUserMediaFeed(currentUserMediaFeed, remoteUsermediaFeed); - } - else if (currentUserMediaFeed && !remoteUsermediaFeed) { - this.removeUserMediaFeed(currentUserMediaFeed); - } - } - const currentScreenshareFeed = this.getScreenshareFeed(opponentMemberId, opponentDeviceId); - const remoteScreensharingFeed = call.remoteScreensharingFeed; - const remoteScreenshareFeedChanged = remoteScreensharingFeed !== currentScreenshareFeed; - if (remoteScreenshareFeedChanged) { - if (!currentScreenshareFeed && remoteScreensharingFeed) { - this.addScreenshareFeed(remoteScreensharingFeed); - } - else if (currentScreenshareFeed && remoteScreensharingFeed) { - this.replaceScreenshareFeed(currentScreenshareFeed, remoteScreensharingFeed); - } - else if (currentScreenshareFeed && !remoteScreensharingFeed) { - this.removeScreenshareFeed(currentScreenshareFeed); - } - } - }; - this.onCallStateChanged = (call, state, _oldState) => { - var _a; - if (state === call_1.CallState.Ended) - return; - const audioMuted = this.localCallFeed.isAudioMuted(); - if (call.localUsermediaStream && call.isMicrophoneMuted() !== audioMuted) { - call.setMicrophoneMuted(audioMuted); - } - const videoMuted = this.localCallFeed.isVideoMuted(); - if (call.localUsermediaStream && call.isLocalVideoMuted() !== videoMuted) { - call.setLocalVideoMuted(videoMuted); - } - const opponentUserId = (_a = call.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId; - if (state === call_1.CallState.Connected && opponentUserId) { - const retriesMap = this.retryCallCounts.get(opponentUserId); - retriesMap === null || retriesMap === void 0 ? void 0 : retriesMap.delete(call.getOpponentDeviceId()); - if ((retriesMap === null || retriesMap === void 0 ? void 0 : retriesMap.size) === 0) - this.retryCallCounts.delete(opponentUserId); - } - }; - this.onCallHangup = (call) => { - var _a, _b; - if (call.hangupReason === call_1.CallErrorCode.Replaced) - return; - const opponentUserId = (_b = (_a = call.getOpponentMember()) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : this.room.getMember(call.invitee).userId; - const deviceMap = this.calls.get(opponentUserId); - // Sanity check that this call is in fact in the map - if ((deviceMap === null || deviceMap === void 0 ? void 0 : deviceMap.get(call.getOpponentDeviceId())) === call) { - this.disposeCall(call, call.hangupReason); - deviceMap.delete(call.getOpponentDeviceId()); - if (deviceMap.size === 0) - this.calls.delete(opponentUserId); - this.emit(GroupCallEvent.CallsChanged, this.calls); - } - }; - this.onCallReplaced = (prevCall, newCall) => { - const opponentUserId = prevCall.getOpponentMember().userId; - let deviceMap = this.calls.get(opponentUserId); - if (deviceMap === undefined) { - deviceMap = new Map(); - this.calls.set(opponentUserId, deviceMap); - } - prevCall.hangup(call_1.CallErrorCode.Replaced, false); - this.initCall(newCall); - deviceMap.set(prevCall.getOpponentDeviceId(), newCall); - this.emit(GroupCallEvent.CallsChanged, this.calls); - }; - this.onActiveSpeakerLoop = () => { - let topAvg = undefined; - let nextActiveSpeaker = undefined; - for (const callFeed of this.userMediaFeeds) { - if (callFeed.isLocal() && this.userMediaFeeds.length > 1) - continue; - const total = callFeed.speakingVolumeSamples.reduce((acc, volume) => acc + Math.max(volume, callFeed_1.SPEAKING_THRESHOLD)); - const avg = total / callFeed.speakingVolumeSamples.length; - if (!topAvg || avg > topAvg) { - topAvg = avg; - nextActiveSpeaker = callFeed; - } - } - if (nextActiveSpeaker && this.activeSpeaker !== nextActiveSpeaker && topAvg && topAvg > callFeed_1.SPEAKING_THRESHOLD) { - this.activeSpeaker = nextActiveSpeaker; - this.emit(GroupCallEvent.ActiveSpeakerChanged, this.activeSpeaker); - } - }; - this.onRoomState = () => this.updateParticipants(); - this.onParticipantsChanged = () => { - // Re-run setTracksEnabled on all calls, so that participants that just - // left get denied access to our media, and participants that just - // joined get granted access - this.forEachCall((call) => { - const expected = this.callExpected(call); - for (const feed of call.getLocalFeeds()) { - (0, call_1.setTracksEnabled)(feed.stream.getAudioTracks(), !feed.isAudioMuted() && expected); - (0, call_1.setTracksEnabled)(feed.stream.getVideoTracks(), !feed.isVideoMuted() && expected); - } - }); - if (this.state === GroupCallState.Entered) - this.placeOutgoingCalls(); - }; - this.onStateChanged = (newState, oldState) => { - if (newState === GroupCallState.Entered || - oldState === GroupCallState.Entered || - newState === GroupCallState.Ended) { - // We either entered, left, or ended the call - this.updateParticipants(); - this.updateMemberState().catch((e) => logger_1.logger.error(`GroupCall ${this.groupCallId} onStateChanged() failed to update member state devices"`, e)); - } - }; - this.onLocalFeedsChanged = () => { - if (this.state === GroupCallState.Entered) { - this.updateMemberState().catch((e) => logger_1.logger.error(`GroupCall ${this.groupCallId} onLocalFeedsChanged() failed to update member state feeds`, e)); - } - }; - this.reEmitter = new ReEmitter_1.ReEmitter(this); - this.groupCallId = groupCallId !== null && groupCallId !== void 0 ? groupCallId : (0, call_1.genCallID)(); - this.creationTs = - (_b = (_a = room.currentState.getStateEvents(event_1.EventType.GroupCallPrefix, this.groupCallId)) === null || _a === void 0 ? void 0 : _a.getTs()) !== null && _b !== void 0 ? _b : null; - this.updateParticipants(); - room.on(room_state_1.RoomStateEvent.Update, this.onRoomState); - this.on(GroupCallEvent.ParticipantsChanged, this.onParticipantsChanged); - this.on(GroupCallEvent.GroupCallStateChanged, this.onStateChanged); - this.on(GroupCallEvent.LocalScreenshareStateChanged, this.onLocalFeedsChanged); - this.allowCallWithoutVideoAndAudio = !!isCallWithoutVideoAndAudio; - const userID = this.client.getUserId() || "unknown"; - this.stats = new groupCallStats_1.GroupCallStats(this.groupCallId, userID); - this.stats.reports.on(statsReport_1.StatsReport.CONNECTION_STATS, this.onConnectionStats); - this.stats.reports.on(statsReport_1.StatsReport.BYTE_SENT_STATS, this.onByteSentStats); - } - create() { - return __awaiter(this, void 0, void 0, function* () { - this.creationTs = Date.now(); - this.client.groupCallEventHandler.groupCalls.set(this.room.roomId, this); - this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Outgoing, this); - const groupCallState = { - "m.intent": this.intent, - "m.type": this.type, - "io.element.ptt": this.isPtt, - // TODO: Specify data-channels better - "dataChannelsEnabled": this.dataChannelsEnabled, - "dataChannelOptions": this.dataChannelsEnabled ? this.dataChannelOptions : undefined, - }; - yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallPrefix, groupCallState, this.groupCallId); - return this; - }); - } - /** - * The group call's state. - */ - get state() { - return this._state; - } - set state(value) { - const prevValue = this._state; - if (value !== prevValue) { - this._state = value; - this.emit(GroupCallEvent.GroupCallStateChanged, value, prevValue); - } - } - /** - * The current participants in the call, as a map from members to device IDs - * to participant info. - */ - get participants() { - return this._participants; - } - set participants(value) { - const prevValue = this._participants; - const participantStateEqual = (x, y) => x.sessionId === y.sessionId && x.screensharing === y.screensharing; - const deviceMapsEqual = (x, y) => (0, utils_1.mapsEqual)(x, y, participantStateEqual); - // Only update if the map actually changed - if (!(0, utils_1.mapsEqual)(value, prevValue, deviceMapsEqual)) { - this._participants = value; - this.emit(GroupCallEvent.ParticipantsChanged, value); - } - } - /** - * The timestamp at which the call was created, or null if it has not yet - * been created. - */ - get creationTs() { - return this._creationTs; - } - set creationTs(value) { - this._creationTs = value; - } - /** - * Whether the local device has entered this call via another session, such - * as a widget. - */ - get enteredViaAnotherSession() { - return this._enteredViaAnotherSession; - } - set enteredViaAnotherSession(value) { - this._enteredViaAnotherSession = value; - this.updateParticipants(); - } - /** - * Executes the given callback on all calls in this group call. - * @param f - The callback. - */ - forEachCall(f) { - for (const deviceMap of this.calls.values()) { - for (const call of deviceMap.values()) - f(call); - } - } - getLocalFeeds() { - const feeds = []; - if (this.localCallFeed) - feeds.push(this.localCallFeed); - if (this.localScreenshareFeed) - feeds.push(this.localScreenshareFeed); - return feeds; - } - hasLocalParticipant() { - var _a, _b; - return ((_b = (_a = this.participants.get(this.room.getMember(this.client.getUserId()))) === null || _a === void 0 ? void 0 : _a.has(this.client.getDeviceId())) !== null && _b !== void 0 ? _b : false); - } - /** - * Determines whether the given call is one that we were expecting to exist - * given our knowledge of who is participating in the group call. - */ - callExpected(call) { - var _a; - const userId = getCallUserId(call); - const member = userId === null ? null : this.room.getMember(userId); - const deviceId = call.getOpponentDeviceId(); - return member !== null && deviceId !== undefined && ((_a = this.participants.get(member)) === null || _a === void 0 ? void 0 : _a.get(deviceId)) !== undefined; - } - initLocalCallFeed() { - return __awaiter(this, void 0, void 0, function* () { - if (this.state !== GroupCallState.LocalCallFeedUninitialized) { - throw new Error(`Cannot initialize local call feed in the "${this.state}" state.`); - } - this.state = GroupCallState.InitializingLocalCallFeed; - // wraps the real method to serialise calls, because we don't want to try starting - // multiple call feeds at once - if (this.initCallFeedPromise) - return this.initCallFeedPromise; - try { - this.initCallFeedPromise = this.initLocalCallFeedInternal(); - yield this.initCallFeedPromise; - } - finally { - this.initCallFeedPromise = undefined; - } - }); - } - initLocalCallFeedInternal() { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`GroupCall ${this.groupCallId} initLocalCallFeedInternal() running`); - let stream; - try { - stream = yield this.client.getMediaHandler().getUserMediaStream(true, this.type === GroupCallType.Video); - } - catch (error) { - // If is allowed to join a call without a media stream, then we - // don't throw an error here. But we need an empty Local Feed to establish - // a connection later. - if (this.allowCallWithoutVideoAndAudio) { - stream = new MediaStream(); - } - else { - this.state = GroupCallState.LocalCallFeedUninitialized; - throw error; - } - } - // The call could've been disposed while we were waiting, and could - // also have been started back up again (hello, React 18) so if we're - // still in this 'initializing' state, carry on, otherwise bail. - if (this._state !== GroupCallState.InitializingLocalCallFeed) { - this.client.getMediaHandler().stopUserMediaStream(stream); - throw new Error("Group call disposed while gathering media stream"); - } - const callFeed = new callFeed_1.CallFeed({ - client: this.client, - roomId: this.room.roomId, - userId: this.client.getUserId(), - deviceId: this.client.getDeviceId(), - stream, - purpose: callEventTypes_1.SDPStreamMetadataPurpose.Usermedia, - audioMuted: this.initWithAudioMuted || stream.getAudioTracks().length === 0 || this.isPtt, - videoMuted: this.initWithVideoMuted || stream.getVideoTracks().length === 0, - }); - (0, call_1.setTracksEnabled)(stream.getAudioTracks(), !callFeed.isAudioMuted()); - (0, call_1.setTracksEnabled)(stream.getVideoTracks(), !callFeed.isVideoMuted()); - this.localCallFeed = callFeed; - this.addUserMediaFeed(callFeed); - this.state = GroupCallState.LocalCallFeedInitialized; - }); - } - updateLocalUsermediaStream(stream) { - return __awaiter(this, void 0, void 0, function* () { - if (this.localCallFeed) { - const oldStream = this.localCallFeed.stream; - this.localCallFeed.setNewStream(stream); - const micShouldBeMuted = this.localCallFeed.isAudioMuted(); - const vidShouldBeMuted = this.localCallFeed.isVideoMuted(); - logger_1.logger.log(`GroupCall ${this.groupCallId} updateLocalUsermediaStream() (oldStreamId=${oldStream.id}, newStreamId=${stream.id}, micShouldBeMuted=${micShouldBeMuted}, vidShouldBeMuted=${vidShouldBeMuted})`); - (0, call_1.setTracksEnabled)(stream.getAudioTracks(), !micShouldBeMuted); - (0, call_1.setTracksEnabled)(stream.getVideoTracks(), !vidShouldBeMuted); - this.client.getMediaHandler().stopUserMediaStream(oldStream); - } - }); - } - enter() { - return __awaiter(this, void 0, void 0, function* () { - if (this.state === GroupCallState.LocalCallFeedUninitialized) { - yield this.initLocalCallFeed(); - } - else if (this.state !== GroupCallState.LocalCallFeedInitialized) { - throw new Error(`Cannot enter call in the "${this.state}" state`); - } - logger_1.logger.log(`GroupCall ${this.groupCallId} enter() running`); - this.state = GroupCallState.Entered; - this.client.on(callEventHandler_1.CallEventHandlerEvent.Incoming, this.onIncomingCall); - for (const call of this.client.callEventHandler.calls.values()) { - this.onIncomingCall(call); - } - this.retryCallLoopInterval = setInterval(this.onRetryCallLoop, this.retryCallInterval); - this.activeSpeaker = undefined; - this.onActiveSpeakerLoop(); - this.activeSpeakerLoopInterval = setInterval(this.onActiveSpeakerLoop, this.activeSpeakerInterval); - }); - } - dispose() { - if (this.localCallFeed) { - this.removeUserMediaFeed(this.localCallFeed); - this.localCallFeed = undefined; - } - if (this.localScreenshareFeed) { - this.client.getMediaHandler().stopScreensharingStream(this.localScreenshareFeed.stream); - this.removeScreenshareFeed(this.localScreenshareFeed); - this.localScreenshareFeed = undefined; - this.localDesktopCapturerSourceId = undefined; - } - this.client.getMediaHandler().stopAllStreams(); - if (this.transmitTimer !== null) { - clearTimeout(this.transmitTimer); - this.transmitTimer = null; - } - if (this.retryCallLoopInterval !== undefined) { - clearInterval(this.retryCallLoopInterval); - this.retryCallLoopInterval = undefined; - } - if (this.participantsExpirationTimer !== null) { - clearTimeout(this.participantsExpirationTimer); - this.participantsExpirationTimer = null; - } - if (this.state !== GroupCallState.Entered) { - return; - } - this.forEachCall((call) => call.hangup(call_1.CallErrorCode.UserHangup, false)); - this.activeSpeaker = undefined; - clearInterval(this.activeSpeakerLoopInterval); - this.retryCallCounts.clear(); - clearInterval(this.retryCallLoopInterval); - this.client.removeListener(callEventHandler_1.CallEventHandlerEvent.Incoming, this.onIncomingCall); - this.stats.stop(); - } - leave() { - this.dispose(); - this.state = GroupCallState.LocalCallFeedUninitialized; - } - terminate(emitStateEvent = true) { - return __awaiter(this, void 0, void 0, function* () { - this.dispose(); - this.room.off(room_state_1.RoomStateEvent.Update, this.onRoomState); - this.client.groupCallEventHandler.groupCalls.delete(this.room.roomId); - this.client.emit(groupCallEventHandler_1.GroupCallEventHandlerEvent.Ended, this); - this.state = GroupCallState.Ended; - if (emitStateEvent) { - const existingStateEvent = this.room.currentState.getStateEvents(event_1.EventType.GroupCallPrefix, this.groupCallId); - yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallPrefix, Object.assign(Object.assign({}, existingStateEvent.getContent()), { "m.terminated": GroupCallTerminationReason.CallEnded }), this.groupCallId); - } - }); - } - /* - * Local Usermedia - */ - isLocalVideoMuted() { - if (this.localCallFeed) { - return this.localCallFeed.isVideoMuted(); - } - return true; - } - isMicrophoneMuted() { - if (this.localCallFeed) { - return this.localCallFeed.isAudioMuted(); - } - return true; - } - /** - * Sets the mute state of the local participants's microphone. - * @param muted - Whether to mute the microphone - * @returns Whether muting/unmuting was successful - */ - setMicrophoneMuted(muted) { - return __awaiter(this, void 0, void 0, function* () { - // hasAudioDevice can block indefinitely if the window has lost focus, - // and it doesn't make much sense to keep a device from being muted, so - // we always allow muted = true changes to go through - if (!muted && !(yield this.client.getMediaHandler().hasAudioDevice())) { - return false; - } - const sendUpdatesBefore = !muted && this.isPtt; - // set a timer for the maximum transmit time on PTT calls - if (this.isPtt) { - // Set or clear the max transmit timer - if (!muted && this.isMicrophoneMuted()) { - this.transmitTimer = setTimeout(() => { - this.setMicrophoneMuted(true); - }, this.pttMaxTransmitTime); - } - else if (muted && !this.isMicrophoneMuted()) { - if (this.transmitTimer !== null) - clearTimeout(this.transmitTimer); - this.transmitTimer = null; - } - } - this.forEachCall((call) => { var _a; return (_a = call.localUsermediaFeed) === null || _a === void 0 ? void 0 : _a.setAudioVideoMuted(muted, null); }); - const sendUpdates = () => __awaiter(this, void 0, void 0, function* () { - const updates = []; - this.forEachCall((call) => updates.push(call.sendMetadataUpdate())); - yield Promise.all(updates).catch((e) => logger_1.logger.info(`GroupCall ${this.groupCallId} setMicrophoneMuted() failed to send some metadata updates`, e)); - }); - if (sendUpdatesBefore) - yield sendUpdates(); - if (this.localCallFeed) { - logger_1.logger.log(`GroupCall ${this.groupCallId} setMicrophoneMuted() (streamId=${this.localCallFeed.stream.id}, muted=${muted})`); - // We needed this here to avoid an error in case user join a call without a device. - // I can not use .then .catch functions because linter :-( - try { - if (!muted) { - const stream = yield this.client - .getMediaHandler() - .getUserMediaStream(true, !this.localCallFeed.isVideoMuted()); - if (stream === null) { - // if case permission denied to get a stream stop this here - /* istanbul ignore next */ - logger_1.logger.log(`GroupCall ${this.groupCallId} setMicrophoneMuted() no device to receive local stream, muted=${muted}`); - return false; - } - } - } - catch (e) { - /* istanbul ignore next */ - logger_1.logger.log(`GroupCall ${this.groupCallId} setMicrophoneMuted() no device or permission to receive local stream, muted=${muted}`); - return false; - } - this.localCallFeed.setAudioVideoMuted(muted, null); - // I don't believe its actually necessary to enable these tracks: they - // are the one on the GroupCall's own CallFeed and are cloned before being - // given to any of the actual calls, so these tracks don't actually go - // anywhere. Let's do it anyway to avoid confusion. - (0, call_1.setTracksEnabled)(this.localCallFeed.stream.getAudioTracks(), !muted); - } - else { - logger_1.logger.log(`GroupCall ${this.groupCallId} setMicrophoneMuted() no stream muted (muted=${muted})`); - this.initWithAudioMuted = muted; - } - this.forEachCall((call) => (0, call_1.setTracksEnabled)(call.localUsermediaFeed.stream.getAudioTracks(), !muted && this.callExpected(call))); - this.emit(GroupCallEvent.LocalMuteStateChanged, muted, this.isLocalVideoMuted()); - if (!sendUpdatesBefore) - yield sendUpdates(); - return true; - }); - } - /** - * Sets the mute state of the local participants's video. - * @param muted - Whether to mute the video - * @returns Whether muting/unmuting was successful - */ - setLocalVideoMuted(muted) { - return __awaiter(this, void 0, void 0, function* () { - // hasAudioDevice can block indefinitely if the window has lost focus, - // and it doesn't make much sense to keep a device from being muted, so - // we always allow muted = true changes to go through - if (!muted && !(yield this.client.getMediaHandler().hasVideoDevice())) { - return false; - } - if (this.localCallFeed) { - /* istanbul ignore next */ - logger_1.logger.log(`GroupCall ${this.groupCallId} setLocalVideoMuted() (stream=${this.localCallFeed.stream.id}, muted=${muted})`); - try { - const stream = yield this.client.getMediaHandler().getUserMediaStream(true, !muted); - yield this.updateLocalUsermediaStream(stream); - this.localCallFeed.setAudioVideoMuted(null, muted); - (0, call_1.setTracksEnabled)(this.localCallFeed.stream.getVideoTracks(), !muted); - } - catch (_) { - // No permission to video device - /* istanbul ignore next */ - logger_1.logger.log(`GroupCall ${this.groupCallId} setLocalVideoMuted() no device or permission to receive local stream, muted=${muted}`); - return false; - } - } - else { - logger_1.logger.log(`GroupCall ${this.groupCallId} setLocalVideoMuted() no stream muted (muted=${muted})`); - this.initWithVideoMuted = muted; - } - const updates = []; - this.forEachCall((call) => updates.push(call.setLocalVideoMuted(muted))); - yield Promise.all(updates); - // We setTracksEnabled again, independently from the call doing it - // internally, since we might not be expecting the call - this.forEachCall((call) => (0, call_1.setTracksEnabled)(call.localUsermediaFeed.stream.getVideoTracks(), !muted && this.callExpected(call))); - this.emit(GroupCallEvent.LocalMuteStateChanged, this.isMicrophoneMuted(), muted); - return true; - }); - } - setScreensharingEnabled(enabled, opts = {}) { - return __awaiter(this, void 0, void 0, function* () { - if (enabled === this.isScreensharing()) { - return enabled; - } - if (enabled) { - try { - logger_1.logger.log(`GroupCall ${this.groupCallId} setScreensharingEnabled() is asking for screensharing permissions`); - const stream = yield this.client.getMediaHandler().getScreensharingStream(opts); - for (const track of stream.getTracks()) { - const onTrackEnded = () => { - this.setScreensharingEnabled(false); - track.removeEventListener("ended", onTrackEnded); - }; - track.addEventListener("ended", onTrackEnded); - } - logger_1.logger.log(`GroupCall ${this.groupCallId} setScreensharingEnabled() granted screensharing permissions. Setting screensharing enabled on all calls`); - this.localDesktopCapturerSourceId = opts.desktopCapturerSourceId; - this.localScreenshareFeed = new callFeed_1.CallFeed({ - client: this.client, - roomId: this.room.roomId, - userId: this.client.getUserId(), - deviceId: this.client.getDeviceId(), - stream, - purpose: callEventTypes_1.SDPStreamMetadataPurpose.Screenshare, - audioMuted: false, - videoMuted: false, - }); - this.addScreenshareFeed(this.localScreenshareFeed); - this.emit(GroupCallEvent.LocalScreenshareStateChanged, true, this.localScreenshareFeed, this.localDesktopCapturerSourceId); - // TODO: handle errors - this.forEachCall((call) => call.pushLocalFeed(this.localScreenshareFeed.clone())); - return true; - } - catch (error) { - if (opts.throwOnFail) - throw error; - logger_1.logger.error(`GroupCall ${this.groupCallId} setScreensharingEnabled() enabling screensharing error`, error); - this.emit(GroupCallEvent.Error, new GroupCallError(GroupCallErrorCode.NoUserMedia, "Failed to get screen-sharing stream: ", error)); - return false; - } - } - else { - this.forEachCall((call) => { - if (call.localScreensharingFeed) - call.removeLocalFeed(call.localScreensharingFeed); - }); - this.client.getMediaHandler().stopScreensharingStream(this.localScreenshareFeed.stream); - this.removeScreenshareFeed(this.localScreenshareFeed); - this.localScreenshareFeed = undefined; - this.localDesktopCapturerSourceId = undefined; - this.emit(GroupCallEvent.LocalScreenshareStateChanged, false, undefined, undefined); - return false; - } - }); - } - isScreensharing() { - return !!this.localScreenshareFeed; - } - /** - * Determines whether a given participant expects us to call them (versus - * them calling us). - * @param userId - The participant's user ID. - * @param deviceId - The participant's device ID. - * @returns Whether we need to place an outgoing call to the participant. - */ - wantsOutgoingCall(userId, deviceId) { - const localUserId = this.client.getUserId(); - const localDeviceId = this.client.getDeviceId(); - return ( - // If a user's ID is less than our own, they'll call us - userId >= localUserId && - // If this is another one of our devices, compare device IDs to tell whether it'll call us - (userId !== localUserId || deviceId > localDeviceId)); - } - /** - * Places calls to all participants that we're responsible for calling. - */ - placeOutgoingCalls() { - var _a; - let callsChanged = false; - for (const [{ userId }, participantMap] of this.participants) { - const callMap = (_a = this.calls.get(userId)) !== null && _a !== void 0 ? _a : new Map(); - for (const [deviceId, participant] of participantMap) { - const prevCall = callMap.get(deviceId); - if ((prevCall === null || prevCall === void 0 ? void 0 : prevCall.getOpponentSessionId()) !== participant.sessionId && - this.wantsOutgoingCall(userId, deviceId)) { - callsChanged = true; - if (prevCall !== undefined) { - logger_1.logger.debug(`GroupCall ${this.groupCallId} placeOutgoingCalls() replacing call (userId=${userId}, deviceId=${deviceId}, callId=${prevCall.callId})`); - prevCall.hangup(call_1.CallErrorCode.NewSession, false); - } - const newCall = (0, call_1.createNewMatrixCall)(this.client, this.room.roomId, { - invitee: userId, - opponentDeviceId: deviceId, - opponentSessionId: participant.sessionId, - groupCallId: this.groupCallId, - }); - if (newCall === null) { - logger_1.logger.error(`GroupCall ${this.groupCallId} placeOutgoingCalls() failed to create call (userId=${userId}, device=${deviceId})`); - callMap.delete(deviceId); - } - else { - this.initCall(newCall); - callMap.set(deviceId, newCall); - logger_1.logger.debug(`GroupCall ${this.groupCallId} placeOutgoingCalls() placing call (userId=${userId}, deviceId=${deviceId}, sessionId=${participant.sessionId})`); - newCall - .placeCallWithCallFeeds(this.getLocalFeeds().map((feed) => feed.clone()), participant.screensharing) - .then(() => { - if (this.dataChannelsEnabled) { - newCall.createDataChannel("datachannel", this.dataChannelOptions); - } - }) - .catch((e) => { - logger_1.logger.warn(`GroupCall ${this.groupCallId} placeOutgoingCalls() failed to place call (userId=${userId})`, e); - if (e instanceof call_1.CallError && e.code === GroupCallErrorCode.UnknownDevice) { - this.emit(GroupCallEvent.Error, e); - } - else { - this.emit(GroupCallEvent.Error, new GroupCallError(GroupCallErrorCode.PlaceCallFailed, `Failed to place call to ${userId}`)); - } - newCall.hangup(call_1.CallErrorCode.SignallingFailed, false); - if (callMap.get(deviceId) === newCall) - callMap.delete(deviceId); - }); - } - } - } - if (callMap.size > 0) { - this.calls.set(userId, callMap); - } - else { - this.calls.delete(userId); - } - } - if (callsChanged) - this.emit(GroupCallEvent.CallsChanged, this.calls); - } - getMemberStateEvents(userId) { - return userId === undefined - ? this.room.currentState.getStateEvents(event_1.EventType.GroupCallMemberPrefix) - : this.room.currentState.getStateEvents(event_1.EventType.GroupCallMemberPrefix, userId); - } - initCall(call) { - const opponentMemberId = getCallUserId(call); - if (!opponentMemberId) { - throw new Error("Cannot init call without user id"); - } - const onCallFeedsChanged = () => this.onCallFeedsChanged(call); - const onCallStateChanged = (state, oldState) => this.onCallStateChanged(call, state, oldState); - const onCallHangup = this.onCallHangup; - const onCallReplaced = (newCall) => this.onCallReplaced(call, newCall); - let deviceMap = this.callHandlers.get(opponentMemberId); - if (deviceMap === undefined) { - deviceMap = new Map(); - this.callHandlers.set(opponentMemberId, deviceMap); - } - deviceMap.set(call.getOpponentDeviceId(), { - onCallFeedsChanged, - onCallStateChanged, - onCallHangup, - onCallReplaced, - }); - call.on(call_1.CallEvent.FeedsChanged, onCallFeedsChanged); - call.on(call_1.CallEvent.State, onCallStateChanged); - call.on(call_1.CallEvent.Hangup, onCallHangup); - call.on(call_1.CallEvent.Replaced, onCallReplaced); - call.isPtt = this.isPtt; - this.reEmitter.reEmit(call, Object.values(call_1.CallEvent)); - call.initStats(this.stats); - onCallFeedsChanged(); - } - disposeCall(call, hangupReason) { - const opponentMemberId = getCallUserId(call); - const opponentDeviceId = call.getOpponentDeviceId(); - if (!opponentMemberId) { - throw new Error("Cannot dispose call without user id"); - } - const deviceMap = this.callHandlers.get(opponentMemberId); - const { onCallFeedsChanged, onCallStateChanged, onCallHangup, onCallReplaced } = deviceMap.get(opponentDeviceId); - call.removeListener(call_1.CallEvent.FeedsChanged, onCallFeedsChanged); - call.removeListener(call_1.CallEvent.State, onCallStateChanged); - call.removeListener(call_1.CallEvent.Hangup, onCallHangup); - call.removeListener(call_1.CallEvent.Replaced, onCallReplaced); - deviceMap.delete(opponentMemberId); - if (deviceMap.size === 0) - this.callHandlers.delete(opponentMemberId); - if (call.hangupReason === call_1.CallErrorCode.Replaced) { - return; - } - const usermediaFeed = this.getUserMediaFeed(opponentMemberId, opponentDeviceId); - if (usermediaFeed) { - this.removeUserMediaFeed(usermediaFeed); - } - const screenshareFeed = this.getScreenshareFeed(opponentMemberId, opponentDeviceId); - if (screenshareFeed) { - this.removeScreenshareFeed(screenshareFeed); - } - } - /* - * UserMedia CallFeed Event Handlers - */ - getUserMediaFeed(userId, deviceId) { - return this.userMediaFeeds.find((f) => f.userId === userId && f.deviceId === deviceId); - } - addUserMediaFeed(callFeed) { - this.userMediaFeeds.push(callFeed); - callFeed.measureVolumeActivity(true); - this.emit(GroupCallEvent.UserMediaFeedsChanged, this.userMediaFeeds); - } - replaceUserMediaFeed(existingFeed, replacementFeed) { - const feedIndex = this.userMediaFeeds.findIndex((f) => f.userId === existingFeed.userId && f.deviceId === existingFeed.deviceId); - if (feedIndex === -1) { - throw new Error("Couldn't find user media feed to replace"); - } - this.userMediaFeeds.splice(feedIndex, 1, replacementFeed); - existingFeed.dispose(); - replacementFeed.measureVolumeActivity(true); - this.emit(GroupCallEvent.UserMediaFeedsChanged, this.userMediaFeeds); - } - removeUserMediaFeed(callFeed) { - const feedIndex = this.userMediaFeeds.findIndex((f) => f.userId === callFeed.userId && f.deviceId === callFeed.deviceId); - if (feedIndex === -1) { - throw new Error("Couldn't find user media feed to remove"); - } - this.userMediaFeeds.splice(feedIndex, 1); - callFeed.dispose(); - this.emit(GroupCallEvent.UserMediaFeedsChanged, this.userMediaFeeds); - if (this.activeSpeaker === callFeed) { - this.activeSpeaker = this.userMediaFeeds[0]; - this.emit(GroupCallEvent.ActiveSpeakerChanged, this.activeSpeaker); - } - } - /* - * Screenshare Call Feed Event Handlers - */ - getScreenshareFeed(userId, deviceId) { - return this.screenshareFeeds.find((f) => f.userId === userId && f.deviceId === deviceId); - } - addScreenshareFeed(callFeed) { - this.screenshareFeeds.push(callFeed); - this.emit(GroupCallEvent.ScreenshareFeedsChanged, this.screenshareFeeds); - } - replaceScreenshareFeed(existingFeed, replacementFeed) { - const feedIndex = this.screenshareFeeds.findIndex((f) => f.userId === existingFeed.userId && f.deviceId === existingFeed.deviceId); - if (feedIndex === -1) { - throw new Error("Couldn't find screenshare feed to replace"); - } - this.screenshareFeeds.splice(feedIndex, 1, replacementFeed); - existingFeed.dispose(); - this.emit(GroupCallEvent.ScreenshareFeedsChanged, this.screenshareFeeds); - } - removeScreenshareFeed(callFeed) { - const feedIndex = this.screenshareFeeds.findIndex((f) => f.userId === callFeed.userId && f.deviceId === callFeed.deviceId); - if (feedIndex === -1) { - throw new Error("Couldn't find screenshare feed to remove"); - } - this.screenshareFeeds.splice(feedIndex, 1); - callFeed.dispose(); - this.emit(GroupCallEvent.ScreenshareFeedsChanged, this.screenshareFeeds); - } - /** - * Recalculates and updates the participant map to match the room state. - */ - updateParticipants() { - const localMember = this.room.getMember(this.client.getUserId()); - if (!localMember) { - // The client hasn't fetched enough of the room state to get our own member - // event. This probably shouldn't happen, but sanity check & exit for now. - logger_1.logger.warn(`GroupCall ${this.groupCallId} updateParticipants() tried to update participants before local room member is available`); - return; - } - if (this.participantsExpirationTimer !== null) { - clearTimeout(this.participantsExpirationTimer); - this.participantsExpirationTimer = null; - } - if (this.state === GroupCallState.Ended) { - this.participants = new Map(); - return; - } - const participants = new Map(); - const now = Date.now(); - const entered = this.state === GroupCallState.Entered || this.enteredViaAnotherSession; - let nextExpiration = Infinity; - for (const e of this.getMemberStateEvents()) { - const member = this.room.getMember(e.getStateKey()); - const content = e.getContent(); - const calls = Array.isArray(content["m.calls"]) ? content["m.calls"] : []; - const call = calls.find((call) => call["m.call_id"] === this.groupCallId); - const devices = Array.isArray(call === null || call === void 0 ? void 0 : call["m.devices"]) ? call["m.devices"] : []; - // Filter out invalid and expired devices - let validDevices = devices.filter((d) => typeof d.device_id === "string" && - typeof d.session_id === "string" && - typeof d.expires_ts === "number" && - d.expires_ts > now && - Array.isArray(d.feeds)); - // Apply local echo for the unentered case - if (!entered && (member === null || member === void 0 ? void 0 : member.userId) === this.client.getUserId()) { - validDevices = validDevices.filter((d) => d.device_id !== this.client.getDeviceId()); - } - // Must have a connected device and be joined to the room - if (validDevices.length > 0 && (member === null || member === void 0 ? void 0 : member.membership) === "join") { - const deviceMap = new Map(); - participants.set(member, deviceMap); - for (const d of validDevices) { - deviceMap.set(d.device_id, { - sessionId: d.session_id, - screensharing: d.feeds.some((f) => f.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare), - }); - if (d.expires_ts < nextExpiration) - nextExpiration = d.expires_ts; - } - } - } - // Apply local echo for the entered case - if (entered) { - let deviceMap = participants.get(localMember); - if (deviceMap === undefined) { - deviceMap = new Map(); - participants.set(localMember, deviceMap); - } - if (!deviceMap.has(this.client.getDeviceId())) { - deviceMap.set(this.client.getDeviceId(), { - sessionId: this.client.getSessionId(), - screensharing: this.getLocalFeeds().some((f) => f.purpose === callEventTypes_1.SDPStreamMetadataPurpose.Screenshare), - }); - } - } - this.participants = participants; - if (nextExpiration < Infinity) { - this.participantsExpirationTimer = setTimeout(() => this.updateParticipants(), nextExpiration - now); - } - } - /** - * Updates the local user's member state with the devices returned by the given function. - * @param fn - A function from the current devices to the new devices. If it - * returns null, the update will be skipped. - * @param keepAlive - Whether the request should outlive the window. - */ - updateDevices(fn, keepAlive = false) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const now = Date.now(); - const localUserId = this.client.getUserId(); - const event = this.getMemberStateEvents(localUserId); - const content = (_a = event === null || event === void 0 ? void 0 : event.getContent()) !== null && _a !== void 0 ? _a : {}; - const calls = Array.isArray(content["m.calls"]) ? content["m.calls"] : []; - let call = null; - const otherCalls = []; - for (const c of calls) { - if (c["m.call_id"] === this.groupCallId) { - call = c; - } - else { - otherCalls.push(c); - } - } - if (call === null) - call = {}; - const devices = Array.isArray(call["m.devices"]) ? call["m.devices"] : []; - // Filter out invalid and expired devices - const validDevices = devices.filter((d) => typeof d.device_id === "string" && - typeof d.session_id === "string" && - typeof d.expires_ts === "number" && - d.expires_ts > now && - Array.isArray(d.feeds)); - const newDevices = fn(validDevices); - if (newDevices === null) - return; - const newCalls = [...otherCalls]; - if (newDevices.length > 0) { - newCalls.push(Object.assign(Object.assign({}, call), { "m.call_id": this.groupCallId, "m.devices": newDevices })); - } - const newContent = { "m.calls": newCalls }; - yield this.client.sendStateEvent(this.room.roomId, event_1.EventType.GroupCallMemberPrefix, newContent, localUserId, { - keepAlive, - }); - }); - } - addDeviceToMemberState() { - return __awaiter(this, void 0, void 0, function* () { - yield this.updateDevices((devices) => [ - ...devices.filter((d) => d.device_id !== this.client.getDeviceId()), - { - device_id: this.client.getDeviceId(), - session_id: this.client.getSessionId(), - expires_ts: Date.now() + DEVICE_TIMEOUT, - feeds: this.getLocalFeeds().map((feed) => ({ purpose: feed.purpose })), - // TODO: Add data channels - }, - ]); - }); - } - updateMemberState() { - return __awaiter(this, void 0, void 0, function* () { - // Clear the old update interval before proceeding - if (this.resendMemberStateTimer !== null) { - clearInterval(this.resendMemberStateTimer); - this.resendMemberStateTimer = null; - } - if (this.state === GroupCallState.Entered) { - // Add the local device - yield this.addDeviceToMemberState(); - // Resend the state event every so often so it doesn't become stale - this.resendMemberStateTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`GroupCall ${this.groupCallId} updateMemberState() resending call member state"`); - try { - yield this.addDeviceToMemberState(); - } - catch (e) { - logger_1.logger.error(`GroupCall ${this.groupCallId} updateMemberState() failed to resend call member state`, e); - } - }), (DEVICE_TIMEOUT * 3) / 4); - } - else { - // Remove the local device - yield this.updateDevices((devices) => devices.filter((d) => d.device_id !== this.client.getDeviceId()), true); - } - }); - } - /** - * Cleans up our member state by filtering out logged out devices, inactive - * devices, and our own device (if we know we haven't entered). - */ - cleanMemberState() { - return __awaiter(this, void 0, void 0, function* () { - const { devices: myDevices } = yield this.client.getDevices(); - const deviceMap = new Map(myDevices.map((d) => [d.device_id, d])); - // updateDevices takes care of filtering out inactive devices for us - yield this.updateDevices((devices) => { - const newDevices = devices.filter((d) => { - const device = deviceMap.get(d.device_id); - return ((device === null || device === void 0 ? void 0 : device.last_seen_ts) !== undefined && - !(d.device_id === this.client.getDeviceId() && - this.state !== GroupCallState.Entered && - !this.enteredViaAnotherSession)); - }); - // Skip the update if the devices are unchanged - return newDevices.length === devices.length ? null : newDevices; - }); - }); - } - getGroupCallStats() { - return this.stats; - } -} -exports.GroupCall = GroupCall; - -},{"../@types/event":306,"../ReEmitter":317,"../logger":374,"../models/room-state":390,"../models/typed-event-emitter":395,"../utils":416,"./call":418,"./callEventHandler":419,"./callEventTypes":420,"./callFeed":421,"./groupCallEventHandler":423,"./stats/groupCallStats":427,"./stats/statsReport":432}],423:[function(require,module,exports){ -"use strict"; -/* -Copyright 2021 Šimon Brandner - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GroupCallEventHandler = exports.GroupCallEventHandlerEvent = void 0; -const client_1 = require("../client"); -const groupCall_1 = require("./groupCall"); -const room_state_1 = require("../models/room-state"); -const logger_1 = require("../logger"); -const event_1 = require("../@types/event"); -const sync_1 = require("../sync"); -var GroupCallEventHandlerEvent; -(function (GroupCallEventHandlerEvent) { - GroupCallEventHandlerEvent["Incoming"] = "GroupCall.incoming"; - GroupCallEventHandlerEvent["Outgoing"] = "GroupCall.outgoing"; - GroupCallEventHandlerEvent["Ended"] = "GroupCall.ended"; - GroupCallEventHandlerEvent["Participants"] = "GroupCall.participants"; -})(GroupCallEventHandlerEvent = exports.GroupCallEventHandlerEvent || (exports.GroupCallEventHandlerEvent = {})); -class GroupCallEventHandler { - constructor(client) { - this.client = client; - this.groupCalls = new Map(); // roomId -> GroupCall - // All rooms we know about and whether we've seen a 'Room' event - // for them. The promise will be fulfilled once we've processed that - // event which means we're "up to date" on what calls are in a room - // and get - this.roomDeferreds = new Map(); - this.onRoomsChanged = (room) => { - this.createGroupCallForRoom(room); - }; - this.onRoomStateChanged = (event, state) => { - const eventType = event.getType(); - if (eventType === event_1.EventType.GroupCallPrefix) { - const groupCallId = event.getStateKey(); - const content = event.getContent(); - const currentGroupCall = this.groupCalls.get(state.roomId); - if (!currentGroupCall && !content["m.terminated"] && !event.isRedacted()) { - this.createGroupCallFromRoomStateEvent(event); - } - else if (currentGroupCall && currentGroupCall.groupCallId === groupCallId) { - if (content["m.terminated"] || event.isRedacted()) { - currentGroupCall.terminate(false); - } - else if (content["m.type"] !== currentGroupCall.type) { - // TODO: Handle the callType changing when the room state changes - logger_1.logger.warn(`GroupCallEventHandler onRoomStateChanged() currently does not support changing type (roomId=${state.roomId})`); - } - } - else if (currentGroupCall && currentGroupCall.groupCallId !== groupCallId) { - // TODO: Handle new group calls and multiple group calls - logger_1.logger.warn(`GroupCallEventHandler onRoomStateChanged() currently does not support multiple calls (roomId=${state.roomId})`); - } - } - }; - } - start() { - return __awaiter(this, void 0, void 0, function* () { - // We wait until the client has started syncing for real. - // This is because we only support one call at a time, and want - // the latest. We therefore want the latest state of the room before - // we create a group call for the room so we can be fairly sure that - // the group call we create is really the latest one. - if (this.client.getSyncState() !== sync_1.SyncState.Syncing) { - logger_1.logger.debug("GroupCallEventHandler start() waiting for client to start syncing"); - yield new Promise((resolve) => { - const onSync = () => { - if (this.client.getSyncState() === sync_1.SyncState.Syncing) { - this.client.off(client_1.ClientEvent.Sync, onSync); - return resolve(); - } - }; - this.client.on(client_1.ClientEvent.Sync, onSync); - }); - } - const rooms = this.client.getRooms(); - for (const room of rooms) { - this.createGroupCallForRoom(room); - } - this.client.on(client_1.ClientEvent.Room, this.onRoomsChanged); - this.client.on(room_state_1.RoomStateEvent.Events, this.onRoomStateChanged); - }); - } - stop() { - this.client.removeListener(room_state_1.RoomStateEvent.Events, this.onRoomStateChanged); - } - getRoomDeferred(roomId) { - let deferred = this.roomDeferreds.get(roomId); - if (deferred === undefined) { - let resolveFunc; - deferred = { - prom: new Promise((resolve) => { - resolveFunc = resolve; - }), - }; - deferred.resolve = resolveFunc; - this.roomDeferreds.set(roomId, deferred); - } - return deferred; - } - waitUntilRoomReadyForGroupCalls(roomId) { - return this.getRoomDeferred(roomId).prom; - } - getGroupCallById(groupCallId) { - return [...this.groupCalls.values()].find((groupCall) => groupCall.groupCallId === groupCallId); - } - createGroupCallForRoom(room) { - const callEvents = room.currentState.getStateEvents(event_1.EventType.GroupCallPrefix); - const sortedCallEvents = callEvents.sort((a, b) => b.getTs() - a.getTs()); - for (const callEvent of sortedCallEvents) { - const content = callEvent.getContent(); - if (content["m.terminated"] || callEvent.isRedacted()) { - continue; - } - logger_1.logger.debug(`GroupCallEventHandler createGroupCallForRoom() choosing group call from possible calls (stateKey=${callEvent.getStateKey()}, ts=${callEvent.getTs()}, roomId=${room.roomId}, numOfPossibleCalls=${callEvents.length})`); - this.createGroupCallFromRoomStateEvent(callEvent); - break; - } - logger_1.logger.info(`GroupCallEventHandler createGroupCallForRoom() processed room (roomId=${room.roomId})`); - this.getRoomDeferred(room.roomId).resolve(); - } - createGroupCallFromRoomStateEvent(event) { - const roomId = event.getRoomId(); - const content = event.getContent(); - const room = this.client.getRoom(roomId); - if (!room) { - logger_1.logger.warn(`GroupCallEventHandler createGroupCallFromRoomStateEvent() couldn't find room for call (roomId=${roomId})`); - return; - } - const groupCallId = event.getStateKey(); - const callType = content["m.type"]; - if (!Object.values(groupCall_1.GroupCallType).includes(callType)) { - logger_1.logger.warn(`GroupCallEventHandler createGroupCallFromRoomStateEvent() received invalid call type (type=${callType}, roomId=${roomId})`); - return; - } - const callIntent = content["m.intent"]; - if (!Object.values(groupCall_1.GroupCallIntent).includes(callIntent)) { - logger_1.logger.warn(`Received invalid group call intent (type=${callType}, roomId=${roomId})`); - return; - } - const isPtt = Boolean(content["io.element.ptt"]); - let dataChannelOptions; - if ((content === null || content === void 0 ? void 0 : content.dataChannelsEnabled) && (content === null || content === void 0 ? void 0 : content.dataChannelOptions)) { - // Pull out just the dataChannelOptions we want to support. - const { ordered, maxPacketLifeTime, maxRetransmits, protocol } = content.dataChannelOptions; - dataChannelOptions = { ordered, maxPacketLifeTime, maxRetransmits, protocol }; - } - const groupCall = new groupCall_1.GroupCall(this.client, room, callType, isPtt, callIntent, groupCallId, - // Because without Media section a WebRTC connection is not possible, so need a RTCDataChannel to set up a - // no media WebRTC connection anyway. - (content === null || content === void 0 ? void 0 : content.dataChannelsEnabled) || this.client.isVoipWithNoMediaAllowed, dataChannelOptions, this.client.isVoipWithNoMediaAllowed); - this.groupCalls.set(room.roomId, groupCall); - this.client.emit(GroupCallEventHandlerEvent.Incoming, groupCall); - return groupCall; - } -} -exports.GroupCallEventHandler = GroupCallEventHandler; - -},{"../@types/event":306,"../client":321,"../logger":374,"../models/room-state":390,"../sync":414,"./groupCall":422}],424:[function(require,module,exports){ -"use strict"; -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 New Vector Ltd -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. -Copyright 2021 - 2022 Šimon Brandner - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MediaHandler = exports.MediaHandlerEvent = void 0; -const typed_event_emitter_1 = require("../models/typed-event-emitter"); -const groupCall_1 = require("../webrtc/groupCall"); -const logger_1 = require("../logger"); -var MediaHandlerEvent; -(function (MediaHandlerEvent) { - MediaHandlerEvent["LocalStreamsChanged"] = "local_streams_changed"; -})(MediaHandlerEvent = exports.MediaHandlerEvent || (exports.MediaHandlerEvent = {})); -class MediaHandler extends typed_event_emitter_1.TypedEventEmitter { - constructor(client) { - super(); - this.client = client; - this.userMediaStreams = []; - this.screensharingStreams = []; - } - restoreMediaSettings(audioInput, videoInput) { - this.audioInput = audioInput; - this.videoInput = videoInput; - } - /** - * Set an audio input device to use for MatrixCalls - * @param deviceId - the identifier for the device - * undefined treated as unset - */ - setAudioInput(deviceId) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info(`MediaHandler setAudioInput() running (deviceId=${deviceId})`); - if (this.audioInput === deviceId) - return; - this.audioInput = deviceId; - yield this.updateLocalUsermediaStreams(); - }); - } - /** - * Set audio settings for MatrixCalls - * @param opts - audio options to set - */ - setAudioSettings(opts) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info(`MediaHandler setAudioSettings() running (opts=${JSON.stringify(opts)})`); - this.audioSettings = Object.assign({}, opts); - yield this.updateLocalUsermediaStreams(); - }); - } - /** - * Set a video input device to use for MatrixCalls - * @param deviceId - the identifier for the device - * undefined treated as unset - */ - setVideoInput(deviceId) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.info(`MediaHandler setVideoInput() running (deviceId=${deviceId})`); - if (this.videoInput === deviceId) - return; - this.videoInput = deviceId; - yield this.updateLocalUsermediaStreams(); - }); - } - /** - * Set media input devices to use for MatrixCalls - * @param audioInput - the identifier for the audio device - * @param videoInput - the identifier for the video device - * undefined treated as unset - */ - setMediaInputs(audioInput, videoInput) { - return __awaiter(this, void 0, void 0, function* () { - logger_1.logger.log(`MediaHandler setMediaInputs() running (audioInput: ${audioInput} videoInput: ${videoInput})`); - this.audioInput = audioInput; - this.videoInput = videoInput; - yield this.updateLocalUsermediaStreams(); - }); - } - /* - * Requests new usermedia streams and replace the old ones - */ - updateLocalUsermediaStreams() { - return __awaiter(this, void 0, void 0, function* () { - if (this.userMediaStreams.length === 0) - return; - const callMediaStreamParams = new Map(); - for (const call of this.client.callEventHandler.calls.values()) { - callMediaStreamParams.set(call.callId, { - audio: call.hasLocalUserMediaAudioTrack, - video: call.hasLocalUserMediaVideoTrack, - }); - } - for (const stream of this.userMediaStreams) { - logger_1.logger.log(`MediaHandler updateLocalUsermediaStreams() stopping all tracks (streamId=${stream.id})`); - for (const track of stream.getTracks()) { - track.stop(); - } - } - this.userMediaStreams = []; - this.localUserMediaStream = undefined; - for (const call of this.client.callEventHandler.calls.values()) { - if (call.callHasEnded() || !callMediaStreamParams.has(call.callId)) { - continue; - } - const { audio, video } = callMediaStreamParams.get(call.callId); - logger_1.logger.log(`MediaHandler updateLocalUsermediaStreams() calling getUserMediaStream() (callId=${call.callId})`); - const stream = yield this.getUserMediaStream(audio, video); - if (call.callHasEnded()) { - continue; - } - yield call.updateLocalUsermediaStream(stream); - } - for (const groupCall of this.client.groupCallEventHandler.groupCalls.values()) { - if (!groupCall.localCallFeed) { - continue; - } - logger_1.logger.log(`MediaHandler updateLocalUsermediaStreams() calling getUserMediaStream() (groupCallId=${groupCall.groupCallId})`); - const stream = yield this.getUserMediaStream(true, groupCall.type === groupCall_1.GroupCallType.Video); - if (groupCall.state === groupCall_1.GroupCallState.Ended) { - continue; - } - yield groupCall.updateLocalUsermediaStream(stream); - } - this.emit(MediaHandlerEvent.LocalStreamsChanged); - }); - } - hasAudioDevice() { - return __awaiter(this, void 0, void 0, function* () { - try { - const devices = yield navigator.mediaDevices.enumerateDevices(); - return devices.filter((device) => device.kind === "audioinput").length > 0; - } - catch (err) { - logger_1.logger.log(`MediaHandler hasAudioDevice() calling navigator.mediaDevices.enumerateDevices with error`, err); - return false; - } - }); - } - hasVideoDevice() { - return __awaiter(this, void 0, void 0, function* () { - try { - const devices = yield navigator.mediaDevices.enumerateDevices(); - return devices.filter((device) => device.kind === "videoinput").length > 0; - } - catch (err) { - logger_1.logger.log(`MediaHandler hasVideoDevice() calling navigator.mediaDevices.enumerateDevices with error`, err); - return false; - } - }); - } - /** - * @param audio - should have an audio track - * @param video - should have a video track - * @param reusable - is allowed to be reused by the MediaHandler - * @returns based on passed parameters - */ - getUserMediaStream(audio, video, reusable = true) { - return __awaiter(this, void 0, void 0, function* () { - // Serialise calls, othertwise we can't sensibly re-use the stream - if (this.getMediaStreamPromise) { - this.getMediaStreamPromise = this.getMediaStreamPromise.then(() => { - return this.getUserMediaStreamInternal(audio, video, reusable); - }); - } - else { - this.getMediaStreamPromise = this.getUserMediaStreamInternal(audio, video, reusable); - } - return this.getMediaStreamPromise; - }); - } - getUserMediaStreamInternal(audio, video, reusable) { - var _a, _b, _c, _d, _e; - return __awaiter(this, void 0, void 0, function* () { - const shouldRequestAudio = audio && (yield this.hasAudioDevice()); - const shouldRequestVideo = video && (yield this.hasVideoDevice()); - let stream; - let canReuseStream = true; - if (this.localUserMediaStream) { - // This figures out if we can reuse the current localUsermediaStream - // based on whether or not the "mute state" (presence of tracks of a - // given kind) matches what is being requested - if (shouldRequestAudio !== this.localUserMediaStream.getAudioTracks().length > 0) { - canReuseStream = false; - } - if (shouldRequestVideo !== this.localUserMediaStream.getVideoTracks().length > 0) { - canReuseStream = false; - } - // This code checks that the device ID is the same as the localUserMediaStream stream, but we update - // the localUserMediaStream whenever the device ID changes (apart from when restoring) so it's not - // clear why this would ever be different, unless there's a race. - if (shouldRequestAudio && - ((_b = (_a = this.localUserMediaStream.getAudioTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings()) === null || _b === void 0 ? void 0 : _b.deviceId) !== this.audioInput) { - canReuseStream = false; - } - if (shouldRequestVideo && - ((_d = (_c = this.localUserMediaStream.getVideoTracks()[0]) === null || _c === void 0 ? void 0 : _c.getSettings()) === null || _d === void 0 ? void 0 : _d.deviceId) !== this.videoInput) { - canReuseStream = false; - } - } - else { - canReuseStream = false; - } - if (!canReuseStream) { - const constraints = this.getUserMediaContraints(shouldRequestAudio, shouldRequestVideo); - stream = yield navigator.mediaDevices.getUserMedia(constraints); - logger_1.logger.log(`MediaHandler getUserMediaStreamInternal() calling getUserMediaStream (streamId=${stream.id}, shouldRequestAudio=${shouldRequestAudio}, shouldRequestVideo=${shouldRequestVideo}, constraints=${JSON.stringify(constraints)})`); - for (const track of stream.getTracks()) { - const settings = track.getSettings(); - if (track.kind === "audio") { - this.audioInput = settings.deviceId; - } - else if (track.kind === "video") { - this.videoInput = settings.deviceId; - } - } - if (reusable) { - this.localUserMediaStream = stream; - } - } - else { - stream = this.localUserMediaStream.clone(); - logger_1.logger.log(`MediaHandler getUserMediaStreamInternal() cloning (oldStreamId=${(_e = this.localUserMediaStream) === null || _e === void 0 ? void 0 : _e.id} newStreamId=${stream.id} shouldRequestAudio=${shouldRequestAudio} shouldRequestVideo=${shouldRequestVideo})`); - if (!shouldRequestAudio) { - for (const track of stream.getAudioTracks()) { - stream.removeTrack(track); - } - } - if (!shouldRequestVideo) { - for (const track of stream.getVideoTracks()) { - stream.removeTrack(track); - } - } - } - if (reusable) { - this.userMediaStreams.push(stream); - } - this.emit(MediaHandlerEvent.LocalStreamsChanged); - return stream; - }); - } - /** - * Stops all tracks on the provided usermedia stream - */ - stopUserMediaStream(mediaStream) { - logger_1.logger.log(`MediaHandler stopUserMediaStream() stopping (streamId=${mediaStream.id})`); - for (const track of mediaStream.getTracks()) { - track.stop(); - } - const index = this.userMediaStreams.indexOf(mediaStream); - if (index !== -1) { - logger_1.logger.debug(`MediaHandler stopUserMediaStream() splicing usermedia stream out stream array (streamId=${mediaStream.id})`, mediaStream.id); - this.userMediaStreams.splice(index, 1); - } - this.emit(MediaHandlerEvent.LocalStreamsChanged); - if (this.localUserMediaStream === mediaStream) { - this.localUserMediaStream = undefined; - } - } - /** - * @param desktopCapturerSourceId - sourceId for Electron DesktopCapturer - * @param reusable - is allowed to be reused by the MediaHandler - * @returns based on passed parameters - */ - getScreensharingStream(opts = {}, reusable = true) { - return __awaiter(this, void 0, void 0, function* () { - let stream; - if (this.screensharingStreams.length === 0) { - const screenshareConstraints = this.getScreenshareContraints(opts); - if (opts.desktopCapturerSourceId) { - // We are using Electron - logger_1.logger.debug(`MediaHandler getScreensharingStream() calling getUserMedia() (opts=${JSON.stringify(opts)})`); - stream = yield navigator.mediaDevices.getUserMedia(screenshareConstraints); - } - else { - // We are not using Electron - logger_1.logger.debug(`MediaHandler getScreensharingStream() calling getDisplayMedia() (opts=${JSON.stringify(opts)})`); - stream = yield navigator.mediaDevices.getDisplayMedia(screenshareConstraints); - } - } - else { - const matchingStream = this.screensharingStreams[this.screensharingStreams.length - 1]; - logger_1.logger.log(`MediaHandler getScreensharingStream() cloning (streamId=${matchingStream.id})`); - stream = matchingStream.clone(); - } - if (reusable) { - this.screensharingStreams.push(stream); - } - this.emit(MediaHandlerEvent.LocalStreamsChanged); - return stream; - }); - } - /** - * Stops all tracks on the provided screensharing stream - */ - stopScreensharingStream(mediaStream) { - logger_1.logger.debug(`MediaHandler stopScreensharingStream() stopping stream (streamId=${mediaStream.id})`); - for (const track of mediaStream.getTracks()) { - track.stop(); - } - const index = this.screensharingStreams.indexOf(mediaStream); - if (index !== -1) { - logger_1.logger.debug(`MediaHandler stopScreensharingStream() splicing stream out (streamId=${mediaStream.id})`); - this.screensharingStreams.splice(index, 1); - } - this.emit(MediaHandlerEvent.LocalStreamsChanged); - } - /** - * Stops all local media tracks - */ - stopAllStreams() { - for (const stream of this.userMediaStreams) { - logger_1.logger.log(`MediaHandler stopAllStreams() stopping (streamId=${stream.id})`); - for (const track of stream.getTracks()) { - track.stop(); - } - } - for (const stream of this.screensharingStreams) { - for (const track of stream.getTracks()) { - track.stop(); - } - } - this.userMediaStreams = []; - this.screensharingStreams = []; - this.localUserMediaStream = undefined; - this.emit(MediaHandlerEvent.LocalStreamsChanged); - } - getUserMediaContraints(audio, video) { - const isWebkit = !!navigator.webkitGetUserMedia; - return { - audio: audio - ? { - deviceId: this.audioInput ? { ideal: this.audioInput } : undefined, - autoGainControl: this.audioSettings ? { ideal: this.audioSettings.autoGainControl } : undefined, - echoCancellation: this.audioSettings ? { ideal: this.audioSettings.echoCancellation } : undefined, - noiseSuppression: this.audioSettings ? { ideal: this.audioSettings.noiseSuppression } : undefined, - } - : false, - video: video - ? { - deviceId: this.videoInput ? { ideal: this.videoInput } : undefined, - /* We want 640x360. Chrome will give it only if we ask exactly, - FF refuses entirely if we ask exactly, so have to ask for ideal - instead - XXX: Is this still true? - */ - width: isWebkit ? { exact: 640 } : { ideal: 640 }, - height: isWebkit ? { exact: 360 } : { ideal: 360 }, - } - : false, - }; - } - getScreenshareContraints(opts) { - const { desktopCapturerSourceId, audio } = opts; - if (desktopCapturerSourceId) { - return { - audio: audio !== null && audio !== void 0 ? audio : false, - video: { - mandatory: { - chromeMediaSource: "desktop", - chromeMediaSourceId: desktopCapturerSourceId, - }, - }, - }; - } - else { - return { - audio: audio !== null && audio !== void 0 ? audio : false, - video: true, - }; - } - } -} -exports.MediaHandler = MediaHandler; - -},{"../logger":374,"../models/typed-event-emitter":395,"../webrtc/groupCall":422}],425:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ConnectionStats = void 0; -class ConnectionStats { - constructor() { - this.bandwidth = {}; - this.bitrate = {}; - this.packetLoss = {}; - this.transport = []; - } -} -exports.ConnectionStats = ConnectionStats; - -},{}],426:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ConnectionStatsReporter = void 0; -class ConnectionStatsReporter { - static buildBandwidthReport(now) { - const availableIncomingBitrate = now.availableIncomingBitrate; - const availableOutgoingBitrate = now.availableOutgoingBitrate; - return { - download: availableIncomingBitrate ? Math.round(availableIncomingBitrate / 1000) : 0, - upload: availableOutgoingBitrate ? Math.round(availableOutgoingBitrate / 1000) : 0, - }; - } -} -exports.ConnectionStatsReporter = ConnectionStatsReporter; - -},{}],427:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GroupCallStats = void 0; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -const statsReportGatherer_1 = require("./statsReportGatherer"); -const statsReportEmitter_1 = require("./statsReportEmitter"); -class GroupCallStats { - constructor(groupCallId, userId, interval = 10000) { - this.groupCallId = groupCallId; - this.userId = userId; - this.interval = interval; - this.gatherers = new Map(); - this.reports = new statsReportEmitter_1.StatsReportEmitter(); - } - start() { - if (this.timer === undefined) { - this.timer = setInterval(() => { - this.processStats(); - }, this.interval); - } - } - stop() { - if (this.timer !== undefined) { - clearInterval(this.timer); - this.gatherers.forEach((c) => c.stopProcessingStats()); - } - } - hasStatsReportGatherer(callId) { - return this.gatherers.has(callId); - } - addStatsReportGatherer(callId, userId, peerConnection) { - if (this.hasStatsReportGatherer(callId)) { - return false; - } - this.gatherers.set(callId, new statsReportGatherer_1.StatsReportGatherer(callId, userId, peerConnection, this.reports)); - return true; - } - removeStatsReportGatherer(callId) { - return this.gatherers.delete(callId); - } - getStatsReportGatherer(callId) { - return this.hasStatsReportGatherer(callId) ? this.gatherers.get(callId) : undefined; - } - processStats() { - this.gatherers.forEach((c) => c.processStats(this.groupCallId, this.userId)); - } -} -exports.GroupCallStats = GroupCallStats; - -},{"./statsReportEmitter":434,"./statsReportGatherer":435}],428:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MediaSsrcHandler = void 0; -const sdp_transform_1 = require("sdp-transform"); -class MediaSsrcHandler { - constructor() { - this.ssrcToMid = { local: new Map(), remote: new Map() }; - } - findMidBySsrc(ssrc, type) { - let mid; - this.ssrcToMid[type].forEach((ssrcs, m) => { - if (ssrcs.find((s) => s == ssrc)) { - mid = m; - return; - } - }); - return mid; - } - parse(description, type) { - const sdp = (0, sdp_transform_1.parse)(description); - const ssrcToMid = new Map(); - sdp.media.forEach((m) => { - var _a; - if ((!!m.mid && m.type === "video") || m.type === "audio") { - const ssrcs = []; - (_a = m.ssrcs) === null || _a === void 0 ? void 0 : _a.forEach((ssrc) => { - if (ssrc.attribute === "cname") { - ssrcs.push(`${ssrc.id}`); - } - }); - ssrcToMid.set(`${m.mid}`, ssrcs); - } - }); - this.ssrcToMid[type] = ssrcToMid; - } - getSsrcToMidMap(type) { - return this.ssrcToMid[type]; - } -} -exports.MediaSsrcHandler = MediaSsrcHandler; - -},{"sdp-transform":253}],429:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MediaTrackHandler = void 0; -class MediaTrackHandler { - constructor(pc) { - this.pc = pc; - } - getLocalTracks(kind) { - const isNotNullAndKind = (track) => { - return track !== null && track.kind === kind; - }; - // @ts-ignore The linter don't get it - return this.pc - .getTransceivers() - .filter((t) => t.currentDirection === "sendonly" || t.currentDirection === "sendrecv") - .filter((t) => t.sender !== null) - .map((t) => t.sender) - .map((s) => s.track) - .filter(isNotNullAndKind); - } - getTackById(trackId) { - return this.pc - .getTransceivers() - .map((t) => { - if ((t === null || t === void 0 ? void 0 : t.sender.track) !== null && t.sender.track.id === trackId) { - return t.sender.track; - } - if ((t === null || t === void 0 ? void 0 : t.receiver.track) !== null && t.receiver.track.id === trackId) { - return t.receiver.track; - } - return undefined; - }) - .find((t) => t !== undefined); - } - getLocalTrackIdByMid(mid) { - const transceiver = this.pc.getTransceivers().find((t) => t.mid === mid); - if (transceiver !== undefined && !!transceiver.sender && !!transceiver.sender.track) { - return transceiver.sender.track.id; - } - return undefined; - } - getRemoteTrackIdByMid(mid) { - const transceiver = this.pc.getTransceivers().find((t) => t.mid === mid); - if (transceiver !== undefined && !!transceiver.receiver && !!transceiver.receiver.track) { - return transceiver.receiver.track.id; - } - return undefined; - } - getActiveSimulcastStreams() { - //@TODO implement this right.. Check how many layer configured - return 3; - } -} -exports.MediaTrackHandler = MediaTrackHandler; - -},{}],430:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MediaTrackStats = void 0; -class MediaTrackStats { - constructor(trackId, type, kind) { - this.trackId = trackId; - this.type = type; - this.kind = kind; - this.loss = { packetsTotal: 0, packetsLost: 0, isDownloadStream: false }; - this.bitrate = { download: 0, upload: 0 }; - this.resolution = { width: -1, height: -1 }; - this.framerate = 0; - this.codec = ""; - } - getType() { - return this.type; - } - setLoss(loos) { - this.loss = loos; - } - getLoss() { - return this.loss; - } - setResolution(resolution) { - this.resolution = resolution; - } - getResolution() { - return this.resolution; - } - setFramerate(framerate) { - this.framerate = framerate; - } - getFramerate() { - return this.framerate; - } - setBitrate(bitrate) { - this.bitrate = bitrate; - } - getBitrate() { - return this.bitrate; - } - setCodec(codecShortType) { - this.codec = codecShortType; - return true; - } - getCodec() { - return this.codec; - } - resetBitrate() { - this.bitrate = { download: 0, upload: 0 }; - } -} -exports.MediaTrackStats = MediaTrackStats; - -},{}],431:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MediaTrackStatsHandler = void 0; -const mediaTrackStats_1 = require("./mediaTrackStats"); -class MediaTrackStatsHandler { - constructor(mediaSsrcHandler, mediaTrackHandler) { - this.mediaSsrcHandler = mediaSsrcHandler; - this.mediaTrackHandler = mediaTrackHandler; - this.track2stats = new Map(); - } - /** - * Find tracks by rtc stats - * Argument report is any because the stats api is not consistent: - * For example `trackIdentifier`, `mid` not existing in every implementations - * https://www.w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats - * https://developer.mozilla.org/en-US/docs/Web/API/RTCInboundRtpStreamStats - */ - findTrack2Stats(report, type) { - let trackID; - if (report.trackIdentifier) { - trackID = report.trackIdentifier; - } - else if (report.mid) { - trackID = - type === "remote" - ? this.mediaTrackHandler.getRemoteTrackIdByMid(report.mid) - : this.mediaTrackHandler.getLocalTrackIdByMid(report.mid); - } - else if (report.ssrc) { - const mid = this.mediaSsrcHandler.findMidBySsrc(report.ssrc, type); - if (!mid) { - return undefined; - } - trackID = - type === "remote" - ? this.mediaTrackHandler.getRemoteTrackIdByMid(report.mid) - : this.mediaTrackHandler.getLocalTrackIdByMid(report.mid); - } - if (!trackID) { - return undefined; - } - let trackStats = this.track2stats.get(trackID); - if (!trackStats) { - const track = this.mediaTrackHandler.getTackById(trackID); - if (track !== undefined) { - const kind = track.kind === "audio" ? track.kind : "video"; - trackStats = new mediaTrackStats_1.MediaTrackStats(trackID, type, kind); - this.track2stats.set(trackID, trackStats); - } - else { - return undefined; - } - } - return trackStats; - } - findLocalVideoTrackStats(report) { - const localVideoTracks = this.mediaTrackHandler.getLocalTracks("video"); - if (localVideoTracks.length === 0) { - return undefined; - } - return this.findTrack2Stats(report, "local"); - } - getTrack2stats() { - return this.track2stats; - } -} -exports.MediaTrackStatsHandler = MediaTrackStatsHandler; - -},{"./mediaTrackStats":430}],432:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StatsReport = void 0; -var StatsReport; -(function (StatsReport) { - StatsReport["CONNECTION_STATS"] = "StatsReport.connection_stats"; - StatsReport["BYTE_SENT_STATS"] = "StatsReport.byte_sent_stats"; -})(StatsReport = exports.StatsReport || (exports.StatsReport = {})); - -},{}],433:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StatsReportBuilder = void 0; -class StatsReportBuilder { - static build(stats) { - const report = {}; - // process stats - const totalPackets = { - download: 0, - upload: 0, - }; - const lostPackets = { - download: 0, - upload: 0, - }; - let bitrateDownload = 0; - let bitrateUpload = 0; - const resolutions = { - local: new Map(), - remote: new Map(), - }; - const framerates = { local: new Map(), remote: new Map() }; - const codecs = { local: new Map(), remote: new Map() }; - let audioBitrateDownload = 0; - let audioBitrateUpload = 0; - let videoBitrateDownload = 0; - let videoBitrateUpload = 0; - for (const [trackId, trackStats] of stats) { - // process packet loss stats - const loss = trackStats.getLoss(); - const type = loss.isDownloadStream ? "download" : "upload"; - totalPackets[type] += loss.packetsTotal; - lostPackets[type] += loss.packetsLost; - // process bitrate stats - bitrateDownload += trackStats.getBitrate().download; - bitrateUpload += trackStats.getBitrate().upload; - // collect resolutions and framerates - if (trackStats.kind === "audio") { - audioBitrateDownload += trackStats.getBitrate().download; - audioBitrateUpload += trackStats.getBitrate().upload; - } - else { - videoBitrateDownload += trackStats.getBitrate().download; - videoBitrateUpload += trackStats.getBitrate().upload; - } - resolutions[trackStats.getType()].set(trackId, trackStats.getResolution()); - framerates[trackStats.getType()].set(trackId, trackStats.getFramerate()); - codecs[trackStats.getType()].set(trackId, trackStats.getCodec()); - trackStats.resetBitrate(); - } - report.bitrate = { - upload: bitrateUpload, - download: bitrateDownload, - }; - report.bitrate.audio = { - upload: audioBitrateUpload, - download: audioBitrateDownload, - }; - report.bitrate.video = { - upload: videoBitrateUpload, - download: videoBitrateDownload, - }; - report.packetLoss = { - total: StatsReportBuilder.calculatePacketLoss(lostPackets.download + lostPackets.upload, totalPackets.download + totalPackets.upload), - download: StatsReportBuilder.calculatePacketLoss(lostPackets.download, totalPackets.download), - upload: StatsReportBuilder.calculatePacketLoss(lostPackets.upload, totalPackets.upload), - }; - report.framerate = framerates; - report.resolution = resolutions; - report.codec = codecs; - return report; - } - static calculatePacketLoss(lostPackets, totalPackets) { - if (!totalPackets || totalPackets <= 0 || !lostPackets || lostPackets <= 0) { - return 0; - } - return Math.round((lostPackets / totalPackets) * 100); - } -} -exports.StatsReportBuilder = StatsReportBuilder; - -},{}],434:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StatsReportEmitter = void 0; -const typed_event_emitter_1 = require("../../models/typed-event-emitter"); -const statsReport_1 = require("./statsReport"); -class StatsReportEmitter extends typed_event_emitter_1.TypedEventEmitter { - emitByteSendReport(byteSentStats) { - this.emit(statsReport_1.StatsReport.BYTE_SENT_STATS, byteSentStats); - } - emitConnectionStatsReport(report) { - this.emit(statsReport_1.StatsReport.CONNECTION_STATS, report); - } -} -exports.StatsReportEmitter = StatsReportEmitter; - -},{"../../models/typed-event-emitter":395,"./statsReport":432}],435:[function(require,module,exports){ -"use strict"; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StatsReportGatherer = void 0; -const connectionStats_1 = require("./connectionStats"); -const connectionStatsReporter_1 = require("./connectionStatsReporter"); -const transportStatsReporter_1 = require("./transportStatsReporter"); -const mediaSsrcHandler_1 = require("./media/mediaSsrcHandler"); -const mediaTrackHandler_1 = require("./media/mediaTrackHandler"); -const mediaTrackStatsHandler_1 = require("./media/mediaTrackStatsHandler"); -const trackStatsReporter_1 = require("./trackStatsReporter"); -const statsReportBuilder_1 = require("./statsReportBuilder"); -const statsValueFormatter_1 = require("./statsValueFormatter"); -class StatsReportGatherer { - // private readonly ssrcToMid = { local: new Map(), remote: new Map() }; - constructor(callId, remoteUserId, pc, emitter, isFocus = true) { - this.callId = callId; - this.remoteUserId = remoteUserId; - this.pc = pc; - this.emitter = emitter; - this.isFocus = isFocus; - this.isActive = true; - this.connectionStats = new connectionStats_1.ConnectionStats(); - pc.addEventListener("signalingstatechange", this.onSignalStateChange.bind(this)); - this.trackStats = new mediaTrackStatsHandler_1.MediaTrackStatsHandler(new mediaSsrcHandler_1.MediaSsrcHandler(), new mediaTrackHandler_1.MediaTrackHandler(pc)); - } - processStats(groupCallId, localUserId) { - return __awaiter(this, void 0, void 0, function* () { - if (this.isActive) { - const statsPromise = this.pc.getStats(); - if (typeof (statsPromise === null || statsPromise === void 0 ? void 0 : statsPromise.then) === "function") { - return statsPromise - .then((report) => { - // @ts-ignore - this.currentStatsReport = typeof (report === null || report === void 0 ? void 0 : report.result) === "function" ? report.result() : report; - try { - this.processStatsReport(groupCallId, localUserId); - } - catch (error) { - this.isActive = false; - return false; - } - this.previousStatsReport = this.currentStatsReport; - return true; - }) - .catch((error) => { - this.handleError(error); - return false; - }); - } - this.isActive = false; - } - return Promise.resolve(false); - }); - } - processStatsReport(groupCallId, localUserId) { - var _a; - const byteSentStats = new Map(); - (_a = this.currentStatsReport) === null || _a === void 0 ? void 0 : _a.forEach((now) => { - const before = this.previousStatsReport ? this.previousStatsReport.get(now.id) : null; - // RTCIceCandidatePairStats - https://w3c.github.io/webrtc-stats/#candidatepair-dict* - if (now.type === "candidate-pair" && now.nominated && now.state === "succeeded") { - this.connectionStats.bandwidth = connectionStatsReporter_1.ConnectionStatsReporter.buildBandwidthReport(now); - this.connectionStats.transport = transportStatsReporter_1.TransportStatsReporter.buildReport(this.currentStatsReport, now, this.connectionStats.transport, this.isFocus); - // RTCReceivedRtpStreamStats - // https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict* - // RTCSentRtpStreamStats - // https://w3c.github.io/webrtc-stats/#sentrtpstats-dict* - } - else if (now.type === "inbound-rtp" || now.type === "outbound-rtp") { - const trackStats = this.trackStats.findTrack2Stats(now, now.type === "inbound-rtp" ? "remote" : "local"); - if (!trackStats) { - return; - } - if (before) { - trackStatsReporter_1.TrackStatsReporter.buildPacketsLost(trackStats, now, before); - } - // Get the resolution and framerate for only remote video sources here. For the local video sources, - // 'track' stats will be used since they have the updated resolution based on the simulcast streams - // currently being sent. Promise based getStats reports three 'outbound-rtp' streams and there will be - // more calculations needed to determine what is the highest resolution stream sent by the client if the - // 'outbound-rtp' stats are used. - if (now.type === "inbound-rtp") { - trackStatsReporter_1.TrackStatsReporter.buildFramerateResolution(trackStats, now); - if (before) { - trackStatsReporter_1.TrackStatsReporter.buildBitrateReceived(trackStats, now, before); - } - } - else if (before) { - byteSentStats.set(trackStats.trackId, statsValueFormatter_1.StatsValueFormatter.getNonNegativeValue(now.bytesSent)); - trackStatsReporter_1.TrackStatsReporter.buildBitrateSend(trackStats, now, before); - } - trackStatsReporter_1.TrackStatsReporter.buildCodec(this.currentStatsReport, trackStats, now); - } - else if (now.type === "track" && now.kind === "video" && !now.remoteSource) { - const trackStats = this.trackStats.findLocalVideoTrackStats(now); - if (!trackStats) { - return; - } - trackStatsReporter_1.TrackStatsReporter.buildFramerateResolution(trackStats, now); - trackStatsReporter_1.TrackStatsReporter.calculateSimulcastFramerate(trackStats, now, before, this.trackStats.mediaTrackHandler.getActiveSimulcastStreams()); - } - }); - this.emitter.emitByteSendReport(byteSentStats); - this.processAndEmitReport(); - } - setActive(isActive) { - this.isActive = isActive; - } - getActive() { - return this.isActive; - } - handleError(_) { - this.isActive = false; - } - processAndEmitReport() { - const report = statsReportBuilder_1.StatsReportBuilder.build(this.trackStats.getTrack2stats()); - this.connectionStats.bandwidth = report.bandwidth; - this.connectionStats.bitrate = report.bitrate; - this.connectionStats.packetLoss = report.packetLoss; - this.emitter.emitConnectionStatsReport(Object.assign(Object.assign({}, report), { transport: this.connectionStats.transport })); - this.connectionStats.transport = []; - } - stopProcessingStats() { } - onSignalStateChange() { - if (this.pc.signalingState === "stable") { - if (this.pc.currentRemoteDescription) { - this.trackStats.mediaSsrcHandler.parse(this.pc.currentRemoteDescription.sdp, "remote"); - } - if (this.pc.currentLocalDescription) { - this.trackStats.mediaSsrcHandler.parse(this.pc.currentLocalDescription.sdp, "local"); - } - } - } -} -exports.StatsReportGatherer = StatsReportGatherer; - -},{"./connectionStats":425,"./connectionStatsReporter":426,"./media/mediaSsrcHandler":428,"./media/mediaTrackHandler":429,"./media/mediaTrackStatsHandler":431,"./statsReportBuilder":433,"./statsValueFormatter":436,"./trackStatsReporter":437,"./transportStatsReporter":438}],436:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StatsValueFormatter = void 0; -/* -Copyright 2023 The Matrix.org Foundation C.I.C. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -class StatsValueFormatter { - static getNonNegativeValue(imput) { - let value = imput; - if (typeof value !== "number") { - value = Number(value); - } - if (isNaN(value)) { - return 0; - } - return Math.max(0, value); - } -} -exports.StatsValueFormatter = StatsValueFormatter; - -},{}],437:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TrackStatsReporter = void 0; -const statsValueFormatter_1 = require("./statsValueFormatter"); -class TrackStatsReporter { - static buildFramerateResolution(trackStats, now) { - const resolution = { - height: now.frameHeight, - width: now.frameWidth, - }; - const frameRate = now.framesPerSecond; - if (resolution.height && resolution.width) { - trackStats.setResolution(resolution); - } - trackStats.setFramerate(Math.round(frameRate || 0)); - } - static calculateSimulcastFramerate(trackStats, now, before, layer) { - let frameRate = trackStats.getFramerate(); - if (!frameRate) { - if (before) { - const timeMs = now.timestamp - before.timestamp; - if (timeMs > 0 && now.framesSent) { - const numberOfFramesSinceBefore = now.framesSent - before.framesSent; - frameRate = (numberOfFramesSinceBefore / timeMs) * 1000; - } - } - if (!frameRate) { - return; - } - } - // Reset frame rate to 0 when video is suspended as a result of endpoint falling out of last-n. - frameRate = layer ? Math.round(frameRate / layer) : 0; - trackStats.setFramerate(frameRate); - } - static buildCodec(report, trackStats, now) { - const codec = report === null || report === void 0 ? void 0 : report.get(now.codecId); - if (codec) { - /** - * The mime type has the following form: video/VP8 or audio/ISAC, - * so we what to keep just the type after the '/', audio and video - * keys will be added on the processing side. - */ - const codecShortType = codec.mimeType.split("/")[1]; - codecShortType && trackStats.setCodec(codecShortType); - } - } - static buildBitrateReceived(trackStats, now, before) { - trackStats.setBitrate({ - download: TrackStatsReporter.calculateBitrate(now.bytesReceived, before.bytesReceived, now.timestamp, before.timestamp), - upload: 0, - }); - } - static buildBitrateSend(trackStats, now, before) { - trackStats.setBitrate({ - download: 0, - upload: this.calculateBitrate(now.bytesSent, before.bytesSent, now.timestamp, before.timestamp), - }); - } - static buildPacketsLost(trackStats, now, before) { - const key = now.type === "outbound-rtp" ? "packetsSent" : "packetsReceived"; - let packetsNow = now[key]; - if (!packetsNow || packetsNow < 0) { - packetsNow = 0; - } - const packetsBefore = statsValueFormatter_1.StatsValueFormatter.getNonNegativeValue(before[key]); - const packetsDiff = Math.max(0, packetsNow - packetsBefore); - const packetsLostNow = statsValueFormatter_1.StatsValueFormatter.getNonNegativeValue(now.packetsLost); - const packetsLostBefore = statsValueFormatter_1.StatsValueFormatter.getNonNegativeValue(before.packetsLost); - const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore); - trackStats.setLoss({ - packetsTotal: packetsDiff + packetsLostDiff, - packetsLost: packetsLostDiff, - isDownloadStream: now.type !== "outbound-rtp", - }); - } - static calculateBitrate(bytesNowAny, bytesBeforeAny, nowTimestamp, beforeTimestamp) { - const bytesNow = statsValueFormatter_1.StatsValueFormatter.getNonNegativeValue(bytesNowAny); - const bytesBefore = statsValueFormatter_1.StatsValueFormatter.getNonNegativeValue(bytesBeforeAny); - const bytesProcessed = Math.max(0, bytesNow - bytesBefore); - const timeMs = nowTimestamp - beforeTimestamp; - let bitrateKbps = 0; - if (timeMs > 0) { - // TODO is there any reason to round here? - bitrateKbps = Math.round((bytesProcessed * 8) / timeMs); - } - return bitrateKbps; - } -} -exports.TrackStatsReporter = TrackStatsReporter; - -},{"./statsValueFormatter":436}],438:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TransportStatsReporter = void 0; -class TransportStatsReporter { - static buildReport(report, now, conferenceStatsTransport, isFocus) { - const localUsedCandidate = report === null || report === void 0 ? void 0 : report.get(now.localCandidateId); - const remoteUsedCandidate = report === null || report === void 0 ? void 0 : report.get(now.remoteCandidateId); - // RTCIceCandidateStats - // https://w3c.github.io/webrtc-stats/#icecandidate-dict* - if (remoteUsedCandidate && localUsedCandidate) { - const remoteIpAddress = remoteUsedCandidate.ip !== undefined ? remoteUsedCandidate.ip : remoteUsedCandidate.address; - const remotePort = remoteUsedCandidate.port; - const ip = `${remoteIpAddress}:${remotePort}`; - const localIpAddress = localUsedCandidate.ip !== undefined ? localUsedCandidate.ip : localUsedCandidate.address; - const localPort = localUsedCandidate.port; - const localIp = `${localIpAddress}:${localPort}`; - const type = remoteUsedCandidate.protocol; - // Save the address unless it has been saved already. - if (!conferenceStatsTransport.some((t) => t.ip === ip && t.type === type && t.localIp === localIp)) { - conferenceStatsTransport.push({ - ip, - type, - localIp, - isFocus, - localCandidateType: localUsedCandidate.candidateType, - remoteCandidateType: remoteUsedCandidate.candidateType, - networkType: localUsedCandidate.networkType, - rtt: now.currentRoundTripTime ? now.currentRoundTripTime * 1000 : NaN, - }); - } - } - return conferenceStatsTransport; - } -} -exports.TransportStatsReporter = TransportStatsReporter; - -},{}]},{},[320]) -//# sourceMappingURL=browser-matrix.js.map -- cgit