From b1ea3c0fecb8addde61338cedda13e755a2f48ea Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 10:04:47 +1100 Subject: [PATCH 01/30] Move node-gyp to dev dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ee6822cbc..d22b1021b 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,6 @@ "moment": "2.21.0", "mustache": "2.3.0", "node-fetch": "2.3.0", - "node-gyp": "3.8.0", "node-sass": "4.9.3", "os-locale": "2.1.0", "pify": "3.0.0", @@ -180,6 +179,7 @@ "grunt-sass": "3.0.1", "mocha": "4.1.0", "mocha-testcheck": "1.0.0-rc.0", + "node-gyp": "3.8.0", "node-sass-import-once": "1.2.0", "nyc": "11.4.1", "prettier": "1.12.0", From d671ce005595530961b170dc729d64ae8d49f670 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 10:44:00 +1100 Subject: [PATCH 02/30] Remove phone number libraries --- .eslintignore | 1 - background.html | 2 - background_test.html | 2 - js/libphonenumber-util.js | 295 ------------------------------ js/views/phone-input-view.js | 39 ---- package.json | 2 - preload.js | 2 - test/index.html | 3 - test/libphonenumber_util_test.js | 26 --- ts/util/libphonenumberInstance.ts | 6 - yarn.lock | 10 - 11 files changed, 388 deletions(-) delete mode 100644 js/libphonenumber-util.js delete mode 100644 js/views/phone-input-view.js delete mode 100644 test/libphonenumber_util_test.js delete mode 100644 ts/util/libphonenumberInstance.ts diff --git a/.eslintignore b/.eslintignore index a1a37d7f5..9957d65d8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -19,7 +19,6 @@ test/test.js # Third-party files js/Mp3LameEncoder.min.js js/WebAudioRecorderMp3.js -js/libphonenumber-util.js js/libsignal-protocol-worker.js libtextsecure/libsignal-protocol.js libtextsecure/test/blanket_mocha.js diff --git a/background.html b/background.html index 785d40ec2..c3994d233 100644 --- a/background.html +++ b/background.html @@ -454,7 +454,6 @@ - @@ -501,7 +500,6 @@ - diff --git a/background_test.html b/background_test.html index 39480ba67..8433a9af4 100644 --- a/background_test.html +++ b/background_test.html @@ -454,7 +454,6 @@ - @@ -501,7 +500,6 @@ - diff --git a/js/libphonenumber-util.js b/js/libphonenumber-util.js deleted file mode 100644 index ac13013c8..000000000 --- a/js/libphonenumber-util.js +++ /dev/null @@ -1,295 +0,0 @@ -(function() { - 'use strict'; - - /* - * This file extends the libphonenumber object with a set of phonenumbery - * utility functions. libphonenumber must be included before you call these - * functions, but the order of the files/script-tags doesn't matter. - */ - - window.libphonenumber = window.libphonenumber || {}; - window.libphonenumber.util = { - getRegionCodeForNumber: function(number) { - try { - var parsedNumber = libphonenumber.parse(number); - return libphonenumber.getRegionCodeForNumber(parsedNumber); - } catch (e) { - return 'ZZ'; - } - }, - - splitCountryCode: function(number) { - var parsedNumber = libphonenumber.parse(number); - return { - country_code: parsedNumber.values_[1], - national_number: parsedNumber.values_[2], - }; - }, - - getCountryCode: function(regionCode) { - var cc = libphonenumber.getCountryCodeForRegion(regionCode); - return cc !== 0 ? cc : ''; - }, - - parseNumber: function(number, defaultRegionCode) { - try { - var parsedNumber = libphonenumber.parse(number, defaultRegionCode); - - return { - isValidNumber: libphonenumber.isValidNumber(parsedNumber), - regionCode: libphonenumber.getRegionCodeForNumber(parsedNumber), - countryCode: '' + parsedNumber.getCountryCode(), - nationalNumber: '' + parsedNumber.getNationalNumber(), - e164: libphonenumber.format( - parsedNumber, - libphonenumber.PhoneNumberFormat.E164 - ), - }; - } catch (ex) { - return { error: ex, isValidNumber: false }; - } - }, - - getAllRegionCodes: function() { - return { - AD: 'Andorra', - AE: 'United Arab Emirates', - AF: 'Afghanistan', - AG: 'Antigua and Barbuda', - AI: 'Anguilla', - AL: 'Albania', - AM: 'Armenia', - AO: 'Angola', - AR: 'Argentina', - AS: 'AmericanSamoa', - AT: 'Austria', - AU: 'Australia', - AW: 'Aruba', - AX: 'Åland Islands', - AZ: 'Azerbaijan', - BA: 'Bosnia and Herzegovina', - BB: 'Barbados', - BD: 'Bangladesh', - BE: 'Belgium', - BF: 'Burkina Faso', - BG: 'Bulgaria', - BH: 'Bahrain', - BI: 'Burundi', - BJ: 'Benin', - BL: 'Saint Barthélemy', - BM: 'Bermuda', - BN: 'Brunei Darussalam', - BO: 'Bolivia, Plurinational State of', - BR: 'Brazil', - BS: 'Bahamas', - BT: 'Bhutan', - BW: 'Botswana', - BY: 'Belarus', - BZ: 'Belize', - CA: 'Canada', - CC: 'Cocos (Keeling) Islands', - CD: 'Congo, The Democratic Republic of the', - CF: 'Central African Republic', - CG: 'Congo', - CH: 'Switzerland', - CI: "Cote d'Ivoire", - CK: 'Cook Islands', - CL: 'Chile', - CM: 'Cameroon', - CN: 'China', - CO: 'Colombia', - CR: 'Costa Rica', - CU: 'Cuba', - CV: 'Cape Verde', - CX: 'Christmas Island', - CY: 'Cyprus', - CZ: 'Czech Republic', - DE: 'Germany', - DJ: 'Djibouti', - DK: 'Denmark', - DM: 'Dominica', - DO: 'Dominican Republic', - DZ: 'Algeria', - EC: 'Ecuador', - EE: 'Estonia', - EG: 'Egypt', - ER: 'Eritrea', - ES: 'Spain', - ET: 'Ethiopia', - FI: 'Finland', - FJ: 'Fiji', - FK: 'Falkland Islands (Malvinas)', - FM: 'Micronesia, Federated States of', - FO: 'Faroe Islands', - FR: 'France', - GA: 'Gabon', - GB: 'United Kingdom', - GD: 'Grenada', - GE: 'Georgia', - GF: 'French Guiana', - GG: 'Guernsey', - GH: 'Ghana', - GI: 'Gibraltar', - GL: 'Greenland', - GM: 'Gambia', - GN: 'Guinea', - GP: 'Guadeloupe', - GQ: 'Equatorial Guinea', - GR: 'Ελλάδα', - GT: 'Guatemala', - GU: 'Guam', - GW: 'Guinea-Bissau', - GY: 'Guyana', - HK: 'Hong Kong', - HN: 'Honduras', - HR: 'Croatia', - HT: 'Haiti', - HU: 'Magyarország', - ID: 'Indonesia', - IE: 'Ireland', - IL: 'Israel', - IM: 'Isle of Man', - IN: 'India', - IO: 'British Indian Ocean Territory', - IQ: 'Iraq', - IR: 'Iran, Islamic Republic of', - IS: 'Iceland', - IT: 'Italy', - JE: 'Jersey', - JM: 'Jamaica', - JO: 'Jordan', - JP: 'Japan', - KE: 'Kenya', - KG: 'Kyrgyzstan', - KH: 'Cambodia', - KI: 'Kiribati', - KM: 'Comoros', - KN: 'Saint Kitts and Nevis', - KP: "Korea, Democratic People's Republic of", - KR: 'Korea, Republic of', - KW: 'Kuwait', - KY: 'Cayman Islands', - KZ: 'Kazakhstan', - LA: "Lao People's Democratic Republic", - LB: 'Lebanon', - LC: 'Saint Lucia', - LI: 'Liechtenstein', - LK: 'Sri Lanka', - LR: 'Liberia', - LS: 'Lesotho', - LT: 'Lithuania', - LU: 'Luxembourg', - LV: 'Latvia', - LY: 'Libyan Arab Jamahiriya', - MA: 'Morocco', - MC: 'Monaco', - MD: 'Moldova, Republic of', - ME: 'Црна Гора', - MF: 'Saint Martin', - MG: 'Madagascar', - MH: 'Marshall Islands', - MK: 'Macedonia, The Former Yugoslav Republic of', - ML: 'Mali', - MM: 'Myanmar', - MN: 'Mongolia', - MO: 'Macao', - MP: 'Northern Mariana Islands', - MQ: 'Martinique', - MR: 'Mauritania', - MS: 'Montserrat', - MT: 'Malta', - MU: 'Mauritius', - MV: 'Maldives', - MW: 'Malawi', - MX: 'Mexico', - MY: 'Malaysia', - MZ: 'Mozambique', - NA: 'Namibia', - NC: 'New Caledonia', - NE: 'Niger', - NF: 'Norfolk Island', - NG: 'Nigeria', - NI: 'Nicaragua', - NL: 'Netherlands', - NO: 'Norway', - NP: 'Nepal', - NR: 'Nauru', - NU: 'Niue', - NZ: 'New Zealand', - OM: 'Oman', - PA: 'Panama', - PE: 'Peru', - PF: 'French Polynesia', - PG: 'Papua New Guinea', - PH: 'Philippines', - PK: 'Pakistan', - PL: 'Polska', - PM: 'Saint Pierre and Miquelon', - PR: 'Puerto Rico', - PS: 'Palestinian Territory, Occupied', - PT: 'Portugal', - PW: 'Palau', - PY: 'Paraguay', - QA: 'Qatar', - RE: 'Réunion', - RO: 'Romania', - RS: 'Србија', - RU: 'Russia', - RW: 'Rwanda', - SA: 'Saudi Arabia', - SB: 'Solomon Islands', - SC: 'Seychelles', - SD: 'Sudan', - SE: 'Sweden', - SG: 'Singapore', - SH: 'Saint Helena, Ascension and Tristan Da Cunha', - SI: 'Slovenia', - SJ: 'Svalbard and Jan Mayen', - SK: 'Slovakia', - SL: 'Sierra Leone', - SM: 'San Marino', - SN: 'Senegal', - SO: 'Somalia', - SR: 'Suriname', - ST: 'Sao Tome and Principe', - SV: 'El Salvador', - SY: 'Syrian Arab Republic', - SZ: 'Swaziland', - TC: 'Turks and Caicos Islands', - TD: 'Chad', - TG: 'Togo', - TH: 'Thailand', - TJ: 'Tajikistan', - TK: 'Tokelau', - TL: 'Timor-Leste', - TM: 'Turkmenistan', - TN: 'Tunisia', - TO: 'Tonga', - TR: 'Turkey', - TT: 'Trinidad and Tobago', - TV: 'Tuvalu', - TW: 'Taiwan, Province of China', - TZ: 'Tanzania, United Republic of', - UA: 'Ukraine', - UG: 'Uganda', - US: 'United States', - UY: 'Uruguay', - UZ: 'Uzbekistan', - VA: 'Holy See (Vatican City State)', - VC: 'Saint Vincent and the Grenadines', - VE: 'Venezuela', - VG: 'Virgin Islands, British', - VI: 'Virgin Islands, U.S.', - VN: 'Viet Nam', - VU: 'Vanuatu', - WF: 'Wallis and Futuna', - WS: 'Samoa', - YE: 'Yemen', - YT: 'Mayotte', - ZA: 'South Africa', - ZM: 'Zambia', - ZW: 'Zimbabwe', - }; - }, // getAllRegionCodes - }; // libphonenumber.util -})(); diff --git a/js/views/phone-input-view.js b/js/views/phone-input-view.js deleted file mode 100644 index e9537f901..000000000 --- a/js/views/phone-input-view.js +++ /dev/null @@ -1,39 +0,0 @@ -/* global libphonenumber, Whisper */ - -// eslint-disable-next-line func-names -(function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - Whisper.PhoneInputView = Whisper.View.extend({ - tagName: 'div', - className: 'phone-input', - templateName: 'phone-number', - initialize() { - this.$('input.number').intlTelInput(); - }, - events: { - change: 'validateNumber', - keyup: 'validateNumber', - }, - validateNumber() { - const input = this.$('input.number'); - const regionCode = this.$('li.active') - .attr('data-country-code') - .toUpperCase(); - const number = input.val(); - - const parsedNumber = libphonenumber.util.parseNumber(number, regionCode); - if (parsedNumber.isValidNumber) { - this.$('.number-container').removeClass('invalid'); - this.$('.number-container').addClass('valid'); - } else { - this.$('.number-container').removeClass('valid'); - } - input.trigger('validation'); - - return parsedNumber.e164; - }, - }); -})(); diff --git a/package.json b/package.json index d22b1021b..a8ff16325 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "form-data": "^3.0.0", "fs-extra": "9.0.0", "glob": "7.1.2", - "google-libphonenumber": "3.2.2", "got": "8.2.0", "he": "1.2.0", "intl-tel-input": "12.1.15", @@ -132,7 +131,6 @@ "@types/electron-is-dev": "^1.1.1", "@types/filesize": "3.6.0", "@types/fs-extra": "5.0.5", - "@types/google-libphonenumber": "7.4.14", "@types/got": "9.4.1", "@types/jquery": "3.3.29", "@types/js-yaml": "3.12.0", diff --git a/preload.js b/preload.js index 08c73ed67..b6eae9581 100644 --- a/preload.js +++ b/preload.js @@ -337,8 +337,6 @@ window.dataURLToBlobSync = require('blueimp-canvas-to-blob'); window.emojiData = require('emoji-datasource'); window.EmojiPanel = require('emoji-panel'); window.filesize = require('filesize'); -window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInstance(); -window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat; window.loadImage = require('blueimp-load-image'); window.getGuid = require('uuid/v4'); window.profileImages = require('./app/profile_images'); diff --git a/test/index.html b/test/index.html index 13477e485..a779fba6d 100644 --- a/test/index.html +++ b/test/index.html @@ -500,7 +500,6 @@ - @@ -548,7 +547,6 @@ - @@ -576,7 +574,6 @@ - diff --git a/test/libphonenumber_util_test.js b/test/libphonenumber_util_test.js deleted file mode 100644 index 0e478e4eb..000000000 --- a/test/libphonenumber_util_test.js +++ /dev/null @@ -1,26 +0,0 @@ -/* global libphonenumber */ - -'use strict'; - -describe('libphonenumber util', () => { - describe('parseNumber', () => { - it('numbers with + are valid without providing regionCode', () => { - const result = libphonenumber.util.parseNumber('+14155555555'); - assert.isTrue(result.isValidNumber); - assert.strictEqual(result.nationalNumber, '4155555555'); - assert.strictEqual(result.e164, '+14155555555'); - assert.strictEqual(result.regionCode, 'US'); - assert.strictEqual(result.countryCode, '1'); - }); - it('variant numbers with the right regionCode are valid', () => { - ['4155555555', '14155555555', '+14155555555'].forEach(number => { - const result = libphonenumber.util.parseNumber(number, 'US'); - assert.isTrue(result.isValidNumber); - assert.strictEqual(result.nationalNumber, '4155555555'); - assert.strictEqual(result.e164, '+14155555555'); - assert.strictEqual(result.regionCode, 'US'); - assert.strictEqual(result.countryCode, '1'); - }); - }); - }); -}); diff --git a/ts/util/libphonenumberInstance.ts b/ts/util/libphonenumberInstance.ts deleted file mode 100644 index 40855ac23..000000000 --- a/ts/util/libphonenumberInstance.ts +++ /dev/null @@ -1,6 +0,0 @@ -import libphonenumber from 'google-libphonenumber'; - -const instance = libphonenumber.PhoneNumberUtil.getInstance(); -const PhoneNumberFormat = libphonenumber.PhoneNumberFormat; - -export { instance, PhoneNumberFormat }; diff --git a/yarn.lock b/yarn.lock index ff06052c3..37b1b7e69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -247,11 +247,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/google-libphonenumber@7.4.14": - version "7.4.14" - resolved "https://registry.yarnpkg.com/@types/google-libphonenumber/-/google-libphonenumber-7.4.14.tgz#3625d7aed0c16df920588428c86f0538bd0612ec" - integrity sha512-HGQobTn8CGn0/j7lezIHlGTE1czxQU6NeT7TYtC3OWcbyyUUSfdt4tXSTEGbXmNZlE+xczTDbyR8P6aYGPzG1A== - "@types/got@9.4.1": version "9.4.1" resolved "https://registry.yarnpkg.com/@types/got/-/got-9.4.1.tgz#7479a3a321599b5e17647f3bd9d402b8c55bfee1" @@ -4389,11 +4384,6 @@ glogg@^1.0.1: dependencies: sparkles "^1.0.0" -google-libphonenumber@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.2.tgz#3d9d7ba727e99a50812f21b0ed313723b76c5c54" - integrity sha512-ubjGeosYPeusjYbUHy76lCniGTTI0k1rIFc+uKBX+jHQLDmWOSUtlFUxaeoLJ+Y+PAMM6dWp+C1HjHx5BI8kEw== - got@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/got/-/got-8.2.0.tgz#0d11a071d05046348a2f5c0a5fa047fb687fdfc6" From e875ba25270ec908b93f1948ae35ee6e69b74922 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 10:44:16 +1100 Subject: [PATCH 03/30] remove got. Fix session IDs not being redacted in logs. --- js/modules/debuglogs.js | 46 ++++------- js/modules/privacy.js | 12 +-- main.js | 15 ++-- package.json | 1 - preload.js | 5 ++ test/modules/privacy_test.js | 39 +++++---- yarn.lock | 156 +---------------------------------- 7 files changed, 62 insertions(+), 212 deletions(-) diff --git a/js/modules/debuglogs.js b/js/modules/debuglogs.js index 81b00a5b8..66fd6252b 100644 --- a/js/modules/debuglogs.js +++ b/js/modules/debuglogs.js @@ -2,46 +2,24 @@ /* global window */ const FormData = require('form-data'); -const got = require('got'); +const fetch = require('node-fetch'); const BASE_URL = 'https://debuglogs.org'; const VERSION = window.getVersion(); const USER_AGENT = `Session ${VERSION}`; -// Workaround: Submitting `FormData` using native `FormData::submit` procedure -// as integration with `got` results in S3 error saying we haven’t set the -// `Content-Length` header: -// https://github.com/sindresorhus/got/pull/466 -const submitFormData = (form, url) => - new Promise((resolve, reject) => { - form.submit(url, (error, response) => { - if (error) { - return reject(error); - } - - const { statusCode } = response; - if (statusCode !== 204) { - return reject( - new Error(`Failed to upload to S3, got status ${statusCode}`) - ); - } - - return resolve(); - }); - }); - // upload :: String -> Promise URL exports.upload = async content => { - const signedForm = await got.get(BASE_URL, { - json: true, + const signedForm = await fetch(BASE_URL, { headers: { - 'user-agent': USER_AGENT, + 'user-agent': USER_AGENT, }, }); - if (!signedForm.body) { + const json = await signedForm.json(); + if (!signedForm.ok || !json) { throw new Error('Failed to retrieve token'); } - const { fields, url } = signedForm.body; + const { fields, url } = json; const form = new FormData(); // The API expects `key` to be the first field: @@ -60,9 +38,15 @@ exports.upload = async content => { filename: `session-desktop-debug-log-${VERSION}.txt`, }); - // WORKAROUND: See comment on `submitFormData`: - // await got.post(url, { body: form }); - await submitFormData(form, url); + const result = await fetch(url, { + method: 'POST', + body: form, + }); + + const { status } = result; + if (status !== 204) { + throw new Error(`Failed to upload to S3, got status ${status}`) + } return `${BASE_URL}/${fields.key}`; }; diff --git a/js/modules/privacy.js b/js/modules/privacy.js index d737f5d05..21bdc6e66 100644 --- a/js/modules/privacy.js +++ b/js/modules/privacy.js @@ -7,7 +7,7 @@ const { compose } = require('lodash/fp'); const { escapeRegExp } = require('lodash'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); -const PHONE_NUMBER_PATTERN = /\+\d{7,12}(\d{3})/g; +const SESSION_ID_PATTERN = /\b(05[0-9a-f]{64})\b/gi const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g; const REDACTION_PLACEHOLDER = '[REDACTED]'; @@ -55,14 +55,14 @@ exports._pathToRegExp = filePath => { }; // Public API -// redactPhoneNumbers :: String -> String -exports.redactPhoneNumbers = text => { +// redactSessionID :: String -> String +exports.redactSessionID = text => { if (!is.string(text)) { throw new TypeError("'text' must be a string"); } - return text.replace(PHONE_NUMBER_PATTERN, `+${REDACTION_PLACEHOLDER}$1`); -}; + return text.replace(SESSION_ID_PATTERN, REDACTION_PLACEHOLDER); +} // redactGroupIds :: String -> String exports.redactGroupIds = text => { @@ -84,7 +84,7 @@ exports.redactSensitivePaths = exports._redactPath(APP_ROOT_PATH); exports.redactAll = compose( exports.redactSensitivePaths, exports.redactGroupIds, - exports.redactPhoneNumbers + exports.redactSessionID ); const removeNewlines = text => text.replace(/\r?\n|\r/g, ''); diff --git a/main.js b/main.js index ca2a1558a..addf453f7 100644 --- a/main.js +++ b/main.js @@ -612,12 +612,13 @@ function showAbout() { let debugLogWindow; async function showDebugLogWindow() { + if (debugLogWindow) { debugLogWindow.show(); return; } - const theme = await pify(getDataFromMainWindow)('theme-setting'); + const theme = await getThemeFromMainWindow(); const size = mainWindow.getSize(); const options = { width: Math.max(size[0] - 100, MIN_WIDTH), @@ -665,7 +666,7 @@ async function showPermissionsPopupWindow() { return; } - const theme = await pify(getDataFromMainWindow)('theme-setting'); + const theme = await getThemeFromMainWindow(); const size = mainWindow.getSize(); const options = { width: Math.min(400, size[0]), @@ -1130,9 +1131,9 @@ ipc.on('set-auto-update-setting', (event, enabled) => { } }); -function getDataFromMainWindow(name, callback) { - ipc.once(`get-success-${name}`, (_event, error, value) => - callback(error, value) - ); - mainWindow.webContents.send(`get-${name}`); +function getThemeFromMainWindow() { + return new Promise(resolve => { + ipc.once(`get-success-theme-setting`, (_event, value) => resolve(value)); + mainWindow.webContents.send(`get-theme-setting`); + }); } diff --git a/package.json b/package.json index a8ff16325..25e45860b 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "form-data": "^3.0.0", "fs-extra": "9.0.0", "glob": "7.1.2", - "got": "8.2.0", "he": "1.2.0", "intl-tel-input": "12.1.15", "jquery": "3.3.1", diff --git a/preload.js b/preload.js index b6eae9581..ed8800aa8 100644 --- a/preload.js +++ b/preload.js @@ -197,6 +197,11 @@ ipc.on('set-up-as-standalone', () => { Whisper.events.trigger('setupAsStandalone'); }); +ipc.on('get-theme-setting', () => { + const theme = window.Events.getThemeSetting(); + ipc.send('get-success-theme-setting', theme); +}); + // Settings-related events window.showPermissionsPopup = () => ipc.send('show-permissions-popup'); diff --git a/test/modules/privacy_test.js b/test/modules/privacy_test.js index df3490bd3..1f91fbdeb 100644 --- a/test/modules/privacy_test.js +++ b/test/modules/privacy_test.js @@ -7,18 +7,27 @@ const Privacy = require('../../js/modules/privacy'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); describe('Privacy', () => { - describe('redactPhoneNumbers', () => { - it('should redact all phone numbers', () => { + describe('redactSessionID', () => { + it('should redact all session IDs', () => { const text = - 'This is a log line with a phone number +12223334455\n' + - 'and another one +13334445566'; + 'This is a log line with a session ID 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827\n' + + 'and another one 05766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab62'; - const actual = Privacy.redactPhoneNumbers(text); + const actual = Privacy.redactSessionID(text); const expected = - 'This is a log line with a phone number +[REDACTED]455\n' + - 'and another one +[REDACTED]566'; + 'This is a log line with a session ID [REDACTED]\n' + + 'and another one [REDACTED]'; assert.equal(actual, expected); }); + + it('should not redact non session IDS', () => { + const text = + 'This is a log line with a non-session ID sadsad0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827888\n' + + 'and another one 766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab6234'; + + const actual = Privacy.redactSessionID(text); + assert.equal(actual, text); + }); }); describe('redactGroupIds', () => { @@ -53,20 +62,20 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${APP_ROOT_PATH}/main.js\n` + - 'phone1 +12223334455 ipsum\n' + + 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + 'group1 group(123456789) doloret\n' + `path2 file:///${encodedAppRootPath}/js/background.js.` + - 'phone2 +13334445566 lorem\n' + + 'phone2 0531033dc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 lorem\n' + 'group2 group(abcdefghij) doloret\n'; const actual = Privacy.redactAll(text); const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 +[REDACTED]455 ipsum\n' + + 'phone1 [REDACTED] ipsum\n' + 'group1 group([REDACTED]789) doloret\n' + 'path2 file:///[REDACTED]/js/background.js.' + - 'phone2 +[REDACTED]566 lorem\n' + + 'phone2 [REDACTED] lorem\n' + 'group2 group([REDACTED]hij) doloret\n'; assert.equal(actual, expected); }); @@ -78,13 +87,13 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${testPath}/main.js\n` + - 'phone1 +12223334455 ipsum\n'; + 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n'; const actual = Privacy._redactPath(testPath)(text); const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 +12223334455 ipsum\n'; + 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n'; assert.equal(actual, expected); }); @@ -94,7 +103,7 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${testPath}/main.js\n` + - 'phone1 +12223334455 ipsum\n' + + 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + 'group1 group(123456789) doloret\n' + `path2 file:///${encodedTestPath}/js/background.js.`; @@ -102,7 +111,7 @@ describe('Privacy', () => { const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 +12223334455 ipsum\n' + + 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + 'group1 group(123456789) doloret\n' + 'path2 file:///[REDACTED]/js/background.js.'; assert.equal(actual, expected); diff --git a/yarn.lock b/yarn.lock index 37b1b7e69..0ae3a8296 100644 --- a/yarn.lock +++ b/yarn.lock @@ -113,11 +113,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== - "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -1590,19 +1585,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -1914,7 +1896,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-response@1.0.2, clone-response@^1.0.2: +clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= @@ -3949,7 +3931,7 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -from2@^2.1.0, from2@^2.1.1: +from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= @@ -4134,7 +4116,7 @@ get-stdin@^5.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= -get-stream@3.0.0, get-stream@^3.0.0: +get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= @@ -4384,29 +4366,6 @@ glogg@^1.0.1: dependencies: sparkles "^1.0.0" -got@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/got/-/got-8.2.0.tgz#0d11a071d05046348a2f5c0a5fa047fb687fdfc6" - integrity sha512-giadqJpXIwjY+ZsuWys8p2yjZGhOHiU4hiJHjS/oeCxw1u8vANQz3zPlrxW2Zw/siCXsSMI3hvzWGcnFyujyAg== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -4623,23 +4582,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -4787,11 +4734,6 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -5073,14 +5015,6 @@ intl-tel-input@12.1.15: resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-12.1.15.tgz#7393e6b77572731bbc65ca4585782e8ba3d74de4" integrity sha512-9TN9x6aGdO1eL6iGFpobuLU4UymZqjSnS9UnsOSi//LU3A8nkLOcokSYBYjak18Uu8OM59HsGYDd1jKmwRsskw== -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -5365,11 +5299,6 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= - is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -5438,11 +5367,6 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-root@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5" @@ -5597,14 +5521,6 @@ istanbul-reports@^1.1.3: dependencies: handlebars "^4.0.3" -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - javascript-stringify@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" @@ -5873,13 +5789,6 @@ keyboardevents-areequal@^0.2.1: resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194" integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw== -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -6174,11 +6083,6 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= - lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -6917,15 +6821,6 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - normalize-url@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -7252,11 +7147,6 @@ osenv@0, osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -7267,11 +7157,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= - p-limit@^1.0.0, p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -7305,13 +7190,6 @@ p-map@^1.1.1: resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -8206,15 +8084,6 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -9092,7 +8961,7 @@ resolve@~1.1.0: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -responselike@1.0.2, responselike@^1.0.2: +responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= @@ -9586,13 +9455,6 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= - dependencies: - is-plain-obj "^1.0.0" - source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -10254,11 +10116,6 @@ time-stamp@^2.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz#917e0a66905688790ec7bbbde04046259af83f57" integrity sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA== -timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - timers-browserify@^2.0.4: version "2.0.11" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" @@ -10813,11 +10670,6 @@ url-regex@^3.0.0: dependencies: ip-regex "^1.0.1" -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" From 1cadb01e8d5b30b2122a0470c074553e1314f53f Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 12:22:32 +1100 Subject: [PATCH 04/30] Remove intl-tel-input --- bower.json | 1 - package.json | 1 - stylesheets/_options.scss | 13 ------------- stylesheets/_theme_dark.scss | 3 --- ts/util/lint/linter.ts | 1 - yarn.lock | 5 ----- 6 files changed, 24 deletions(-) diff --git a/bower.json b/bower.json index 8b2fbba06..751d468d0 100644 --- a/bower.json +++ b/bower.json @@ -56,7 +56,6 @@ "node_modules/backbone/backbone.js", "components/indexeddb-backbonejs-adapter/**/*.js", "components/qrcode/**/*.js", - "node_modules/intl-tel-input/build/js/intlTelInput.js", "components/autosize/**/*.js", "components/webaudiorecorder/lib/WebAudioRecorder.js", "components/multibase/dist/index.js" diff --git a/package.json b/package.json index 25e45860b..f35fafb8d 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "fs-extra": "9.0.0", "glob": "7.1.2", "he": "1.2.0", - "intl-tel-input": "12.1.15", "jquery": "3.3.1", "js-sha512": "0.8.0", "js-yaml": "3.13.0", diff --git a/stylesheets/_options.scss b/stylesheets/_options.scss index a28bbafcb..51b78dccd 100644 --- a/stylesheets/_options.scss +++ b/stylesheets/_options.scss @@ -1,15 +1,2 @@ @import 'variables'; -@import '../node_modules/intl-tel-input/build/css/intlTelInput.css'; @import 'progress'; -.iti-flag { - // override intlTelInput's flags image location - background: url('../node_modules/intl-tel-input/build/img/flags.png'); -} - -.intl-tel-input .country-list { - text-align: left; -} - -.intl-tel-input .country-list .country .country-name { - color: #000; -} diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index 39de2faad..b291bb442 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -1670,9 +1670,6 @@ body.dark-theme { // _options - .intl-tel-input .country-list .country .country-name { - color: #000; - } // _progress diff --git a/ts/util/lint/linter.ts b/ts/util/lint/linter.ts index 1ada85d60..a1ad506f7 100644 --- a/ts/util/lint/linter.ts +++ b/ts/util/lint/linter.ts @@ -122,7 +122,6 @@ const excludedFiles = [ '^node_modules/hpack\\.js/*', '^node_modules/http-proxy-middlewar/*', '^node_modules/icss-utils/*', - '^node_modules/intl-tel-input/examples/*', '^node_modules/istanbul*', '^node_modules/jimp/*', '^node_modules/jquery/*', diff --git a/yarn.lock b/yarn.lock index 0ae3a8296..abb2c1f7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5010,11 +5010,6 @@ internal-ip@1.2.0: dependencies: meow "^3.3.0" -intl-tel-input@12.1.15: - version "12.1.15" - resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-12.1.15.tgz#7393e6b77572731bbc65ca4585782e8ba3d74de4" - integrity sha512-9TN9x6aGdO1eL6iGFpobuLU4UymZqjSnS9UnsOSi//LU3A8nkLOcokSYBYjak18Uu8OM59HsGYDd1jKmwRsskw== - invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" From b38c666e95fac064609f0d8cfd26d891d8e6635d Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 12:32:52 +1100 Subject: [PATCH 05/30] Remove js-sha512 --- app/password_util.js | 9 ++++++++- package.json | 1 - yarn.lock | 5 ----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/password_util.js b/app/password_util.js index 944769c60..d2bf73c20 100644 --- a/app/password_util.js +++ b/app/password_util.js @@ -1,4 +1,4 @@ -const { sha512 } = require('js-sha512'); +const crypto = require('crypto'); const ERRORS = { TYPE: 'Password must be a string', @@ -6,6 +6,12 @@ const ERRORS = { CHARACTER: 'Password must only contain letters, numbers and symbols', }; +const sha512 = text => { + const hash = crypto.createHash('sha512'); + hash.update(text.trim()); + return hash.digest('hex'); +}; + const generateHash = phrase => phrase && sha512(phrase.trim()); const matchesHash = (phrase, hash) => phrase && sha512(phrase.trim()) === hash.trim(); @@ -29,6 +35,7 @@ const validatePassword = (phrase, i18n) => { return null; }; + module.exports = { generateHash, matchesHash, diff --git a/package.json b/package.json index f35fafb8d..8a1d8193e 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "glob": "7.1.2", "he": "1.2.0", "jquery": "3.3.1", - "js-sha512": "0.8.0", "js-yaml": "3.13.0", "jsbn": "1.1.0", "linkify-it": "2.0.3", diff --git a/yarn.lock b/yarn.lock index abb2c1f7c..07431b7b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5558,11 +5558,6 @@ js-base64@^2.1.8, js-base64@^2.1.9: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== -js-sha512@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" - integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== - "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" From 828444aadc8d6027786c9ae5d009eefb8f209267 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 12:32:57 +1100 Subject: [PATCH 06/30] Linting --- app/password_util.js | 1 - js/modules/debuglogs.js | 4 ++-- js/modules/privacy.js | 4 ++-- main.js | 1 - stylesheets/_theme_dark.scss | 1 - test/modules/privacy_test.js | 4 ++-- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/password_util.js b/app/password_util.js index d2bf73c20..915f017fe 100644 --- a/app/password_util.js +++ b/app/password_util.js @@ -35,7 +35,6 @@ const validatePassword = (phrase, i18n) => { return null; }; - module.exports = { generateHash, matchesHash, diff --git a/js/modules/debuglogs.js b/js/modules/debuglogs.js index 66fd6252b..bea65a431 100644 --- a/js/modules/debuglogs.js +++ b/js/modules/debuglogs.js @@ -12,7 +12,7 @@ const USER_AGENT = `Session ${VERSION}`; exports.upload = async content => { const signedForm = await fetch(BASE_URL, { headers: { - 'user-agent': USER_AGENT, + 'user-agent': USER_AGENT, }, }); const json = await signedForm.json(); @@ -45,7 +45,7 @@ exports.upload = async content => { const { status } = result; if (status !== 204) { - throw new Error(`Failed to upload to S3, got status ${status}`) + throw new Error(`Failed to upload to S3, got status ${status}`); } return `${BASE_URL}/${fields.key}`; diff --git a/js/modules/privacy.js b/js/modules/privacy.js index 21bdc6e66..1720b7420 100644 --- a/js/modules/privacy.js +++ b/js/modules/privacy.js @@ -7,7 +7,7 @@ const { compose } = require('lodash/fp'); const { escapeRegExp } = require('lodash'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); -const SESSION_ID_PATTERN = /\b(05[0-9a-f]{64})\b/gi +const SESSION_ID_PATTERN = /\b(05[0-9a-f]{64})\b/gi; const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g; const REDACTION_PLACEHOLDER = '[REDACTED]'; @@ -62,7 +62,7 @@ exports.redactSessionID = text => { } return text.replace(SESSION_ID_PATTERN, REDACTION_PLACEHOLDER); -} +}; // redactGroupIds :: String -> String exports.redactGroupIds = text => { diff --git a/main.js b/main.js index addf453f7..1990461fa 100644 --- a/main.js +++ b/main.js @@ -612,7 +612,6 @@ function showAbout() { let debugLogWindow; async function showDebugLogWindow() { - if (debugLogWindow) { debugLogWindow.show(); return; diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index b291bb442..6ff2f4e76 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -1670,7 +1670,6 @@ body.dark-theme { // _options - // _progress // Not sure we need to change anything there - it's blue diff --git a/test/modules/privacy_test.js b/test/modules/privacy_test.js index 1f91fbdeb..e51959f6a 100644 --- a/test/modules/privacy_test.js +++ b/test/modules/privacy_test.js @@ -22,8 +22,8 @@ describe('Privacy', () => { it('should not redact non session IDS', () => { const text = - 'This is a log line with a non-session ID sadsad0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827888\n' + - 'and another one 766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab6234'; + 'This is a log line with a non-session ID sadsad0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827888\n' + + 'and another one 766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab6234'; const actual = Privacy.redactSessionID(text); assert.equal(actual, text); From ba1525d6de6e7a2e39f53469fd24664073534d65 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 12:47:01 +1100 Subject: [PATCH 07/30] Remove js-yaml --- package.json | 3 --- yarn.lock | 26 -------------------------- 2 files changed, 29 deletions(-) diff --git a/package.json b/package.json index 8a1d8193e..0e64f76ec 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "glob": "7.1.2", "he": "1.2.0", "jquery": "3.3.1", - "js-yaml": "3.13.0", "jsbn": "1.1.0", "linkify-it": "2.0.3", "lodash": "4.17.11", @@ -128,9 +127,7 @@ "@types/electron-is-dev": "^1.1.1", "@types/filesize": "3.6.0", "@types/fs-extra": "5.0.5", - "@types/got": "9.4.1", "@types/jquery": "3.3.29", - "@types/js-yaml": "3.12.0", "@types/linkify-it": "2.0.3", "@types/lodash": "4.14.106", "@types/mkdirp": "0.5.2", diff --git a/yarn.lock b/yarn.lock index 07431b7b4..ce4913277 100644 --- a/yarn.lock +++ b/yarn.lock @@ -242,14 +242,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/got@9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@types/got/-/got-9.4.1.tgz#7479a3a321599b5e17647f3bd9d402b8c55bfee1" - integrity sha512-m7Uc07bG/bZ+Dis7yI3mGssYDcAdUvP4irF3ZmBzf0ig7zEd1FyADfnELVGcf+p1Ol/iPCXbZYwcSNOJA2a+Qg== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - "@types/jquery@3.3.29": version "3.3.29" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd" @@ -257,11 +249,6 @@ dependencies: "@types/sizzle" "*" -"@types/js-yaml@3.12.0": - version "3.12.0" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.0.tgz#3494ce97358e2675e24e97a747ec23478eeaf8b6" - integrity sha512-UGEe/6RsNAxgWdknhzFZbCxuYc5I7b/YEKlfKbo+76SM8CJzGs7XKCj7zyugXViRbKYpXhSXhCYVQZL5tmDbpQ== - "@types/linkify-it@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.0.3.tgz#5352a2d7a35d7c77b527483cd6e68da9148bd780" @@ -422,11 +409,6 @@ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== -"@types/tough-cookie@*": - version "2.3.6" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.6.tgz#c880579e087d7a0db13777ff8af689f4ffc7b0d5" - integrity sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ== - "@types/trusted-types@*": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.4.tgz#922d092c84a776a59acb0bd6785fd82b59b9bad5" @@ -5568,14 +5550,6 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e" - integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^3.13.1, js-yaml@^3.2.7, js-yaml@^3.7.0, js-yaml@^3.9.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" From 9d2417c2f62941baf447c78f1f9ecc5b4c53e1e0 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 13:24:06 +1100 Subject: [PATCH 08/30] Removed proxy-agent and web-socket. Trimmed down web_api.js to most basic functions we are using. This needs to be completely removed in the future. --- js/background.js | 39 -- js/modules/loki_snode_api.js | 2 +- js/modules/refresh_sender_certificate.js | 120 ----- js/modules/signal.js | 2 - js/modules/web_api.js | 574 +---------------------- libtextsecure/account_manager.js | 38 +- libtextsecure/message_receiver.js | 79 +--- libtextsecure/outgoing_message.js | 4 - libtextsecure/sendmessage.js | 17 +- libtextsecure/test/fake_web_api.js | 47 +- package.json | 4 +- preload.js | 8 +- yarn.lock | 232 +-------- 13 files changed, 40 insertions(+), 1126 deletions(-) delete mode 100644 js/modules/refresh_sender_certificate.js diff --git a/js/background.js b/js/background.js index 1b77e422a..8db812708 100644 --- a/js/background.js +++ b/js/background.js @@ -569,12 +569,6 @@ // listeners Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); - // window.Signal.RefreshSenderCertificate.initialize({ - // events: Whisper.events, - // storage, - // navigator, - // logger: window.log, - // }); connect(true); }); @@ -597,12 +591,6 @@ ) { // listeners Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); - // window.Signal.RefreshSenderCertificate.initialize({ - // events: Whisper.events, - // storage, - // navigator, - // logger: window.log, - // }); connect(); appView.openInbox({ @@ -1547,35 +1535,8 @@ textsecure.storage.user.getDeviceId() != '1' ) { window.getSyncRequest(); - - try { - const manager = window.getAccountManager(); - await Promise.all([ - manager.maybeUpdateDeviceName(), - manager.maybeDeleteSignalingKey(), - ]); - } catch (e) { - window.log.error( - 'Problem with account manager updates after starting new version: ', - e && e.stack ? e.stack : e - ); - } } - // const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery'; - // if (!storage.get(udSupportKey)) { - // const server = WebAPI.connect({ username: USERNAME, password: PASSWORD }); - // try { - // await server.registerSupportForUnauthenticatedDelivery(); - // storage.put(udSupportKey, true); - // } catch (error) { - // window.log.error( - // 'Error: Unable to register for unauthenticated delivery support.', - // error && error.stack ? error.stack : error - // ); - // } - // } - const deviceId = textsecure.storage.user.getDeviceId(); if (firstRun === true && deviceId !== '1') { const hasThemeSetting = Boolean(storage.get('theme-setting')); diff --git a/js/modules/loki_snode_api.js b/js/modules/loki_snode_api.js index 9642d64e0..42096a74e 100644 --- a/js/modules/loki_snode_api.js +++ b/js/modules/loki_snode_api.js @@ -19,7 +19,7 @@ const timeoutDelay = ms => new Promise(resolve => setTimeout(resolve, ms)); class LokiSnodeAPI { constructor({ serverUrl, localUrl }) { if (!is.string(serverUrl)) { - throw new Error('WebAPI.initialize: Invalid server url'); + throw new Error('LokiSnodeAPI.initialize: Invalid server url'); } this.serverUrl = serverUrl; // random.snode this.localUrl = localUrl; // localhost.loki diff --git a/js/modules/refresh_sender_certificate.js b/js/modules/refresh_sender_certificate.js deleted file mode 100644 index 275453fda..000000000 --- a/js/modules/refresh_sender_certificate.js +++ /dev/null @@ -1,120 +0,0 @@ -/* global window, setTimeout, clearTimeout, textsecure, WebAPI, ConversationController */ - -module.exports = { - initialize, -}; - -const ONE_DAY = 24 * 60 * 60 * 1000; // one day -const MINIMUM_TIME_LEFT = 2 * 60 * 60 * 1000; // two hours - -let timeout = null; -let scheduledTime = null; -let scheduleNext = null; - -// We need to refresh our own profile regularly to account for newly-added devices which -// do not support unidentified delivery. -function refreshOurProfile() { - const ourNumber = textsecure.storage.user.getNumber(); - const conversation = ConversationController.getOrCreate(ourNumber, 'private'); - conversation.getProfiles(); -} - -function initialize({ events, storage, navigator, logger }) { - // We don't want to set up all of the below functions, but we do want to ensure that our - // refresh timer is up-to-date. - if (scheduleNext) { - scheduleNext(); - return; - } - - runWhenOnline(); - - events.on('timetravel', scheduleNextRotation); - - function scheduleNextRotation() { - const now = Date.now(); - const certificate = storage.get('senderCertificate'); - if (!certificate) { - setTimeoutForNextRun(now); - - return; - } - - // The useful information in a SenderCertificate is all serialized, so we - // need to do another layer of decoding. - const decoded = textsecure.protobuf.SenderCertificate.Certificate.decode( - certificate.certificate - ); - const expires = decoded.expires.toNumber(); - - const time = Math.min(now + ONE_DAY, expires - MINIMUM_TIME_LEFT); - - setTimeoutForNextRun(time); - } - - // Keeping this entrypoint around so more inialize() calls just kick the timing - scheduleNext = scheduleNextRotation; - - async function run() { - logger.info('refreshSenderCertificate: Getting new certificate...'); - try { - const username = storage.get('number_id'); - const password = storage.get('password'); - const server = WebAPI.connect({ username, password }); - - const { certificate } = await server.getSenderCertificate(); - const arrayBuffer = window.Signal.Crypto.base64ToArrayBuffer(certificate); - const decoded = textsecure.protobuf.SenderCertificate.decode(arrayBuffer); - - decoded.certificate = decoded.certificate.toArrayBuffer(); - decoded.signature = decoded.signature.toArrayBuffer(); - decoded.serialized = arrayBuffer; - - storage.put('senderCertificate', decoded); - scheduleNextRotation(); - } catch (error) { - logger.error( - 'refreshSenderCertificate: Get failed. Trying again in two minutes...', - error && error.stack ? error.stack : error - ); - setTimeout(runWhenOnline, 2 * 60 * 1000); - } - - refreshOurProfile(); - } - - function runWhenOnline() { - if (navigator.onLine) { - run(); - } else { - logger.info( - 'refreshSenderCertificate: Offline. Will update certificate when online...' - ); - const listener = () => { - logger.info( - 'refreshSenderCertificate: Online. Now updating certificate...' - ); - window.removeEventListener('online', listener); - run(); - }; - window.addEventListener('online', listener); - } - } - - function setTimeoutForNextRun(time = Date.now()) { - const now = Date.now(); - - if (scheduledTime !== time || !timeout) { - logger.info( - 'Next sender certificate refresh scheduled for', - new Date(time).toISOString() - ); - } - - scheduledTime = time; - const waitTime = Math.max(0, time - now); - - clearTimeout(timeout); - timeout = setTimeout(runWhenOnline, waitTime); - } -} diff --git a/js/modules/signal.js b/js/modules/signal.js index f5ddc198b..c676c5845 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -13,7 +13,6 @@ const Settings = require('./settings'); const Util = require('../../ts/util'); const { migrateToSQL } = require('./migrate_to_sql'); const Metadata = require('./metadata/SecretSessionCipher'); -const RefreshSenderCertificate = require('./refresh_sender_certificate'); const LinkPreviews = require('./link_previews'); const AttachmentDownloads = require('./attachment_downloads'); @@ -379,7 +378,6 @@ exports.setup = (options = {}) => { Migrations, Notifications, OS, - RefreshSenderCertificate, Settings, State, Types, diff --git a/js/modules/web_api.js b/js/modules/web_api.js index c92681b05..2e58c2013 100644 --- a/js/modules/web_api.js +++ b/js/modules/web_api.js @@ -1,6 +1,4 @@ -const WebSocket = require('websocket').w3cwebsocket; const fetch = require('node-fetch'); -const ProxyAgent = require('proxy-agent'); const { Agent } = require('https'); const FormData = require('form-data'); @@ -39,89 +37,6 @@ function _getString(thing) { return thing; } -function _b64ToUint6(nChr) { - return nChr > 64 && nChr < 91 - ? nChr - 65 - : nChr > 96 && nChr < 123 - ? nChr - 71 - : nChr > 47 && nChr < 58 - ? nChr + 4 - : nChr === 43 - ? 62 - : nChr === 47 - ? 63 - : 0; -} - -function _getStringable(thing) { - return ( - typeof thing === 'string' || - typeof thing === 'number' || - typeof thing === 'boolean' || - (thing === Object(thing) && - (_call(thing) === ArrayBufferToString || - _call(thing) === Uint8ArrayToString)) - ); -} - -function _ensureStringed(thing) { - if (_getStringable(thing)) { - return _getString(thing); - } else if (thing instanceof Array) { - const res = []; - for (let i = 0; i < thing.length; i += 1) { - res[i] = _ensureStringed(thing[i]); - } - return res; - } else if (thing === Object(thing)) { - const res = {}; - // eslint-disable-next-line guard-for-in, no-restricted-syntax - for (const key in thing) { - res[key] = _ensureStringed(thing[key]); - } - return res; - } else if (thing === null) { - return null; - } else if (thing === undefined) { - return undefined; - } - throw new Error(`unsure of how to jsonify object of type ${typeof thing}`); -} - -function _jsonThing(thing) { - return JSON.stringify(_ensureStringed(thing)); -} - -function _base64ToBytes(sBase64, nBlocksSize) { - const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ''); - const nInLen = sB64Enc.length; - const nOutLen = nBlocksSize - ? Math.ceil(((nInLen * 3 + 1) >> 2) / nBlocksSize) * nBlocksSize - : (nInLen * 3 + 1) >> 2; - const aBBytes = new ArrayBuffer(nOutLen); - const taBytes = new Uint8Array(aBBytes); - - for ( - let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; - nInIdx < nInLen; - nInIdx += 1 - ) { - nMod4 = nInIdx & 3; - nUint24 |= _b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4); - if (nMod4 === 3 || nInLen - nInIdx === 1) { - for ( - nMod3 = 0; - nMod3 < 3 && nOutIdx < nOutLen; - nMod3 += 1, nOutIdx += 1 - ) { - taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255; - } - nUint24 = 0; - } - } - return aBBytes; -} - function _validateResponse(response, schema) { try { // eslint-disable-next-line guard-for-in, no-restricted-syntax @@ -144,30 +59,6 @@ function _validateResponse(response, schema) { return true; } -function _createSocket(url, { certificateAuthority, proxyUrl, signature }) { - let requestOptions; - if (proxyUrl) { - requestOptions = { - ca: certificateAuthority, - agent: new ProxyAgent(proxyUrl), - }; - } else { - requestOptions = { - ca: certificateAuthority, - }; - } - // TODO: sign a timestamp - let headers; - if (signature) { - headers = { - signature, - }; - } - - // eslint-disable-next-line new-cap - return new WebSocket(url, null, null, headers, requestOptions); -} - const FIVE_MINUTES = 1000 * 60 * 5; const agents = { unauth: null, @@ -210,9 +101,7 @@ function _promiseAjax(providedUrl, options) { log.info(`Cycling agent for type ${cacheKey}`); } agents[cacheKey] = { - agent: proxyUrl - ? new ProxyAgent(proxyUrl) - : new Agent({ keepAlive: true }), + agent: new Agent({ keepAlive: true }), timestamp: Date.now(), }; } @@ -394,45 +283,12 @@ function HTTPError(message, providedCode, response, stack) { return e; } -const URL_CALLS = { - accounts: 'v1/accounts', - updateDeviceName: 'v1/accounts/name', - removeSignalingKey: 'v1/accounts/signaling_key', - attachment: 'v1/attachments', - deliveryCert: 'v1/certificate/delivery', - supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery', - devices: 'v1/devices', - keys: 'v2/keys', - messages: 'v1/messages', - profile: 'v1/profile', - signed: 'v2/keys/signed', -}; - module.exports = { initialize, }; // We first set up the data that won't change during this session of the app -function initialize({ - url, - cdnUrl, - certificateAuthority, - contentProxyUrl, - proxyUrl, -}) { - if (!is.string(url)) { - throw new Error('WebAPI.initialize: Invalid server url'); - } - if (!is.string(cdnUrl)) { - throw new Error('WebAPI.initialize: Invalid cdnUrl'); - } - if (!is.string(certificateAuthority)) { - throw new Error('WebAPI.initialize: Invalid certificateAuthority'); - } - if (!is.string(contentProxyUrl)) { - throw new Error('WebAPI.initialize: Invalid contentProxyUrl'); - } - +function initialize() { // Thanks to function-hoisting, we can put this return statement before all of the // below function definitions. return { @@ -442,413 +298,19 @@ function initialize({ // Then we connect to the server with user-specific information. This is the only API // exposed to the browser context, ensuring that it can't connect to arbitrary // locations. - function connect({ username: initialUsername, password: initialPassword }) { - let username = initialUsername; - let password = initialPassword; - + function connect() { // Thanks, function hoisting! return { - confirmCode, getAttachment, - getAvatar, - getDevices, - getKeysForNumber, - getKeysForNumberUnauth, - getMessageSocket, - getMyKeys, - getProfile, - getProfileUnauth, - getProvisioningSocket, getProxiedSize, - getSenderCertificate, makeProxiedRequest, putAttachment, putAvatar, - registerKeys, - registerSupportForUnauthenticatedDelivery, - removeSignalingKey, - requestVerificationSMS, - requestVerificationVoice, - sendMessages, - sendMessagesUnauth, - setSignedPreKey, - updateDeviceName, }; - function _ajax(param) { - if (!param.urlParameters) { - // eslint-disable-next-line no-param-reassign - param.urlParameters = ''; - } - return _outerAjax(null, { - certificateAuthority, - contentType: 'application/json; charset=utf-8', - data: param.jsonData && _jsonThing(param.jsonData), - host: url, - password, - path: URL_CALLS[param.call] + param.urlParameters, - proxyUrl, - responseType: param.responseType, - timeout: param.timeout, - type: param.httpType, - user: username, - validateResponse: param.validateResponse, - unauthenticated: param.unauthenticated, - accessKey: param.accessKey, - }).catch(e => { - const { code } = e; - if (code === 200) { - // happens sometimes when we get no response - // (TODO: Fix server to return 204? instead) - return null; - } - let message; - switch (code) { - case -1: - message = - 'Failed to connect to the server, please check your network connection.'; - break; - case 413: - message = 'Rate limit exceeded, please try again later.'; - break; - case 403: - message = 'Invalid code, please try again.'; - break; - case 417: - // TODO: This shouldn't be a thing?, but its in the API doc? - message = 'Number already registered.'; - break; - case 401: - message = - 'Invalid authentication, most likely someone re-registered and invalidated our registration.'; - break; - case 404: - message = 'Number is not registered.'; - break; - default: - message = - 'The server rejected our query, please file a bug report.'; - } - e.message = `${message} (original: ${e.message})`; - throw e; - }); - } - - function getSenderCertificate() { - return _ajax({ - call: 'deliveryCert', - httpType: 'GET', - responseType: 'json', - schema: { certificate: 'string' }, - }); - } - - function registerSupportForUnauthenticatedDelivery() { - return _ajax({ - call: 'supportUnauthenticatedDelivery', - httpType: 'PUT', - responseType: 'json', - }); - } - - function getProfile(number) { - return _ajax({ - call: 'profile', - httpType: 'GET', - urlParameters: `/${number}`, - responseType: 'json', - }); - } - function getProfileUnauth(number, { accessKey } = {}) { - return _ajax({ - call: 'profile', - httpType: 'GET', - urlParameters: `/${number}`, - responseType: 'json', - unauthenticated: true, - accessKey, - }); - } - - function getAvatar(path) { - // Using _outerAJAX, since it's not hardcoded to the Signal Server. Unlike our - // attachment CDN, it uses our self-signed certificate, so we pass it in. - return _outerAjax(`${cdnUrl}/${path}`, { - certificateAuthority, - contentType: 'application/octet-stream', - proxyUrl, - responseType: 'arraybuffer', - timeout: 0, - type: 'GET', - }); - } - - function requestVerificationSMS(number) { - return _ajax({ - call: 'accounts', - httpType: 'GET', - urlParameters: `/sms/code/${number}`, - }); - } - - function requestVerificationVoice(number) { - return _ajax({ - call: 'accounts', - httpType: 'GET', - urlParameters: `/voice/code/${number}`, - }); - } - - async function confirmCode( - number, - code, - newPassword, - registrationId, - deviceName, - options = {} - ) { - const { accessKey } = options; - const jsonData = { - supportsSms: false, - fetchesMessages: true, - registrationId, - unidentifiedAccessKey: accessKey - ? _btoa(_getString(accessKey)) - : undefined, - unrestrictedUnidentifiedAccess: false, - }; - - let call; - let urlPrefix; - let schema; - let responseType; - - if (deviceName) { - jsonData.name = deviceName; - call = 'devices'; - urlPrefix = '/'; - schema = { deviceId: 'number' }; - responseType = 'json'; - } else { - call = 'accounts'; - urlPrefix = '/code/'; - } - - // We update our saved username and password, since we're creating a new account - username = number; - password = newPassword; - - const response = await _ajax({ - call, - httpType: 'PUT', - urlParameters: urlPrefix + code, - jsonData, - responseType, - validateResponse: schema, - }); - - // From here on out, our username will be our phone number combined with device - username = `${number}.${response.deviceId || 1}`; - - return response; - } - - function updateDeviceName(deviceName) { - return _ajax({ - call: 'updateDeviceName', - httpType: 'PUT', - jsonData: { - deviceName, - }, - }); - } - - function removeSignalingKey() { - return _ajax({ - call: 'removeSignalingKey', - httpType: 'DELETE', - }); - } - - function getDevices() { - return _ajax({ - call: 'devices', - httpType: 'GET', - }); - } - - function registerKeys(genKeys) { - const keys = {}; - keys.identityKey = _btoa(_getString(genKeys.identityKey)); - keys.signedPreKey = { - keyId: genKeys.signedPreKey.keyId, - publicKey: _btoa(_getString(genKeys.signedPreKey.publicKey)), - signature: _btoa(_getString(genKeys.signedPreKey.signature)), - }; - - keys.preKeys = []; - let j = 0; - // eslint-disable-next-line guard-for-in, no-restricted-syntax - for (const i in genKeys.preKeys) { - keys.preKeys[j] = { - keyId: genKeys.preKeys[i].keyId, - publicKey: _btoa(_getString(genKeys.preKeys[i].publicKey)), - }; - j += 1; - } - - // This is just to make the server happy - // (v2 clients should choke on publicKey) - keys.lastResortKey = { keyId: 0x7fffffff, publicKey: _btoa('42') }; - - return _ajax({ - call: 'keys', - httpType: 'PUT', - jsonData: keys, - }); - } - - function setSignedPreKey(signedPreKey) { - return _ajax({ - call: 'signed', - httpType: 'PUT', - jsonData: { - keyId: signedPreKey.keyId, - publicKey: _btoa(_getString(signedPreKey.publicKey)), - signature: _btoa(_getString(signedPreKey.signature)), - }, - }); - } - - function getMyKeys() { - return _ajax({ - call: 'keys', - httpType: 'GET', - responseType: 'json', - validateResponse: { count: 'number' }, - }).then(res => res.count); - } - - function handleKeys(res) { - if (!Array.isArray(res.devices)) { - throw new Error('Invalid response'); - } - res.identityKey = _base64ToBytes(res.identityKey); - res.devices.forEach(device => { - if ( - !_validateResponse(device, { signedPreKey: 'object' }) || - !_validateResponse(device.signedPreKey, { - publicKey: 'string', - signature: 'string', - }) - ) { - throw new Error('Invalid signedPreKey'); - } - if (device.preKey) { - if ( - !_validateResponse(device, { preKey: 'object' }) || - !_validateResponse(device.preKey, { publicKey: 'string' }) - ) { - throw new Error('Invalid preKey'); - } - // eslint-disable-next-line no-param-reassign - device.preKey.publicKey = _base64ToBytes(device.preKey.publicKey); - } - // eslint-disable-next-line no-param-reassign - device.signedPreKey.publicKey = _base64ToBytes( - device.signedPreKey.publicKey - ); - // eslint-disable-next-line no-param-reassign - device.signedPreKey.signature = _base64ToBytes( - device.signedPreKey.signature - ); - }); - return res; - } - - function getKeysForNumber(number, deviceId = '*') { - return _ajax({ - call: 'keys', - httpType: 'GET', - urlParameters: `/${number}/${deviceId}`, - responseType: 'json', - validateResponse: { identityKey: 'string', devices: 'object' }, - }).then(handleKeys); - } - - function getKeysForNumberUnauth( - number, - deviceId = '*', - { accessKey } = {} - ) { - return _ajax({ - call: 'keys', - httpType: 'GET', - urlParameters: `/${number}/${deviceId}`, - responseType: 'json', - validateResponse: { identityKey: 'string', devices: 'object' }, - unauthenticated: true, - accessKey, - }).then(handleKeys); - } - - function sendMessagesUnauth( - destination, - messageArray, - timestamp, - silent, - online, - { accessKey } = {} - ) { - const jsonData = { messages: messageArray, timestamp }; - - if (silent) { - jsonData.silent = true; - } - if (online) { - jsonData.online = true; - } - - return _ajax({ - call: 'messages', - httpType: 'PUT', - urlParameters: `/${destination}`, - jsonData, - responseType: 'json', - unauthenticated: true, - accessKey, - }); - } - - function sendMessages( - destination, - messageArray, - timestamp, - silent, - online - ) { - const jsonData = { messages: messageArray, timestamp }; - - if (silent) { - jsonData.silent = true; - } - if (online) { - jsonData.online = true; - } - - return _ajax({ - call: 'messages', - httpType: 'PUT', - urlParameters: `/${destination}`, - jsonData, - responseType: 'json', - }); - } - function getAttachment(fileUrl) { return _outerAjax(fileUrl, { contentType: 'application/octet-stream', - proxyUrl, responseType: 'arraybuffer', timeout: 0, type: 'GET', @@ -884,7 +346,7 @@ function initialize({ const result = await _outerAjax(url, { processData: false, responseType: 'arraybufferwithdetails', - proxyUrl: contentProxyUrl, + proxyUrl: '', type: 'HEAD', disableLogs: true, }); @@ -912,38 +374,12 @@ function initialize({ return _outerAjax(url, { processData: false, responseType: returnArrayBuffer ? 'arraybufferwithdetails' : null, - proxyUrl: contentProxyUrl, + proxyUrl: '', type: 'GET', redirect: 'follow', disableLogs: true, headers, }); } - - function getMessageSocket() { - log.info('opening message socket', url); - const fixedScheme = url - .replace('https://', 'wss://') - .replace('http://', 'ws://'); - const login = encodeURIComponent(username); - const pass = encodeURIComponent(password); - - return _createSocket( - `${fixedScheme}/v1/websocket/?login=${login}&password=${pass}&agent=OWD`, - { certificateAuthority, proxyUrl } - ); - } - - function getProvisioningSocket() { - log.info('opening provisioning socket', url); - const fixedScheme = url - .replace('https://', 'wss://') - .replace('http://', 'ws://'); - - return _createSocket( - `${fixedScheme}/v1/websocket/provisioning/?agent=OWD`, - { certificateAuthority, proxyUrl } - ); - } } } diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 737bf363c..e037305c5 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -28,7 +28,6 @@ const ARCHIVE_AGE = 7 * 24 * 60 * 60 * 1000; function AccountManager(username, password) { - // this.server = window.WebAPI.connect({ username, password }); this.pending = Promise.resolve(); } @@ -48,12 +47,8 @@ AccountManager.prototype = new textsecure.EventTarget(); AccountManager.prototype.extend({ constructor: AccountManager, - requestVoiceVerification(number) { - // return this.server.requestVerificationVoice(number); - }, - requestSMSVerification(number) { - // return this.server.requestVerificationSMS(number); - }, + requestVoiceVerification(number) {}, + requestSMSVerification(number) {}, async encryptDeviceName(name, providedIdentityKey) { if (!name) { return null; @@ -98,23 +93,13 @@ return name; }, async maybeUpdateDeviceName() { - const isNameEncrypted = textsecure.storage.user.getDeviceNameEncrypted(); - if (isNameEncrypted) { - return; - } - const deviceName = await textsecure.storage.user.getDeviceName(); - const base64 = await this.encryptDeviceName(deviceName); - - await this.server.updateDeviceName(base64); + throw new Error('Signal method called: maybeUpdateDeviceName'); }, async deviceNameIsEncrypted() { await textsecure.storage.user.setDeviceNameEncrypted(); }, async maybeDeleteSignalingKey() { - const key = await textsecure.storage.user.getSignalingKey(); - if (key) { - await this.server.removeSignalingKey(); - } + throw new Error('Signal method called: maybeDeleteSignalingKey'); }, registerSingleDevice(mnemonic, mnemonicLanguage, profileName) { const createAccount = this.createAccount.bind(this); @@ -212,19 +197,7 @@ 'account_manager: registerSecondDevice has not been implemented!' ); }, - refreshPreKeys() { - // const generateKeys = this.generateKeys.bind(this, 0); - // const registerKeys = this.server.registerKeys.bind(this.server); - // return this.queueTask(() => - // this.server.getMyKeys().then(preKeyCount => { - // window.log.info(`prekey count ${preKeyCount}`); - // if (preKeyCount < 10) { - // return generateKeys().then(registerKeys); - // } - // return null; - // }) - // ); - }, + refreshPreKeys() {}, rotateSignedPreKey() { return this.queueTask(() => { const signedKeyId = textsecure.storage.get('signedKeyId', 1); @@ -512,7 +485,6 @@ keyId: res.keyId, publicKey: res.keyPair.pubKey, signature: res.signature, - // server.registerKeys doesn't use keyPair, confirmKeys does keyPair: res.keyPair, }; }) diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index ab0f9afb9..bbf753c57 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -32,14 +32,7 @@ function MessageReceiver(username, password, signalingKey, options = {}) { this.signalingKey = signalingKey; this.username = username; this.password = password; - this.server = WebAPI.connect({ username, password }); - - if (!options.serverTrustRoot) { - throw new Error('Server trust root is required!'); - } - this.serverTrustRoot = window.Signal.Crypto.base64ToArrayBuffer( - options.serverTrustRoot - ); + this.server = WebAPI.connect(); const address = libsignal.SignalProtocolAddress.fromString(username); this.number = address.getName(); @@ -110,31 +103,6 @@ MessageReceiver.prototype.extend({ feed.on('rssMessage', this.handleUnencryptedMessage.bind(this)); }); - // TODO: Rework this socket stuff to work with online messaging - const useWebSocket = false; - if (useWebSocket) { - if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { - this.socket.close(); - this.wsr.close(); - } - // initialize the socket and start listening for messages - this.socket = this.server.getMessageSocket(); - this.socket.onclose = this.onclose.bind(this); - this.socket.onerror = this.onerror.bind(this); - this.socket.onopen = this.onopen.bind(this); - this.wsr = new WebSocketResource(this.socket, { - handleRequest: this.handleRequest.bind(this), - keepalive: { - path: '/v1/keepalive', - disconnect: true, - }, - }); - - // Because sometimes the socket doesn't properly emit its close event - this._onClose = this.onclose.bind(this); - this.wsr.addEventListener('close', this._onClose); - } - // Ensures that an immediate 'empty' event from the websocket will fire only after // all cached envelopes are processed. this.incoming = [this.pending]; @@ -163,29 +131,11 @@ MessageReceiver.prototype.extend({ this.stoppingProcessing = true; return this.close(); }, - shutdown() { - if (this.socket) { - this.socket.onclose = null; - this.socket.onerror = null; - this.socket.onopen = null; - this.socket = null; - } - - if (this.wsr) { - this.wsr.removeEventListener('close', this._onClose); - this.wsr = null; - } - }, + shutdown() {}, async close() { window.log.info('MessageReceiver.close()'); this.calledClose = true; - // Our WebSocketResource instance will close the socket and emit a 'close' event - // if the socket doesn't emit one quickly enough. - if (this.wsr) { - this.wsr.close(3000, 'called close'); - } - // stop polling all open group rooms if (lokiPublicChatAPI) { await lokiPublicChatAPI.close(); @@ -220,27 +170,6 @@ MessageReceiver.prototype.extend({ 'calledClose:', this.calledClose ); - // TODO: handle properly - // this.shutdown(); - - // if (this.calledClose) { - // return Promise.resolve(); - // } - // if (ev.code === 3000) { - // return Promise.resolve(); - // } - // if (ev.code === 3001) { - // this.onEmpty(); - // } - // // possible 403 or network issue. Make an request to confirm - // return this.server - // .getDevices(this.number) - // .then(this.connect.bind(this)) // No HTTP error? Reconnect - // .catch(e => { - // const event = new Event('error'); - // event.error = e; - // return this.dispatchAndWait(event); - // }); }, handleRequest(request, options) { const { onSuccess, onFailure } = options; @@ -637,9 +566,7 @@ MessageReceiver.prototype.extend({ ? WebSocket.OPEN : WebSocket.CLOSED; } - if (this.socket) { - return this.socket.readyState; - } else if (this.hasConnected) { + if (this.hasConnected) { return WebSocket.CLOSED; } return -1; diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 369f8ecee..5feb6d648 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -178,10 +178,6 @@ OutgoingMessage.prototype = { return false; }) ); - // TODO: check if still applicable - // if (updateDevices === undefined) { - // return this.server.getKeysForNumber(number, '*').then(handleResult); - // } let promise = Promise.resolve(true); updateDevices.forEach(device => { promise = promise.then(() => diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index 8775c9987..a0bfe4267 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -184,8 +184,8 @@ Message.prototype = { }, }; -function MessageSender(username, password) { - this.server = WebAPI.connect({ username, password }); +function MessageSender() { + this.server = WebAPI.connect(); this.pendingMessages = {}; } @@ -595,16 +595,8 @@ MessageSender.prototype = { ); }, - async getProfile(number, { accessKey } = {}) { - if (accessKey) { - return this.server.getProfileUnauth(number, { accessKey }); - } - - return this.server.getProfile(number); - }, - - getAvatar(path) { - return this.server.getAvatar(path); + async getProfile() { + throw new Error('Signal code called. This needs to be removed'); }, uploadAvatar(attachment) { @@ -1338,7 +1330,6 @@ textsecure.MessageSender = function MessageSenderWrapper(username, password) { this.leaveGroup = sender.leaveGroup.bind(sender); this.sendSyncMessage = sender.sendSyncMessage.bind(sender); this.getProfile = sender.getProfile.bind(sender); - this.getAvatar = sender.getAvatar.bind(sender); this.uploadAvatar = sender.uploadAvatar.bind(sender); this.syncReadMessages = sender.syncReadMessages.bind(sender); this.syncVerification = sender.syncVerification.bind(sender); diff --git a/libtextsecure/test/fake_web_api.js b/libtextsecure/test/fake_web_api.js index 29e2a932f..3c38a806d 100644 --- a/libtextsecure/test/fake_web_api.js +++ b/libtextsecure/test/fake_web_api.js @@ -1,56 +1,11 @@ window.setImmediate = window.nodeSetImmediate; -const getKeysForNumberMap = {}; -const messagesSentMap = {}; - const fakeCall = () => Promise.resolve(); const fakeAPI = { - confirmCode: fakeCall, getAttachment: fakeCall, - getAvatar: fakeCall, - getDevices: fakeCall, - // getKeysForNumber: fakeCall, - getMessageSocket: fakeCall, - getMyKeys: fakeCall, - getProfile: fakeCall, - getProvisioningSocket: fakeCall, putAttachment: fakeCall, - registerKeys: fakeCall, - requestVerificationSMS: fakeCall, - requestVerificationVoice: fakeCall, - // sendMessages: fakeCall, - setSignedPreKey: fakeCall, - - getKeysForNumber(number) { - const res = getKeysForNumberMap[number]; - if (res !== undefined) { - delete getKeysForNumberMap[number]; - return Promise.resolve(res); - } - throw new Error('getKeysForNumber of unknown/used number'); - }, - - sendMessages(destination, messageArray) { - for (let i = 0, max = messageArray.length; i < max; i += 1) { - const msg = messageArray[i]; - if ( - (msg.type !== 1 && msg.type !== 3) || - msg.destinationDeviceId === undefined || - msg.destinationRegistrationId === undefined || - msg.body === undefined || - msg.timestamp === undefined || - msg.relay !== undefined || - msg.destination !== undefined - ) { - throw new Error('Invalid message'); - } - - messagesSentMap[ - `${destination}.${messageArray[i].destinationDeviceId}` - ] = msg; - } - }, + putAvatar: fakeCall, }; window.WebAPI = { diff --git a/package.json b/package.json index 0e64f76ec..2b0964733 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,6 @@ "os-locale": "2.1.0", "pify": "3.0.0", "protobufjs": "6.8.6", - "proxy-agent": "3.0.3", "rc-slider": "^8.7.1", "react": "16.8.3", "react-contextmenu": "2.11.0", @@ -116,8 +115,7 @@ "tmp": "0.0.33", "to-arraybuffer": "1.0.1", "underscore": "1.9.0", - "uuid": "3.3.2", - "websocket": "1.0.28" + "uuid": "3.3.2" }, "devDependencies": { "@types/chai": "4.1.2", diff --git a/preload.js b/preload.js index ed8800aa8..d4130d90a 100644 --- a/preload.js +++ b/preload.js @@ -292,13 +292,7 @@ window.nodeSetImmediate = setImmediate; const { initialize: initializeWebAPI } = require('./js/modules/web_api'); -window.WebAPI = initializeWebAPI({ - url: config.serverUrl, - cdnUrl: config.cdnUrl, - certificateAuthority: config.certificateAuthority, - contentProxyUrl: config.contentProxyUrl, - proxyUrl: config.proxyUrl, -}); +window.WebAPI = initializeWebAPI(); window.seedNodeList = JSON.parse(config.seedNodeList); const LokiSnodeAPI = require('./js/modules/loki_snode_api'); diff --git a/yarn.lock b/yarn.lock index ce4913277..cb0fcfaab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -516,20 +516,6 @@ address@^1.0.1: resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== -agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - -agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== - dependencies: - es6-promisify "^5.0.0" - ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" @@ -900,11 +886,6 @@ ast-types@0.9.11: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.11.tgz#371177bb59232ff5ceaa1d09ee5cad705b1a5aa9" integrity sha1-NxF3u1kjL/XOqh0J7lytcFsaWqk= -ast-types@0.x.x: - version "0.13.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" - integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== - ast-types@^0.7.2: version "0.7.8" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" @@ -2511,11 +2492,6 @@ dashdash@1.14.1, dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" - integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== - dateformat@~1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" @@ -2529,7 +2505,7 @@ debug-log@^1.0.1: resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" integrity sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8= -debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2686,15 +2662,6 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= -degenerator@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" - integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= - dependencies: - ast-types "0.x.x" - escodegen "1.x.x" - esprima "3.x.x" - del@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" @@ -3202,13 +3169,6 @@ es6-promise@^4.0.3, es6-promise@^4.2.4: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - escape-goat@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" @@ -3229,7 +3189,7 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@1.x.x, escodegen@^1.9.1: +escodegen@^1.9.1: version "1.14.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== @@ -3368,11 +3328,6 @@ espree@^3.5.2: acorn "^5.5.0" acorn-jsx "^3.0.0" -esprima@3.x.x: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - esprima@^2.1.0, esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -3703,7 +3658,7 @@ file-type@^3.1.0: resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= -file-uri-to-path@1, file-uri-to-path@1.0.0: +file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== @@ -4024,14 +3979,6 @@ fstream@^1.0.0, fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -ftp@~0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4117,18 +4064,6 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-uri@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.4.tgz#d4937ab819e218d4cb5ae18e4f5962bef169cc6a" - integrity sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q== - dependencies: - data-uri-to-buffer "1" - debug "2" - extend "~3.0.2" - file-uri-to-path "1" - ftp "~0.3.10" - readable-stream "2" - get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4737,17 +4672,6 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@1.7.3, http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" @@ -4766,19 +4690,22 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + "http-parser-js@>=0.4.0 <0.4.11": version "0.4.10" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" - http-proxy-middleware@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -4812,22 +4739,6 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - -https-proxy-agent@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz#b8c286433e87602311b01c8ea34413d856a4af81" - integrity sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - hyphenate-style-name@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" @@ -5009,7 +4920,7 @@ ip-regex@^1.0.1: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0= -ip@1.1.5, ip@^1.1.0, ip@^1.1.5: +ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -6057,7 +5968,7 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2: +lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -6550,7 +6461,7 @@ mz@^2.3.1: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.10.0, nan@^2.11.0, nan@^2.12.1, nan@^2.14.0: +nan@^2.10.0, nan@^2.12.1, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -6608,11 +6519,6 @@ neo-async@^2.5.0, neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== -netmask@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" - integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= - nise@^1.2.0: version "1.5.3" resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" @@ -7164,31 +7070,6 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz#115b1e58f92576cac2eba718593ca7b0e37de2ad" - integrity sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ== - dependencies: - agent-base "^4.2.0" - debug "^4.1.1" - get-uri "^2.0.0" - http-proxy-agent "^2.1.0" - https-proxy-agent "^3.0.0" - pac-resolver "^3.0.0" - raw-body "^2.2.0" - socks-proxy-agent "^4.0.1" - -pac-resolver@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" - integrity sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA== - dependencies: - co "^4.6.0" - degenerator "^1.0.4" - ip "^1.1.5" - netmask "^1.0.6" - thunkify "^2.1.2" - package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -7903,25 +7784,6 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" -proxy-agent@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.3.tgz#1c1a33db60ef5f2e9e35b876fd63c2bc681c611d" - integrity sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA== - dependencies: - agent-base "^4.2.0" - debug "^3.1.0" - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - lru-cache "^4.1.2" - pac-proxy-agent "^3.0.0" - proxy-from-env "^1.0.0" - socks-proxy-agent "^4.0.1" - -proxy-from-env@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -8114,16 +7976,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" - integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - dependencies: - bytes "3.1.0" - http-errors "1.7.3" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@~2.1.5: version "2.1.7" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" @@ -8495,7 +8347,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8508,7 +8360,7 @@ read-pkg@^2.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.1.x, readable-stream@^1.1.8, readable-stream@~1.1.9: +readable-stream@^1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -9341,11 +9193,6 @@ slide@^1.1.5: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= -smart-buffer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" - integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -9396,22 +9243,6 @@ sockjs@0.3.19: faye-websocket "^0.10.0" uuid "^3.0.1" -socks-proxy-agent@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" - integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== - dependencies: - agent-base "~4.2.1" - socks "~2.3.2" - -socks@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" - integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== - dependencies: - ip "1.1.5" - smart-buffer "^4.1.0" - sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -10065,11 +9896,6 @@ through@2, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -thunkify@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" - integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= - thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -10908,16 +10734,6 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== -websocket@1.0.28: - version "1.0.28" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3" - integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA== - dependencies: - debug "^2.2.0" - nan "^2.11.0" - typedarray-to-buffer "^3.1.5" - yaeti "^0.0.6" - wgxpath@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690" @@ -11076,11 +10892,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= - xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -11103,11 +10914,6 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= - yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" From eed0ea058c7c7c837e302c4cf579b32e5c2ffbce Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 13:45:08 +1100 Subject: [PATCH 09/30] Removed other dependencies --- package.json | 5 ++--- ts/util/lint/linter.ts | 1 - yarn.lock | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2b0964733..f795df29c 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,6 @@ "dependencies": { "@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#b10f232fac62ba7f8775c9e086bb5558fe7d948b", "@sindresorhus/is": "0.8.0", - "@types/dompurify": "^2.0.0", - "@types/rc-slider": "^8.6.5", "backbone": "1.3.3", "blob-util": "1.3.0", "blueimp-canvas-to-blob": "3.14.0", @@ -111,7 +109,6 @@ "rimraf": "2.6.2", "semver": "5.4.1", "tar": "4.4.8", - "testcheck": "1.0.0-rc.2", "tmp": "0.0.33", "to-arraybuffer": "1.0.1", "underscore": "1.9.0", @@ -122,6 +119,7 @@ "@types/classnames": "2.2.3", "@types/color": "^3.0.0", "@types/config": "0.0.34", + "@types/dompurify": "^2.0.0", "@types/electron-is-dev": "^1.1.1", "@types/filesize": "3.6.0", "@types/fs-extra": "5.0.5", @@ -132,6 +130,7 @@ "@types/mocha": "5.0.0", "@types/pify": "3.0.2", "@types/qs": "6.5.1", + "@types/rc-slider": "^8.6.5", "@types/react": "16.8.5", "@types/react-dom": "16.8.2", "@types/react-portal": "^4.0.2", diff --git a/ts/util/lint/linter.ts b/ts/util/lint/linter.ts index a1ad506f7..a0773fbc6 100644 --- a/ts/util/lint/linter.ts +++ b/ts/util/lint/linter.ts @@ -160,7 +160,6 @@ const excludedFiles = [ '^node_modules/spectron/*', '^node_modules/style-loader/*', '^node_modules/svgo/*', - '^node_modules/testcheck/*', '^node_modules/text-encoding/*', '^node_modules/tinycolor2/*', '^node_modules/to-ast/*', diff --git a/yarn.lock b/yarn.lock index cb0fcfaab..564421fe4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9841,7 +9841,7 @@ test-exclude@^4.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -testcheck@1.0.0-rc.2, testcheck@^1.0.0-rc: +testcheck@^1.0.0-rc: version "1.0.0-rc.2" resolved "https://registry.yarnpkg.com/testcheck/-/testcheck-1.0.0-rc.2.tgz#11356a25b84575efe0b0857451e85b5fa74ee4e4" integrity sha1-ETVqJbhFde/gsIV0UehbX6dO5OQ= From b16da2c5f56c4444fdfab3730385ccf52f013334 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 31 Mar 2020 14:23:19 +1100 Subject: [PATCH 10/30] Linting --- js/modules/web_api.js | 2 -- libtextsecure/message_receiver.js | 1 - 2 files changed, 3 deletions(-) diff --git a/js/modules/web_api.js b/js/modules/web_api.js index 2e58c2013..0b89c3804 100644 --- a/js/modules/web_api.js +++ b/js/modules/web_api.js @@ -2,8 +2,6 @@ const fetch = require('node-fetch'); const { Agent } = require('https'); const FormData = require('form-data'); -const is = require('@sindresorhus/is'); - /* global Buffer, setTimeout, log, _, lokiFileServerAPI */ /* eslint-disable more/no-then, no-bitwise, no-nested-ternary */ diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index bbf753c57..94b14fc7b 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -11,7 +11,6 @@ /* global HttpResource: false */ /* global ContactBuffer: false */ /* global GroupBuffer: false */ -/* global WebSocketResource: false */ /* global lokiPublicChatAPI: false */ /* global lokiMessageAPI: false */ /* global feeds: false */ From b39ac9f44e71b7f35ccd3e89c1f678a29b3b190a Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 31 Mar 2020 17:36:51 +1100 Subject: [PATCH 11/30] Close the password window once mainwindow is focused password was closed to early and no windows were shown which triggered the app stop --- main.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/main.js b/main.js index a555c3b7c..e9393b221 100644 --- a/main.js +++ b/main.js @@ -346,6 +346,10 @@ async function createWindow() { mainWindow.on('focus', () => { mainWindow.flashFrame(false); + if (passwordWindow) { + passwordWindow.close(); + passwordWindow = null; + } }); if (config.environment === 'test') { @@ -1018,11 +1022,6 @@ ipc.on('password-window-login', async (event, passPhrase) => { const passwordAttempt = true; await showMainWindow(passPhrase, passwordAttempt); sendResponse(); - - if (passwordWindow) { - passwordWindow.close(); - passwordWindow = null; - } } catch (e) { const localisedError = locale.messages.invalidPassword.message; sendResponse(localisedError || 'Invalid password'); From e78426de99e539842596881c86b3f5ce319250bf Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Wed, 1 Apr 2020 21:13:48 -0700 Subject: [PATCH 12/30] just in case onChange isnt triggered --- ts/components/session/SessionInput.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ts/components/session/SessionInput.tsx b/ts/components/session/SessionInput.tsx index 11432e80a..edd8c64ed 100644 --- a/ts/components/session/SessionInput.tsx +++ b/ts/components/session/SessionInput.tsx @@ -61,6 +61,10 @@ export class SessionInput extends React.PureComponent { className={classNames( enableShowHide ? 'session-input-floating-label-show-hide' : '' )} + // just incase onChange isn't triggered + onBlur={e => { + this.updateInputValue(e); + }} onKeyPress={event => { event.persist(); if (event.key === 'Enter' && this.props.onEnterPressed) { From 34a1fdbfec0e216335bed5373870974bff23a5d4 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Wed, 1 Apr 2020 21:43:07 -0700 Subject: [PATCH 13/30] add onBlur incase onChange doesn't fire --- ts/components/session/SessionIdEditable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ts/components/session/SessionIdEditable.tsx b/ts/components/session/SessionIdEditable.tsx index dc4bab8bc..6fca9f63e 100644 --- a/ts/components/session/SessionIdEditable.tsx +++ b/ts/components/session/SessionIdEditable.tsx @@ -45,6 +45,7 @@ export class SessionIdEditable extends React.PureComponent { spellCheck={false} onKeyDown={this.handleKeyDown} onChange={this.handleChange} + onBlur={this.handleChange} value={value || text} maxLength={maxLength} /> From 86ab440bb8832048b72934eb2606792b771cc6ec Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Wed, 1 Apr 2020 23:11:15 -0700 Subject: [PATCH 14/30] make minimum window height, so integration tests can clicked on closed group button --- main.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main.js b/main.js index 66ac9c46f..d398aa1ae 100644 --- a/main.js +++ b/main.js @@ -199,9 +199,11 @@ function captureClicks(window) { } const DEFAULT_WIDTH = 880; -const DEFAULT_HEIGHT = 720; +// add contact button needs to be visible (on HiDpi screens?) +// otherwise integration test fail +const DEFAULT_HEIGHT = 820; const MIN_WIDTH = 880; -const MIN_HEIGHT = 720; +const MIN_HEIGHT = 820; const BOUNDS_BUFFER = 100; function isVisible(window, bounds) { From 1e863e5a8a1e96e115000cc0fd5b3285e1a35d2a Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 2 Apr 2020 00:16:54 -0700 Subject: [PATCH 15/30] bump int test timeout from 5s to 10s, add test-integration-parts which is more reliable --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e4b371f68..ce16bbb39 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "test-lib-view": "NODE_ENV=test-lib yarn run start", "test-loki-view": "NODE_ENV=test-loki yarn run start", "test-electron": "yarn grunt test", - "test-integration": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 5000 integration_test/integration_test.js", + "test-integration": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js", + "test-integration-parts": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'registration' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'openGroup' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'addFriends' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'linkDevice' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'closedGroup'", "test-node": "mocha --recursive --exit test/app test/modules ts/test libloki/test/node", "eslint": "eslint --cache .", "eslint-fix": "eslint --fix .", From f5f9614680c073e362538d02491facf14c4caf63 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 2 Apr 2020 00:17:20 -0700 Subject: [PATCH 16/30] I looked up how to xpath byId, thought I'd include it --- integration_test/page-objects/common.page.js | 1 + 1 file changed, 1 insertion(+) diff --git a/integration_test/page-objects/common.page.js b/integration_test/page-objects/common.page.js index 9a3a77573..e5374f6d6 100644 --- a/integration_test/page-objects/common.page.js +++ b/integration_test/page-objects/common.page.js @@ -13,6 +13,7 @@ module.exports = { `//input[contains(@placeholder, "${placeholder}")]`, textAreaWithPlaceholder: placeholder => `//textarea[contains(@placeholder, "${placeholder}")]`, + byId: id => `//*[@id="${id}"]`, divWithClass: classname => `//div[contains(@class, "${classname}")]`, divWithClassAndText: (classname, text) => module.exports.objWithClassAndText('div', classname, text), From 3d93c0592949cffa291cb5db3b610b5a60efa993 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 2 Apr 2020 00:18:49 -0700 Subject: [PATCH 17/30] setValueWrapper refactor, fix process killing on macOs, remove unneeded resolves --- integration_test/common.js | 103 ++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/integration_test/common.js b/integration_test/common.js index ee8d35f98..306ae9bf1 100644 --- a/integration_test/common.js +++ b/integration_test/common.js @@ -50,6 +50,38 @@ module.exports = { return new Promise(resolve => setTimeout(resolve, ms)); }, + // a wrapper to work around electron/spectron bug + async setValueWrapper(app, selector, value) { + await app.client.element(selector).click(); + // keys, setValue and addValue hang on certain platforms + // could put a branch here to use one of those + // if we know what platforms are good and which ones are broken + await app.client.execute( + (slctr, val) => { + // eslint-disable-next-line no-undef + const iter = document.evaluate( + slctr, + // eslint-disable-next-line no-undef + document, + null, + // eslint-disable-next-line no-undef + XPathResult.UNORDERED_NODE_ITERATOR_TYPE, + null + ); + const elem = iter.iterateNext(); + if (elem) { + elem.value = val; + } else { + console.error('Cant find', slctr, elem, iter); + } + }, + selector, + value + ); + // let session js detect the text change + await app.client.element(selector).click(); + }, + async startApp(environment = 'test-integration-session') { const env = environment.startsWith('test-integration') ? 'test-integration' @@ -92,16 +124,16 @@ module.exports = { async stopApp(app1) { if (app1 && app1.isRunning()) { await app1.stop(); - return Promise.resolve(); } - return Promise.resolve(); }, async killallElectron() { + // rtharp - my 2nd client on MacOs needs: pkill -f "node_modules/.bin/electron" + // node_modules/electron/dist/electron is node_modules/electron/dist/Electron.app on MacOS const killStr = process.platform === 'win32' ? 'taskkill /im electron.exe /t /f' - : 'pkill -f "node_modules/electron/dist/electron"'; + : 'pkill -f "node_modules/electron/dist/electron" | pkill -f "node_modules/.bin/electron"'; return new Promise(resolve => { exec(killStr, (err, stdout, stderr) => { if (err) { @@ -145,23 +177,24 @@ module.exports = { stubOpenGroups = false, env = 'test-integration-session', }) { - const app1 = await this.startAndAssureCleanedApp(env); + const app = await this.startAndAssureCleanedApp(env); if (stubSnode) { await this.startStubSnodeServer(); - this.stubSnodeCalls(app1); + this.stubSnodeCalls(app); } if (stubOpenGroups) { - this.stubOpenGroupsCalls(app1); + this.stubOpenGroupsCalls(app); } if (mnemonic && displayName) { - await this.restoreFromMnemonic(app1, mnemonic, displayName); + await this.restoreFromMnemonic(app, mnemonic, displayName); + // not sure we need this - rtharp. await this.timeout(2000); } - return app1; + return app; }, async startAndStub2(props) { @@ -173,18 +206,23 @@ module.exports = { return app2; }, - async restoreFromMnemonic(app1, mnemonic, displayName) { - await app1.client.element(RegistrationPage.registrationTabSignIn).click(); - await app1.client.element(RegistrationPage.restoreFromSeedMode).click(); - await app1.client - .element(RegistrationPage.recoveryPhraseInput) - .setValue(mnemonic); - await app1.client - .element(RegistrationPage.displayNameInput) - .setValue(displayName); - - await app1.client.element(RegistrationPage.continueSessionButton).click(); - await app1.client.waitForExist( + async restoreFromMnemonic(app, mnemonic, displayName) { + await app.client.element(RegistrationPage.registrationTabSignIn).click(); + await app.client.element(RegistrationPage.restoreFromSeedMode).click(); + await this.setValueWrapper( + app, + RegistrationPage.recoveryPhraseInput, + mnemonic + ); + + await this.setValueWrapper( + app, + RegistrationPage.displayNameInput, + displayName + ); + + await app.client.element(RegistrationPage.continueSessionButton).click(); + await app.client.waitForExist( RegistrationPage.conversationListContainer, 4000 ); @@ -214,9 +252,11 @@ module.exports = { await app1.client.element(ConversationPage.contactsButtonSection).click(); await app1.client.element(ConversationPage.addContactButton).click(); - await app1.client - .element(ConversationPage.sessionIDInput) - .setValue(this.TEST_PUBKEY2); + await this.setValueWrapper( + app1, + ConversationPage.sessionIDInput, + this.TEST_PUBKEY2 + ); await app1.client.element(ConversationPage.nextButton).click(); await app1.client.waitForExist( ConversationPage.sendFriendRequestTextarea, @@ -224,9 +264,11 @@ module.exports = { ); // send a text message to that user (will be a friend request) - await app1.client - .element(ConversationPage.sendFriendRequestTextarea) - .setValue(textMessage); + await this.setValueWrapper( + app1, + ConversationPage.sendFriendRequestTextarea, + textMessage + ); await app1.client.keys('Enter'); await app1.client.waitForExist( ConversationPage.existingFriendRequestText(textMessage), @@ -284,9 +326,12 @@ module.exports = { // next trigger the link request from the app2 with the app1 pubkey await app2.client.element(RegistrationPage.registrationTabSignIn).click(); await app2.client.element(RegistrationPage.linkDeviceMode).click(); - await app2.client - .element(RegistrationPage.textareaLinkDevicePubkey) - .setValue(this.TEST_PUBKEY1); + + await this.setValueWrapper( + app2, + RegistrationPage.textareaLinkDevicePubkey, + this.TEST_PUBKEY1 + ); await app2.client.element(RegistrationPage.linkDeviceTriggerButton).click(); await app1.client.waitForExist(RegistrationPage.toastWrapper, 7000); let secretWordsapp1 = await app1.client From 0bffccb44120b6bd10bdbb70d398704932d478e0 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 2 Apr 2020 00:19:25 -0700 Subject: [PATCH 18/30] setValueWrapper refactor, add group prefix for easier grepping, DRY --- integration_test/add_friends_test.js | 12 +++-- integration_test/closed_group_test.js | 10 ++-- integration_test/link_device_test.js | 4 +- integration_test/open_group_test.js | 78 ++++++++++++++------------- integration_test/registration_test.js | 10 ++-- 5 files changed, 62 insertions(+), 52 deletions(-) diff --git a/integration_test/add_friends_test.js b/integration_test/add_friends_test.js index a86dbf1ec..aa8c04d4d 100644 --- a/integration_test/add_friends_test.js +++ b/integration_test/add_friends_test.js @@ -38,7 +38,7 @@ describe('Add friends', function() { await common.stopStubSnodeServer(); }); - it('can add a friend by sessionID', async () => { + it('addFriends: can add a friend by sessionID', async () => { const textMessage = common.generateSendMessageText(); await app.client.element(ConversationPage.contactsButtonSection).click(); @@ -46,13 +46,16 @@ describe('Add friends', function() { await app.client.isExisting(ConversationPage.leftPaneOverlay).should .eventually.be.true; - await app.client - .element(ConversationPage.sessionIDInput) - .setValue(common.TEST_PUBKEY2); + await common.setValueWrapper( + app, + ConversationPage.sessionIDInput, + common.TEST_PUBKEY2 + ); await app.client .element(ConversationPage.sessionIDInput) .getValue() .should.eventually.equal(common.TEST_PUBKEY2); + await app.client.element(ConversationPage.nextButton).click(); await app.client.waitForExist( ConversationPage.sendFriendRequestTextarea, @@ -68,6 +71,7 @@ describe('Add friends', function() { ConversationPage.existingFriendRequestText(textMessage), 1000 ); + // assure friend request message has been sent await common.timeout(3000); await app.client.isExisting(ConversationPage.retrySendButton).should diff --git a/integration_test/closed_group_test.js b/integration_test/closed_group_test.js index 42db71fbc..c937c97ff 100644 --- a/integration_test/closed_group_test.js +++ b/integration_test/closed_group_test.js @@ -23,14 +23,16 @@ describe('Closed groups', function() { await common.stopStubSnodeServer(); }); - it('can create a closed group with a friend and send/receive a message', async () => { + it('closedGroup: can create a closed group with a friend and send/receive a message', async () => { await app.client.element(ConversationPage.globeButtonSection).click(); await app.client.element(ConversationPage.createClosedGroupButton).click(); // fill the groupname - await app.client - .element(ConversationPage.closedGroupNameTextarea) - .setValue(common.VALID_CLOSED_GROUP_NAME1); + await common.setValueWrapper( + app, + ConversationPage.closedGroupNameTextarea, + common.VALID_CLOSED_GROUP_NAME1 + ); await app.client .element(ConversationPage.closedGroupNameTextarea) .getValue() diff --git a/integration_test/link_device_test.js b/integration_test/link_device_test.js index 14e60af03..a206a5e9c 100644 --- a/integration_test/link_device_test.js +++ b/integration_test/link_device_test.js @@ -36,11 +36,11 @@ describe('Link Device', function() { await common.stopStubSnodeServer(); }); - it('link two desktop devices', async () => { + it('linkDevice: link two desktop devices', async () => { await common.linkApp2ToApp(app, app2); }); - it('unlink two devices', async () => { + it('linkDevice: unlink two devices', async () => { await common.linkApp2ToApp(app, app2); await common.timeout(1000); await common.triggerUnlinkApp2FromApp(app, app2); diff --git a/integration_test/open_group_test.js b/integration_test/open_group_test.js index 486a5256b..9910fd68e 100644 --- a/integration_test/open_group_test.js +++ b/integration_test/open_group_test.js @@ -23,25 +23,25 @@ describe('Open groups', function() { await common.killallElectron(); }); - it('works with valid group url', async () => { + // reduce code duplication to get the initial join + async function joinOpenGroup(url, name) { await app.client.element(ConversationPage.globeButtonSection).click(); await app.client.element(ConversationPage.joinOpenGroupButton).click(); - await app.client - .element(ConversationPage.openGroupInputUrl) - .setValue(common.VALID_GROUP_URL); + await common.setValueWrapper(app, ConversationPage.openGroupInputUrl, url); await app.client .element(ConversationPage.openGroupInputUrl) .getValue() - .should.eventually.equal(common.VALID_GROUP_URL); + .should.eventually.equal(url); await app.client.element(ConversationPage.joinOpenGroupButton).click(); // validate session loader is shown await app.client.isExisting(ConversationPage.sessionLoader).should .eventually.be.true; + // account for slow home internet connection delays... await app.client.waitForExist( ConversationPage.sessionToastJoinOpenGroupSuccess, - 9000 + 60 * 1000 ); // validate overlay is closed @@ -50,35 +50,27 @@ describe('Open groups', function() { // validate open chat has been added await app.client.waitForExist( - ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME), + ConversationPage.rowOpenGroupConversationName(name), 4000 ); + } - await common.timeout(1000); + it('openGroup: works with valid open group url', async () => { + await joinOpenGroup(common.VALID_GROUP_URL, common.VALID_GROUP_NAME); }); - it('cannot join two times the same open group', async () => { - await app.client.element(ConversationPage.globeButtonSection).click(); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - - await app.client - .element(ConversationPage.openGroupInputUrl) - .setValue(common.VALID_GROUP_URL2); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - // first add is a success - await common.timeout(2000); - await app.client.waitForExist( - ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2), - 8000 - ); + it('openGroup: cannot join two times the same open group', async () => { + await joinOpenGroup(common.VALID_GROUP_URL2, common.VALID_GROUP_NAME2); // adding a second time the same open group await app.client.element(ConversationPage.globeButtonSection).click(); await app.client.element(ConversationPage.joinOpenGroupButton).click(); - await app.client - .element(ConversationPage.openGroupInputUrl) - .setValue(common.VALID_GROUP_URL2); + await common.setValueWrapper( + app, + ConversationPage.openGroupInputUrl, + common.VALID_GROUP_URL2 + ); await app.client.element(ConversationPage.joinOpenGroupButton).click(); // validate session loader is not shown await app.client.isExisting(ConversationPage.sessionLoader).should @@ -86,7 +78,7 @@ describe('Open groups', function() { await app.client.waitForExist( ConversationPage.sessionToastJoinOpenGroupAlreadyExist, - 1000 + 1 * 1000 ); // validate overlay is still opened @@ -94,20 +86,28 @@ describe('Open groups', function() { .eventually.be.true; }); - it('can send message to open group', async () => { + it('openGroup: can send message to open group', async () => { // join dev-chat group await app.client.element(ConversationPage.globeButtonSection).click(); await app.client.element(ConversationPage.joinOpenGroupButton).click(); - await app.client - .element(ConversationPage.openGroupInputUrl) - .setValue(common.VALID_GROUP_URL2); + await common.setValueWrapper( + app, + ConversationPage.openGroupInputUrl, + common.VALID_GROUP_URL2 + ); await app.client.element(ConversationPage.joinOpenGroupButton).click(); - // first add is a success - await common.timeout(2000); + + // wait for toast to appear + await app.client.waitForExist( + ConversationPage.sessionToastJoinOpenGroupSuccess, + 30 * 1000 + ); + await common.timeout(5 * 1000); // wait for toast to clear + await app.client.waitForExist( ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2), - 8000 + 10 * 1000 ); // generate a message containing the current timestamp so we can find it in the list of messages const textMessage = common.generateSendMessageText(); @@ -118,14 +118,18 @@ describe('Open groups', function() { await app.client.isExisting( ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2) ); + await app.client .element( ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2) ) .click(); - await app.client - .element(ConversationPage.sendMessageTextarea) - .setValue(textMessage); + + await common.setValueWrapper( + app, + ConversationPage.sendMessageTextarea, + textMessage + ); await app.client .element(ConversationPage.sendMessageTextarea) .getValue() @@ -139,7 +143,7 @@ describe('Open groups', function() { // validate that the message has been added to the message list view await app.client.waitForExist( ConversationPage.existingSendMessageText(textMessage), - 3000 + 3 * 1000 ); // we should validate that the message has been added effectively sent // (checking the check icon on the metadata part of the message?) diff --git a/integration_test/registration_test.js b/integration_test/registration_test.js index 3e0a57aa7..a60a5cb52 100644 --- a/integration_test/registration_test.js +++ b/integration_test/registration_test.js @@ -21,12 +21,12 @@ describe('Window Test and Login', function() { await common.killallElectron(); }); - it('opens one window', async () => { + it('registration: opens one window', async () => { app = await common.startAndAssureCleanedApp(); app.client.getWindowCount().should.eventually.be.equal(1); }); - it('window title is correct', async () => { + it('registration: window title is correct', async () => { app = await common.startAndAssureCleanedApp(); app.client @@ -34,7 +34,7 @@ describe('Window Test and Login', function() { .should.eventually.be.equal('Session - test-integration-session'); }); - it('can restore from seed', async () => { + it('registration: can restore from seed', async () => { app = await common.startAndAssureCleanedApp(); await app.client.element(RegistrationPage.registrationTabSignIn).click(); @@ -70,7 +70,7 @@ describe('Window Test and Login', function() { .should.eventually.be.equal(common.TEST_PUBKEY1); }); - it('can create new account', async () => { + it('registration: can create new account', async () => { app = await common.startAndAssureCleanedApp(); await app.client.element(RegistrationPage.createSessionIDButton).click(); // wait for the animation of generated pubkey to finish @@ -98,7 +98,7 @@ describe('Window Test and Login', function() { .should.eventually.be.equal(pubkeyGenerated); }); - it('can delete account when logged in', async () => { + it('registration: can delete account when logged in', async () => { // login as user1 const login = { mnemonic: common.TEST_MNEMONIC1, From 796a37189a2038749b9c5fb520126acc4a23c0be Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 3 Apr 2020 10:00:44 +1100 Subject: [PATCH 19/30] Disable disappearing message before friend request accepted Hide nickname option fron menu in ConversationHeader for now --- .../conversation/ConversationHeader.tsx | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index f0d4e021b..198d06fbd 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -419,6 +419,7 @@ export class ConversationHeader extends React.Component { $('.session-search-input input').focus(); } + // tslint:disable-next-line: cyclomatic-complexity private renderPublicMenuItems() { const { i18n, @@ -438,9 +439,10 @@ export class ConversationHeader extends React.Component { timerOptions, onBlockUser, onUnblockUser, - hasNickname, - onClearNickname, - onChangeNickname, + // hasNickname, + // onClearNickname, + // onChangeNickname, + isFriend, } = this.props; if (isPublic || isRss) { @@ -452,7 +454,7 @@ export class ConversationHeader extends React.Component { const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser'); const blockHandler = isBlocked ? onUnblockUser : onBlockUser; - const disappearingMessagesMenuItem = ( + const disappearingMessagesMenuItem = isFriend && ( {(timerOptions || []).map(item => ( { {i18n('showSafetyNumber')} ); - const resetSessionMenuItem = !isGroup && ( - {i18n('resetSession')} - ); - const blockHandlerMenuItem = !isMe && - !isGroup && - !isRss && {blockTitle}; - const changeNicknameMenuItem = !isMe && + const resetSessionMenuItem = isFriend && !isGroup && ( - {i18n('changeNickname')} + {i18n('resetSession')} ); - const clearNicknameMenuItem = !isMe && + const blockHandlerMenuItem = !isMe && !isGroup && - hasNickname && ( - {i18n('clearNickname')} - ); + !isRss && {blockTitle}; + // const changeNicknameMenuItem = !isMe && + // !isGroup && ( + // {i18n('changeNickname')} + // ); + // const clearNicknameMenuItem = !isMe && + // !isGroup && + // hasNickname && ( + // {i18n('clearNickname')} + // ); const archiveConversationMenuItem = isArchived ? ( {i18n('moveConversationToInbox')} @@ -506,8 +509,8 @@ export class ConversationHeader extends React.Component { {showSafetyNumberMenuItem} {resetSessionMenuItem} {blockHandlerMenuItem} - {changeNicknameMenuItem} - {clearNicknameMenuItem} + {/* {changeNicknameMenuItem} + {clearNicknameMenuItem} */} {archiveConversationMenuItem} ); From 406f5deedda5a106a79b2a2e5d28ed10dbce1e5b Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 3 Apr 2020 10:45:08 +1100 Subject: [PATCH 20/30] remove bug_report.md to update line endings --- .github/ISSUE_TEMPLATE/bug_report.md | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 216832649..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: - -**Screenshots or Logs** -If applicable, add screenshots or log files to help explain your problem. - -**Other information (please complete the following information):** - -* Device: [e.g. PC, Mac] -* OS: [e.g. Ubuntu 16.04, Windows 10] -* Session Version or Git commit hash: From 8dd94eb17270e94b56fcb67cd4f832a7cfdad956 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 3 Apr 2020 10:45:31 +1100 Subject: [PATCH 21/30] add back bug_report.md fixing lint --- .github/ISSUE_TEMPLATE/bug_report.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..216832649 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,22 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +**Screenshots or Logs** +If applicable, add screenshots or log files to help explain your problem. + +**Other information (please complete the following information):** + +* Device: [e.g. PC, Mac] +* OS: [e.g. Ubuntu 16.04, Windows 10] +* Session Version or Git commit hash: From edb85e3426c77c75863b2c5a3a990a99f5ac3003 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 3 Apr 2020 14:34:53 +1100 Subject: [PATCH 22/30] ignore .md file prettier --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c1d06a746..224bd06f6 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,8 @@ "lint-files-full": "yarn eslint-full && yarn tslint", "lint-deps": "node ts/util/lint/linter.js", "tslint": "tslint --format stylish --project .", - "format": "prettier --list-different --write `git ls-files --modified *.{css,js,json,md,scss,ts,tsx}` `git ls-files --modified ./**/*.{css,js,json,md,scss,ts,tsx}`", - "format-full": "prettier --list-different --write \"*.{css,js,json,md,scss,ts,tsx}\" \"./**/*.{css,js,json,md,scss,ts,tsx}\"", + "format": "prettier --list-different --write `git ls-files --modified *.{css,js,json,scss,ts,tsx}` `git ls-files --modified ./**/*.{css,js,json,scss,ts,tsx}`", + "format-full": "prettier --list-different --write \"*.{css,js,json,scss,ts,tsx}\" \"./**/*.{css,js,json,scss,ts,tsx}\"", "transpile": "tsc", "clean-transpile": "rimraf ts/**/*.js && rimraf ts/*.js", "pow-metrics": "node metrics_app.js localhost 9000", From 83ac64a0e721ed89ebf91d3d242027fe0e1edb40 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 3 Apr 2020 15:45:23 +1100 Subject: [PATCH 23/30] run lint ci task only on linux and macos --- .github/workflows/build-binaries.yml | 1 + .github/workflows/pull-request.yml | 3 ++- .github/workflows/release.yml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 19760fd1a..01b436bed 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -46,6 +46,7 @@ jobs: run: yarn generate - name: Lint Files + if: runner.os != 'Windows' run: yarn lint-full - name: Build windows production binaries diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 9cab74b71..7e74cb96d 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -47,8 +47,9 @@ jobs: run: yarn generate - name: Lint Files + if: runner.os != 'Windows' run: | - yarn format-full --list-different + yarn format-full yarn eslint yarn tslint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f56cc97b..7fe998f3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,6 +43,7 @@ jobs: run: yarn generate - name: Lint Files + if: runner.os != 'Windows' run: yarn lint-full - name: Build windows production binaries From 5ff747635921b4d3332b7743b470b822874e1953 Mon Sep 17 00:00:00 2001 From: Vince Date: Fri, 3 Apr 2020 16:20:49 +1100 Subject: [PATCH 24/30] Revert "Clean up unused dependencies." --- .eslintignore | 1 + app/password_util.js | 8 +- background.html | 2 + background_test.html | 2 + bower.json | 1 + js/background.js | 39 ++ js/libphonenumber-util.js | 295 ++++++++++++ js/modules/debuglogs.js | 44 +- js/modules/loki_snode_api.js | 2 +- js/modules/privacy.js | 10 +- js/modules/refresh_sender_certificate.js | 120 +++++ js/modules/signal.js | 2 + js/modules/web_api.js | 576 ++++++++++++++++++++++- js/views/phone-input-view.js | 39 ++ libtextsecure/account_manager.js | 38 +- libtextsecure/message_receiver.js | 80 +++- libtextsecure/outgoing_message.js | 4 + libtextsecure/sendmessage.js | 17 +- libtextsecure/test/fake_web_api.js | 47 +- package.json | 19 +- preload.js | 10 +- stylesheets/_options.scss | 13 + stylesheets/_theme_dark.scss | 4 + test/index.html | 3 + test/libphonenumber_util_test.js | 26 + test/modules/privacy_test.js | 39 +- ts/util/libphonenumberInstance.ts | 6 + ts/util/lint/linter.ts | 2 + yarn.lock | 436 ++++++++++++++++- 29 files changed, 1787 insertions(+), 98 deletions(-) create mode 100644 js/libphonenumber-util.js create mode 100644 js/modules/refresh_sender_certificate.js create mode 100644 js/views/phone-input-view.js create mode 100644 test/libphonenumber_util_test.js create mode 100644 ts/util/libphonenumberInstance.ts diff --git a/.eslintignore b/.eslintignore index 9957d65d8..a1a37d7f5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -19,6 +19,7 @@ test/test.js # Third-party files js/Mp3LameEncoder.min.js js/WebAudioRecorderMp3.js +js/libphonenumber-util.js js/libsignal-protocol-worker.js libtextsecure/libsignal-protocol.js libtextsecure/test/blanket_mocha.js diff --git a/app/password_util.js b/app/password_util.js index 915f017fe..944769c60 100644 --- a/app/password_util.js +++ b/app/password_util.js @@ -1,4 +1,4 @@ -const crypto = require('crypto'); +const { sha512 } = require('js-sha512'); const ERRORS = { TYPE: 'Password must be a string', @@ -6,12 +6,6 @@ const ERRORS = { CHARACTER: 'Password must only contain letters, numbers and symbols', }; -const sha512 = text => { - const hash = crypto.createHash('sha512'); - hash.update(text.trim()); - return hash.digest('hex'); -}; - const generateHash = phrase => phrase && sha512(phrase.trim()); const matchesHash = (phrase, hash) => phrase && sha512(phrase.trim()) === hash.trim(); diff --git a/background.html b/background.html index c3994d233..785d40ec2 100644 --- a/background.html +++ b/background.html @@ -454,6 +454,7 @@ + @@ -500,6 +501,7 @@ + diff --git a/background_test.html b/background_test.html index 8433a9af4..39480ba67 100644 --- a/background_test.html +++ b/background_test.html @@ -454,6 +454,7 @@ + @@ -500,6 +501,7 @@ + diff --git a/bower.json b/bower.json index 751d468d0..8b2fbba06 100644 --- a/bower.json +++ b/bower.json @@ -56,6 +56,7 @@ "node_modules/backbone/backbone.js", "components/indexeddb-backbonejs-adapter/**/*.js", "components/qrcode/**/*.js", + "node_modules/intl-tel-input/build/js/intlTelInput.js", "components/autosize/**/*.js", "components/webaudiorecorder/lib/WebAudioRecorder.js", "components/multibase/dist/index.js" diff --git a/js/background.js b/js/background.js index e494311ed..6875cb753 100644 --- a/js/background.js +++ b/js/background.js @@ -569,6 +569,12 @@ // listeners Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); + // window.Signal.RefreshSenderCertificate.initialize({ + // events: Whisper.events, + // storage, + // navigator, + // logger: window.log, + // }); connect(true); }); @@ -591,6 +597,12 @@ ) { // listeners Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); + // window.Signal.RefreshSenderCertificate.initialize({ + // events: Whisper.events, + // storage, + // navigator, + // logger: window.log, + // }); connect(); appView.openInbox({ @@ -1535,8 +1547,35 @@ textsecure.storage.user.getDeviceId() != '1' ) { window.getSyncRequest(); + + try { + const manager = window.getAccountManager(); + await Promise.all([ + manager.maybeUpdateDeviceName(), + manager.maybeDeleteSignalingKey(), + ]); + } catch (e) { + window.log.error( + 'Problem with account manager updates after starting new version: ', + e && e.stack ? e.stack : e + ); + } } + // const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery'; + // if (!storage.get(udSupportKey)) { + // const server = WebAPI.connect({ username: USERNAME, password: PASSWORD }); + // try { + // await server.registerSupportForUnauthenticatedDelivery(); + // storage.put(udSupportKey, true); + // } catch (error) { + // window.log.error( + // 'Error: Unable to register for unauthenticated delivery support.', + // error && error.stack ? error.stack : error + // ); + // } + // } + const deviceId = textsecure.storage.user.getDeviceId(); if (firstRun === true && deviceId !== '1') { const hasThemeSetting = Boolean(storage.get('theme-setting')); diff --git a/js/libphonenumber-util.js b/js/libphonenumber-util.js new file mode 100644 index 000000000..ac13013c8 --- /dev/null +++ b/js/libphonenumber-util.js @@ -0,0 +1,295 @@ +(function() { + 'use strict'; + + /* + * This file extends the libphonenumber object with a set of phonenumbery + * utility functions. libphonenumber must be included before you call these + * functions, but the order of the files/script-tags doesn't matter. + */ + + window.libphonenumber = window.libphonenumber || {}; + window.libphonenumber.util = { + getRegionCodeForNumber: function(number) { + try { + var parsedNumber = libphonenumber.parse(number); + return libphonenumber.getRegionCodeForNumber(parsedNumber); + } catch (e) { + return 'ZZ'; + } + }, + + splitCountryCode: function(number) { + var parsedNumber = libphonenumber.parse(number); + return { + country_code: parsedNumber.values_[1], + national_number: parsedNumber.values_[2], + }; + }, + + getCountryCode: function(regionCode) { + var cc = libphonenumber.getCountryCodeForRegion(regionCode); + return cc !== 0 ? cc : ''; + }, + + parseNumber: function(number, defaultRegionCode) { + try { + var parsedNumber = libphonenumber.parse(number, defaultRegionCode); + + return { + isValidNumber: libphonenumber.isValidNumber(parsedNumber), + regionCode: libphonenumber.getRegionCodeForNumber(parsedNumber), + countryCode: '' + parsedNumber.getCountryCode(), + nationalNumber: '' + parsedNumber.getNationalNumber(), + e164: libphonenumber.format( + parsedNumber, + libphonenumber.PhoneNumberFormat.E164 + ), + }; + } catch (ex) { + return { error: ex, isValidNumber: false }; + } + }, + + getAllRegionCodes: function() { + return { + AD: 'Andorra', + AE: 'United Arab Emirates', + AF: 'Afghanistan', + AG: 'Antigua and Barbuda', + AI: 'Anguilla', + AL: 'Albania', + AM: 'Armenia', + AO: 'Angola', + AR: 'Argentina', + AS: 'AmericanSamoa', + AT: 'Austria', + AU: 'Australia', + AW: 'Aruba', + AX: 'Åland Islands', + AZ: 'Azerbaijan', + BA: 'Bosnia and Herzegovina', + BB: 'Barbados', + BD: 'Bangladesh', + BE: 'Belgium', + BF: 'Burkina Faso', + BG: 'Bulgaria', + BH: 'Bahrain', + BI: 'Burundi', + BJ: 'Benin', + BL: 'Saint Barthélemy', + BM: 'Bermuda', + BN: 'Brunei Darussalam', + BO: 'Bolivia, Plurinational State of', + BR: 'Brazil', + BS: 'Bahamas', + BT: 'Bhutan', + BW: 'Botswana', + BY: 'Belarus', + BZ: 'Belize', + CA: 'Canada', + CC: 'Cocos (Keeling) Islands', + CD: 'Congo, The Democratic Republic of the', + CF: 'Central African Republic', + CG: 'Congo', + CH: 'Switzerland', + CI: "Cote d'Ivoire", + CK: 'Cook Islands', + CL: 'Chile', + CM: 'Cameroon', + CN: 'China', + CO: 'Colombia', + CR: 'Costa Rica', + CU: 'Cuba', + CV: 'Cape Verde', + CX: 'Christmas Island', + CY: 'Cyprus', + CZ: 'Czech Republic', + DE: 'Germany', + DJ: 'Djibouti', + DK: 'Denmark', + DM: 'Dominica', + DO: 'Dominican Republic', + DZ: 'Algeria', + EC: 'Ecuador', + EE: 'Estonia', + EG: 'Egypt', + ER: 'Eritrea', + ES: 'Spain', + ET: 'Ethiopia', + FI: 'Finland', + FJ: 'Fiji', + FK: 'Falkland Islands (Malvinas)', + FM: 'Micronesia, Federated States of', + FO: 'Faroe Islands', + FR: 'France', + GA: 'Gabon', + GB: 'United Kingdom', + GD: 'Grenada', + GE: 'Georgia', + GF: 'French Guiana', + GG: 'Guernsey', + GH: 'Ghana', + GI: 'Gibraltar', + GL: 'Greenland', + GM: 'Gambia', + GN: 'Guinea', + GP: 'Guadeloupe', + GQ: 'Equatorial Guinea', + GR: 'Ελλάδα', + GT: 'Guatemala', + GU: 'Guam', + GW: 'Guinea-Bissau', + GY: 'Guyana', + HK: 'Hong Kong', + HN: 'Honduras', + HR: 'Croatia', + HT: 'Haiti', + HU: 'Magyarország', + ID: 'Indonesia', + IE: 'Ireland', + IL: 'Israel', + IM: 'Isle of Man', + IN: 'India', + IO: 'British Indian Ocean Territory', + IQ: 'Iraq', + IR: 'Iran, Islamic Republic of', + IS: 'Iceland', + IT: 'Italy', + JE: 'Jersey', + JM: 'Jamaica', + JO: 'Jordan', + JP: 'Japan', + KE: 'Kenya', + KG: 'Kyrgyzstan', + KH: 'Cambodia', + KI: 'Kiribati', + KM: 'Comoros', + KN: 'Saint Kitts and Nevis', + KP: "Korea, Democratic People's Republic of", + KR: 'Korea, Republic of', + KW: 'Kuwait', + KY: 'Cayman Islands', + KZ: 'Kazakhstan', + LA: "Lao People's Democratic Republic", + LB: 'Lebanon', + LC: 'Saint Lucia', + LI: 'Liechtenstein', + LK: 'Sri Lanka', + LR: 'Liberia', + LS: 'Lesotho', + LT: 'Lithuania', + LU: 'Luxembourg', + LV: 'Latvia', + LY: 'Libyan Arab Jamahiriya', + MA: 'Morocco', + MC: 'Monaco', + MD: 'Moldova, Republic of', + ME: 'Црна Гора', + MF: 'Saint Martin', + MG: 'Madagascar', + MH: 'Marshall Islands', + MK: 'Macedonia, The Former Yugoslav Republic of', + ML: 'Mali', + MM: 'Myanmar', + MN: 'Mongolia', + MO: 'Macao', + MP: 'Northern Mariana Islands', + MQ: 'Martinique', + MR: 'Mauritania', + MS: 'Montserrat', + MT: 'Malta', + MU: 'Mauritius', + MV: 'Maldives', + MW: 'Malawi', + MX: 'Mexico', + MY: 'Malaysia', + MZ: 'Mozambique', + NA: 'Namibia', + NC: 'New Caledonia', + NE: 'Niger', + NF: 'Norfolk Island', + NG: 'Nigeria', + NI: 'Nicaragua', + NL: 'Netherlands', + NO: 'Norway', + NP: 'Nepal', + NR: 'Nauru', + NU: 'Niue', + NZ: 'New Zealand', + OM: 'Oman', + PA: 'Panama', + PE: 'Peru', + PF: 'French Polynesia', + PG: 'Papua New Guinea', + PH: 'Philippines', + PK: 'Pakistan', + PL: 'Polska', + PM: 'Saint Pierre and Miquelon', + PR: 'Puerto Rico', + PS: 'Palestinian Territory, Occupied', + PT: 'Portugal', + PW: 'Palau', + PY: 'Paraguay', + QA: 'Qatar', + RE: 'Réunion', + RO: 'Romania', + RS: 'Србија', + RU: 'Russia', + RW: 'Rwanda', + SA: 'Saudi Arabia', + SB: 'Solomon Islands', + SC: 'Seychelles', + SD: 'Sudan', + SE: 'Sweden', + SG: 'Singapore', + SH: 'Saint Helena, Ascension and Tristan Da Cunha', + SI: 'Slovenia', + SJ: 'Svalbard and Jan Mayen', + SK: 'Slovakia', + SL: 'Sierra Leone', + SM: 'San Marino', + SN: 'Senegal', + SO: 'Somalia', + SR: 'Suriname', + ST: 'Sao Tome and Principe', + SV: 'El Salvador', + SY: 'Syrian Arab Republic', + SZ: 'Swaziland', + TC: 'Turks and Caicos Islands', + TD: 'Chad', + TG: 'Togo', + TH: 'Thailand', + TJ: 'Tajikistan', + TK: 'Tokelau', + TL: 'Timor-Leste', + TM: 'Turkmenistan', + TN: 'Tunisia', + TO: 'Tonga', + TR: 'Turkey', + TT: 'Trinidad and Tobago', + TV: 'Tuvalu', + TW: 'Taiwan, Province of China', + TZ: 'Tanzania, United Republic of', + UA: 'Ukraine', + UG: 'Uganda', + US: 'United States', + UY: 'Uruguay', + UZ: 'Uzbekistan', + VA: 'Holy See (Vatican City State)', + VC: 'Saint Vincent and the Grenadines', + VE: 'Venezuela', + VG: 'Virgin Islands, British', + VI: 'Virgin Islands, U.S.', + VN: 'Viet Nam', + VU: 'Vanuatu', + WF: 'Wallis and Futuna', + WS: 'Samoa', + YE: 'Yemen', + YT: 'Mayotte', + ZA: 'South Africa', + ZM: 'Zambia', + ZW: 'Zimbabwe', + }; + }, // getAllRegionCodes + }; // libphonenumber.util +})(); diff --git a/js/modules/debuglogs.js b/js/modules/debuglogs.js index bea65a431..81b00a5b8 100644 --- a/js/modules/debuglogs.js +++ b/js/modules/debuglogs.js @@ -2,24 +2,46 @@ /* global window */ const FormData = require('form-data'); -const fetch = require('node-fetch'); +const got = require('got'); const BASE_URL = 'https://debuglogs.org'; const VERSION = window.getVersion(); const USER_AGENT = `Session ${VERSION}`; +// Workaround: Submitting `FormData` using native `FormData::submit` procedure +// as integration with `got` results in S3 error saying we haven’t set the +// `Content-Length` header: +// https://github.com/sindresorhus/got/pull/466 +const submitFormData = (form, url) => + new Promise((resolve, reject) => { + form.submit(url, (error, response) => { + if (error) { + return reject(error); + } + + const { statusCode } = response; + if (statusCode !== 204) { + return reject( + new Error(`Failed to upload to S3, got status ${statusCode}`) + ); + } + + return resolve(); + }); + }); + // upload :: String -> Promise URL exports.upload = async content => { - const signedForm = await fetch(BASE_URL, { + const signedForm = await got.get(BASE_URL, { + json: true, headers: { 'user-agent': USER_AGENT, }, }); - const json = await signedForm.json(); - if (!signedForm.ok || !json) { + if (!signedForm.body) { throw new Error('Failed to retrieve token'); } - const { fields, url } = json; + const { fields, url } = signedForm.body; const form = new FormData(); // The API expects `key` to be the first field: @@ -38,15 +60,9 @@ exports.upload = async content => { filename: `session-desktop-debug-log-${VERSION}.txt`, }); - const result = await fetch(url, { - method: 'POST', - body: form, - }); - - const { status } = result; - if (status !== 204) { - throw new Error(`Failed to upload to S3, got status ${status}`); - } + // WORKAROUND: See comment on `submitFormData`: + // await got.post(url, { body: form }); + await submitFormData(form, url); return `${BASE_URL}/${fields.key}`; }; diff --git a/js/modules/loki_snode_api.js b/js/modules/loki_snode_api.js index fe7457037..686fd2fc2 100644 --- a/js/modules/loki_snode_api.js +++ b/js/modules/loki_snode_api.js @@ -19,7 +19,7 @@ const timeoutDelay = ms => new Promise(resolve => setTimeout(resolve, ms)); class LokiSnodeAPI { constructor({ serverUrl, localUrl }) { if (!is.string(serverUrl)) { - throw new Error('LokiSnodeAPI.initialize: Invalid server url'); + throw new Error('WebAPI.initialize: Invalid server url'); } this.serverUrl = serverUrl; // random.snode this.localUrl = localUrl; // localhost.loki diff --git a/js/modules/privacy.js b/js/modules/privacy.js index 1720b7420..d737f5d05 100644 --- a/js/modules/privacy.js +++ b/js/modules/privacy.js @@ -7,7 +7,7 @@ const { compose } = require('lodash/fp'); const { escapeRegExp } = require('lodash'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); -const SESSION_ID_PATTERN = /\b(05[0-9a-f]{64})\b/gi; +const PHONE_NUMBER_PATTERN = /\+\d{7,12}(\d{3})/g; const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g; const REDACTION_PLACEHOLDER = '[REDACTED]'; @@ -55,13 +55,13 @@ exports._pathToRegExp = filePath => { }; // Public API -// redactSessionID :: String -> String -exports.redactSessionID = text => { +// redactPhoneNumbers :: String -> String +exports.redactPhoneNumbers = text => { if (!is.string(text)) { throw new TypeError("'text' must be a string"); } - return text.replace(SESSION_ID_PATTERN, REDACTION_PLACEHOLDER); + return text.replace(PHONE_NUMBER_PATTERN, `+${REDACTION_PLACEHOLDER}$1`); }; // redactGroupIds :: String -> String @@ -84,7 +84,7 @@ exports.redactSensitivePaths = exports._redactPath(APP_ROOT_PATH); exports.redactAll = compose( exports.redactSensitivePaths, exports.redactGroupIds, - exports.redactSessionID + exports.redactPhoneNumbers ); const removeNewlines = text => text.replace(/\r?\n|\r/g, ''); diff --git a/js/modules/refresh_sender_certificate.js b/js/modules/refresh_sender_certificate.js new file mode 100644 index 000000000..275453fda --- /dev/null +++ b/js/modules/refresh_sender_certificate.js @@ -0,0 +1,120 @@ +/* global window, setTimeout, clearTimeout, textsecure, WebAPI, ConversationController */ + +module.exports = { + initialize, +}; + +const ONE_DAY = 24 * 60 * 60 * 1000; // one day +const MINIMUM_TIME_LEFT = 2 * 60 * 60 * 1000; // two hours + +let timeout = null; +let scheduledTime = null; +let scheduleNext = null; + +// We need to refresh our own profile regularly to account for newly-added devices which +// do not support unidentified delivery. +function refreshOurProfile() { + const ourNumber = textsecure.storage.user.getNumber(); + const conversation = ConversationController.getOrCreate(ourNumber, 'private'); + conversation.getProfiles(); +} + +function initialize({ events, storage, navigator, logger }) { + // We don't want to set up all of the below functions, but we do want to ensure that our + // refresh timer is up-to-date. + if (scheduleNext) { + scheduleNext(); + return; + } + + runWhenOnline(); + + events.on('timetravel', scheduleNextRotation); + + function scheduleNextRotation() { + const now = Date.now(); + const certificate = storage.get('senderCertificate'); + if (!certificate) { + setTimeoutForNextRun(now); + + return; + } + + // The useful information in a SenderCertificate is all serialized, so we + // need to do another layer of decoding. + const decoded = textsecure.protobuf.SenderCertificate.Certificate.decode( + certificate.certificate + ); + const expires = decoded.expires.toNumber(); + + const time = Math.min(now + ONE_DAY, expires - MINIMUM_TIME_LEFT); + + setTimeoutForNextRun(time); + } + + // Keeping this entrypoint around so more inialize() calls just kick the timing + scheduleNext = scheduleNextRotation; + + async function run() { + logger.info('refreshSenderCertificate: Getting new certificate...'); + try { + const username = storage.get('number_id'); + const password = storage.get('password'); + const server = WebAPI.connect({ username, password }); + + const { certificate } = await server.getSenderCertificate(); + const arrayBuffer = window.Signal.Crypto.base64ToArrayBuffer(certificate); + const decoded = textsecure.protobuf.SenderCertificate.decode(arrayBuffer); + + decoded.certificate = decoded.certificate.toArrayBuffer(); + decoded.signature = decoded.signature.toArrayBuffer(); + decoded.serialized = arrayBuffer; + + storage.put('senderCertificate', decoded); + scheduleNextRotation(); + } catch (error) { + logger.error( + 'refreshSenderCertificate: Get failed. Trying again in two minutes...', + error && error.stack ? error.stack : error + ); + setTimeout(runWhenOnline, 2 * 60 * 1000); + } + + refreshOurProfile(); + } + + function runWhenOnline() { + if (navigator.onLine) { + run(); + } else { + logger.info( + 'refreshSenderCertificate: Offline. Will update certificate when online...' + ); + const listener = () => { + logger.info( + 'refreshSenderCertificate: Online. Now updating certificate...' + ); + window.removeEventListener('online', listener); + run(); + }; + window.addEventListener('online', listener); + } + } + + function setTimeoutForNextRun(time = Date.now()) { + const now = Date.now(); + + if (scheduledTime !== time || !timeout) { + logger.info( + 'Next sender certificate refresh scheduled for', + new Date(time).toISOString() + ); + } + + scheduledTime = time; + const waitTime = Math.max(0, time - now); + + clearTimeout(timeout); + timeout = setTimeout(runWhenOnline, waitTime); + } +} diff --git a/js/modules/signal.js b/js/modules/signal.js index c676c5845..f5ddc198b 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -13,6 +13,7 @@ const Settings = require('./settings'); const Util = require('../../ts/util'); const { migrateToSQL } = require('./migrate_to_sql'); const Metadata = require('./metadata/SecretSessionCipher'); +const RefreshSenderCertificate = require('./refresh_sender_certificate'); const LinkPreviews = require('./link_previews'); const AttachmentDownloads = require('./attachment_downloads'); @@ -378,6 +379,7 @@ exports.setup = (options = {}) => { Migrations, Notifications, OS, + RefreshSenderCertificate, Settings, State, Types, diff --git a/js/modules/web_api.js b/js/modules/web_api.js index 0b89c3804..c92681b05 100644 --- a/js/modules/web_api.js +++ b/js/modules/web_api.js @@ -1,7 +1,11 @@ +const WebSocket = require('websocket').w3cwebsocket; const fetch = require('node-fetch'); +const ProxyAgent = require('proxy-agent'); const { Agent } = require('https'); const FormData = require('form-data'); +const is = require('@sindresorhus/is'); + /* global Buffer, setTimeout, log, _, lokiFileServerAPI */ /* eslint-disable more/no-then, no-bitwise, no-nested-ternary */ @@ -35,6 +39,89 @@ function _getString(thing) { return thing; } +function _b64ToUint6(nChr) { + return nChr > 64 && nChr < 91 + ? nChr - 65 + : nChr > 96 && nChr < 123 + ? nChr - 71 + : nChr > 47 && nChr < 58 + ? nChr + 4 + : nChr === 43 + ? 62 + : nChr === 47 + ? 63 + : 0; +} + +function _getStringable(thing) { + return ( + typeof thing === 'string' || + typeof thing === 'number' || + typeof thing === 'boolean' || + (thing === Object(thing) && + (_call(thing) === ArrayBufferToString || + _call(thing) === Uint8ArrayToString)) + ); +} + +function _ensureStringed(thing) { + if (_getStringable(thing)) { + return _getString(thing); + } else if (thing instanceof Array) { + const res = []; + for (let i = 0; i < thing.length; i += 1) { + res[i] = _ensureStringed(thing[i]); + } + return res; + } else if (thing === Object(thing)) { + const res = {}; + // eslint-disable-next-line guard-for-in, no-restricted-syntax + for (const key in thing) { + res[key] = _ensureStringed(thing[key]); + } + return res; + } else if (thing === null) { + return null; + } else if (thing === undefined) { + return undefined; + } + throw new Error(`unsure of how to jsonify object of type ${typeof thing}`); +} + +function _jsonThing(thing) { + return JSON.stringify(_ensureStringed(thing)); +} + +function _base64ToBytes(sBase64, nBlocksSize) { + const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ''); + const nInLen = sB64Enc.length; + const nOutLen = nBlocksSize + ? Math.ceil(((nInLen * 3 + 1) >> 2) / nBlocksSize) * nBlocksSize + : (nInLen * 3 + 1) >> 2; + const aBBytes = new ArrayBuffer(nOutLen); + const taBytes = new Uint8Array(aBBytes); + + for ( + let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; + nInIdx < nInLen; + nInIdx += 1 + ) { + nMod4 = nInIdx & 3; + nUint24 |= _b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4); + if (nMod4 === 3 || nInLen - nInIdx === 1) { + for ( + nMod3 = 0; + nMod3 < 3 && nOutIdx < nOutLen; + nMod3 += 1, nOutIdx += 1 + ) { + taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255; + } + nUint24 = 0; + } + } + return aBBytes; +} + function _validateResponse(response, schema) { try { // eslint-disable-next-line guard-for-in, no-restricted-syntax @@ -57,6 +144,30 @@ function _validateResponse(response, schema) { return true; } +function _createSocket(url, { certificateAuthority, proxyUrl, signature }) { + let requestOptions; + if (proxyUrl) { + requestOptions = { + ca: certificateAuthority, + agent: new ProxyAgent(proxyUrl), + }; + } else { + requestOptions = { + ca: certificateAuthority, + }; + } + // TODO: sign a timestamp + let headers; + if (signature) { + headers = { + signature, + }; + } + + // eslint-disable-next-line new-cap + return new WebSocket(url, null, null, headers, requestOptions); +} + const FIVE_MINUTES = 1000 * 60 * 5; const agents = { unauth: null, @@ -99,7 +210,9 @@ function _promiseAjax(providedUrl, options) { log.info(`Cycling agent for type ${cacheKey}`); } agents[cacheKey] = { - agent: new Agent({ keepAlive: true }), + agent: proxyUrl + ? new ProxyAgent(proxyUrl) + : new Agent({ keepAlive: true }), timestamp: Date.now(), }; } @@ -281,12 +394,45 @@ function HTTPError(message, providedCode, response, stack) { return e; } +const URL_CALLS = { + accounts: 'v1/accounts', + updateDeviceName: 'v1/accounts/name', + removeSignalingKey: 'v1/accounts/signaling_key', + attachment: 'v1/attachments', + deliveryCert: 'v1/certificate/delivery', + supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery', + devices: 'v1/devices', + keys: 'v2/keys', + messages: 'v1/messages', + profile: 'v1/profile', + signed: 'v2/keys/signed', +}; + module.exports = { initialize, }; // We first set up the data that won't change during this session of the app -function initialize() { +function initialize({ + url, + cdnUrl, + certificateAuthority, + contentProxyUrl, + proxyUrl, +}) { + if (!is.string(url)) { + throw new Error('WebAPI.initialize: Invalid server url'); + } + if (!is.string(cdnUrl)) { + throw new Error('WebAPI.initialize: Invalid cdnUrl'); + } + if (!is.string(certificateAuthority)) { + throw new Error('WebAPI.initialize: Invalid certificateAuthority'); + } + if (!is.string(contentProxyUrl)) { + throw new Error('WebAPI.initialize: Invalid contentProxyUrl'); + } + // Thanks to function-hoisting, we can put this return statement before all of the // below function definitions. return { @@ -296,19 +442,413 @@ function initialize() { // Then we connect to the server with user-specific information. This is the only API // exposed to the browser context, ensuring that it can't connect to arbitrary // locations. - function connect() { + function connect({ username: initialUsername, password: initialPassword }) { + let username = initialUsername; + let password = initialPassword; + // Thanks, function hoisting! return { + confirmCode, getAttachment, + getAvatar, + getDevices, + getKeysForNumber, + getKeysForNumberUnauth, + getMessageSocket, + getMyKeys, + getProfile, + getProfileUnauth, + getProvisioningSocket, getProxiedSize, + getSenderCertificate, makeProxiedRequest, putAttachment, putAvatar, + registerKeys, + registerSupportForUnauthenticatedDelivery, + removeSignalingKey, + requestVerificationSMS, + requestVerificationVoice, + sendMessages, + sendMessagesUnauth, + setSignedPreKey, + updateDeviceName, }; + function _ajax(param) { + if (!param.urlParameters) { + // eslint-disable-next-line no-param-reassign + param.urlParameters = ''; + } + return _outerAjax(null, { + certificateAuthority, + contentType: 'application/json; charset=utf-8', + data: param.jsonData && _jsonThing(param.jsonData), + host: url, + password, + path: URL_CALLS[param.call] + param.urlParameters, + proxyUrl, + responseType: param.responseType, + timeout: param.timeout, + type: param.httpType, + user: username, + validateResponse: param.validateResponse, + unauthenticated: param.unauthenticated, + accessKey: param.accessKey, + }).catch(e => { + const { code } = e; + if (code === 200) { + // happens sometimes when we get no response + // (TODO: Fix server to return 204? instead) + return null; + } + let message; + switch (code) { + case -1: + message = + 'Failed to connect to the server, please check your network connection.'; + break; + case 413: + message = 'Rate limit exceeded, please try again later.'; + break; + case 403: + message = 'Invalid code, please try again.'; + break; + case 417: + // TODO: This shouldn't be a thing?, but its in the API doc? + message = 'Number already registered.'; + break; + case 401: + message = + 'Invalid authentication, most likely someone re-registered and invalidated our registration.'; + break; + case 404: + message = 'Number is not registered.'; + break; + default: + message = + 'The server rejected our query, please file a bug report.'; + } + e.message = `${message} (original: ${e.message})`; + throw e; + }); + } + + function getSenderCertificate() { + return _ajax({ + call: 'deliveryCert', + httpType: 'GET', + responseType: 'json', + schema: { certificate: 'string' }, + }); + } + + function registerSupportForUnauthenticatedDelivery() { + return _ajax({ + call: 'supportUnauthenticatedDelivery', + httpType: 'PUT', + responseType: 'json', + }); + } + + function getProfile(number) { + return _ajax({ + call: 'profile', + httpType: 'GET', + urlParameters: `/${number}`, + responseType: 'json', + }); + } + function getProfileUnauth(number, { accessKey } = {}) { + return _ajax({ + call: 'profile', + httpType: 'GET', + urlParameters: `/${number}`, + responseType: 'json', + unauthenticated: true, + accessKey, + }); + } + + function getAvatar(path) { + // Using _outerAJAX, since it's not hardcoded to the Signal Server. Unlike our + // attachment CDN, it uses our self-signed certificate, so we pass it in. + return _outerAjax(`${cdnUrl}/${path}`, { + certificateAuthority, + contentType: 'application/octet-stream', + proxyUrl, + responseType: 'arraybuffer', + timeout: 0, + type: 'GET', + }); + } + + function requestVerificationSMS(number) { + return _ajax({ + call: 'accounts', + httpType: 'GET', + urlParameters: `/sms/code/${number}`, + }); + } + + function requestVerificationVoice(number) { + return _ajax({ + call: 'accounts', + httpType: 'GET', + urlParameters: `/voice/code/${number}`, + }); + } + + async function confirmCode( + number, + code, + newPassword, + registrationId, + deviceName, + options = {} + ) { + const { accessKey } = options; + const jsonData = { + supportsSms: false, + fetchesMessages: true, + registrationId, + unidentifiedAccessKey: accessKey + ? _btoa(_getString(accessKey)) + : undefined, + unrestrictedUnidentifiedAccess: false, + }; + + let call; + let urlPrefix; + let schema; + let responseType; + + if (deviceName) { + jsonData.name = deviceName; + call = 'devices'; + urlPrefix = '/'; + schema = { deviceId: 'number' }; + responseType = 'json'; + } else { + call = 'accounts'; + urlPrefix = '/code/'; + } + + // We update our saved username and password, since we're creating a new account + username = number; + password = newPassword; + + const response = await _ajax({ + call, + httpType: 'PUT', + urlParameters: urlPrefix + code, + jsonData, + responseType, + validateResponse: schema, + }); + + // From here on out, our username will be our phone number combined with device + username = `${number}.${response.deviceId || 1}`; + + return response; + } + + function updateDeviceName(deviceName) { + return _ajax({ + call: 'updateDeviceName', + httpType: 'PUT', + jsonData: { + deviceName, + }, + }); + } + + function removeSignalingKey() { + return _ajax({ + call: 'removeSignalingKey', + httpType: 'DELETE', + }); + } + + function getDevices() { + return _ajax({ + call: 'devices', + httpType: 'GET', + }); + } + + function registerKeys(genKeys) { + const keys = {}; + keys.identityKey = _btoa(_getString(genKeys.identityKey)); + keys.signedPreKey = { + keyId: genKeys.signedPreKey.keyId, + publicKey: _btoa(_getString(genKeys.signedPreKey.publicKey)), + signature: _btoa(_getString(genKeys.signedPreKey.signature)), + }; + + keys.preKeys = []; + let j = 0; + // eslint-disable-next-line guard-for-in, no-restricted-syntax + for (const i in genKeys.preKeys) { + keys.preKeys[j] = { + keyId: genKeys.preKeys[i].keyId, + publicKey: _btoa(_getString(genKeys.preKeys[i].publicKey)), + }; + j += 1; + } + + // This is just to make the server happy + // (v2 clients should choke on publicKey) + keys.lastResortKey = { keyId: 0x7fffffff, publicKey: _btoa('42') }; + + return _ajax({ + call: 'keys', + httpType: 'PUT', + jsonData: keys, + }); + } + + function setSignedPreKey(signedPreKey) { + return _ajax({ + call: 'signed', + httpType: 'PUT', + jsonData: { + keyId: signedPreKey.keyId, + publicKey: _btoa(_getString(signedPreKey.publicKey)), + signature: _btoa(_getString(signedPreKey.signature)), + }, + }); + } + + function getMyKeys() { + return _ajax({ + call: 'keys', + httpType: 'GET', + responseType: 'json', + validateResponse: { count: 'number' }, + }).then(res => res.count); + } + + function handleKeys(res) { + if (!Array.isArray(res.devices)) { + throw new Error('Invalid response'); + } + res.identityKey = _base64ToBytes(res.identityKey); + res.devices.forEach(device => { + if ( + !_validateResponse(device, { signedPreKey: 'object' }) || + !_validateResponse(device.signedPreKey, { + publicKey: 'string', + signature: 'string', + }) + ) { + throw new Error('Invalid signedPreKey'); + } + if (device.preKey) { + if ( + !_validateResponse(device, { preKey: 'object' }) || + !_validateResponse(device.preKey, { publicKey: 'string' }) + ) { + throw new Error('Invalid preKey'); + } + // eslint-disable-next-line no-param-reassign + device.preKey.publicKey = _base64ToBytes(device.preKey.publicKey); + } + // eslint-disable-next-line no-param-reassign + device.signedPreKey.publicKey = _base64ToBytes( + device.signedPreKey.publicKey + ); + // eslint-disable-next-line no-param-reassign + device.signedPreKey.signature = _base64ToBytes( + device.signedPreKey.signature + ); + }); + return res; + } + + function getKeysForNumber(number, deviceId = '*') { + return _ajax({ + call: 'keys', + httpType: 'GET', + urlParameters: `/${number}/${deviceId}`, + responseType: 'json', + validateResponse: { identityKey: 'string', devices: 'object' }, + }).then(handleKeys); + } + + function getKeysForNumberUnauth( + number, + deviceId = '*', + { accessKey } = {} + ) { + return _ajax({ + call: 'keys', + httpType: 'GET', + urlParameters: `/${number}/${deviceId}`, + responseType: 'json', + validateResponse: { identityKey: 'string', devices: 'object' }, + unauthenticated: true, + accessKey, + }).then(handleKeys); + } + + function sendMessagesUnauth( + destination, + messageArray, + timestamp, + silent, + online, + { accessKey } = {} + ) { + const jsonData = { messages: messageArray, timestamp }; + + if (silent) { + jsonData.silent = true; + } + if (online) { + jsonData.online = true; + } + + return _ajax({ + call: 'messages', + httpType: 'PUT', + urlParameters: `/${destination}`, + jsonData, + responseType: 'json', + unauthenticated: true, + accessKey, + }); + } + + function sendMessages( + destination, + messageArray, + timestamp, + silent, + online + ) { + const jsonData = { messages: messageArray, timestamp }; + + if (silent) { + jsonData.silent = true; + } + if (online) { + jsonData.online = true; + } + + return _ajax({ + call: 'messages', + httpType: 'PUT', + urlParameters: `/${destination}`, + jsonData, + responseType: 'json', + }); + } + function getAttachment(fileUrl) { return _outerAjax(fileUrl, { contentType: 'application/octet-stream', + proxyUrl, responseType: 'arraybuffer', timeout: 0, type: 'GET', @@ -344,7 +884,7 @@ function initialize() { const result = await _outerAjax(url, { processData: false, responseType: 'arraybufferwithdetails', - proxyUrl: '', + proxyUrl: contentProxyUrl, type: 'HEAD', disableLogs: true, }); @@ -372,12 +912,38 @@ function initialize() { return _outerAjax(url, { processData: false, responseType: returnArrayBuffer ? 'arraybufferwithdetails' : null, - proxyUrl: '', + proxyUrl: contentProxyUrl, type: 'GET', redirect: 'follow', disableLogs: true, headers, }); } + + function getMessageSocket() { + log.info('opening message socket', url); + const fixedScheme = url + .replace('https://', 'wss://') + .replace('http://', 'ws://'); + const login = encodeURIComponent(username); + const pass = encodeURIComponent(password); + + return _createSocket( + `${fixedScheme}/v1/websocket/?login=${login}&password=${pass}&agent=OWD`, + { certificateAuthority, proxyUrl } + ); + } + + function getProvisioningSocket() { + log.info('opening provisioning socket', url); + const fixedScheme = url + .replace('https://', 'wss://') + .replace('http://', 'ws://'); + + return _createSocket( + `${fixedScheme}/v1/websocket/provisioning/?agent=OWD`, + { certificateAuthority, proxyUrl } + ); + } } } diff --git a/js/views/phone-input-view.js b/js/views/phone-input-view.js new file mode 100644 index 000000000..e9537f901 --- /dev/null +++ b/js/views/phone-input-view.js @@ -0,0 +1,39 @@ +/* global libphonenumber, Whisper */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.PhoneInputView = Whisper.View.extend({ + tagName: 'div', + className: 'phone-input', + templateName: 'phone-number', + initialize() { + this.$('input.number').intlTelInput(); + }, + events: { + change: 'validateNumber', + keyup: 'validateNumber', + }, + validateNumber() { + const input = this.$('input.number'); + const regionCode = this.$('li.active') + .attr('data-country-code') + .toUpperCase(); + const number = input.val(); + + const parsedNumber = libphonenumber.util.parseNumber(number, regionCode); + if (parsedNumber.isValidNumber) { + this.$('.number-container').removeClass('invalid'); + this.$('.number-container').addClass('valid'); + } else { + this.$('.number-container').removeClass('valid'); + } + input.trigger('validation'); + + return parsedNumber.e164; + }, + }); +})(); diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index e037305c5..737bf363c 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -28,6 +28,7 @@ const ARCHIVE_AGE = 7 * 24 * 60 * 60 * 1000; function AccountManager(username, password) { + // this.server = window.WebAPI.connect({ username, password }); this.pending = Promise.resolve(); } @@ -47,8 +48,12 @@ AccountManager.prototype = new textsecure.EventTarget(); AccountManager.prototype.extend({ constructor: AccountManager, - requestVoiceVerification(number) {}, - requestSMSVerification(number) {}, + requestVoiceVerification(number) { + // return this.server.requestVerificationVoice(number); + }, + requestSMSVerification(number) { + // return this.server.requestVerificationSMS(number); + }, async encryptDeviceName(name, providedIdentityKey) { if (!name) { return null; @@ -93,13 +98,23 @@ return name; }, async maybeUpdateDeviceName() { - throw new Error('Signal method called: maybeUpdateDeviceName'); + const isNameEncrypted = textsecure.storage.user.getDeviceNameEncrypted(); + if (isNameEncrypted) { + return; + } + const deviceName = await textsecure.storage.user.getDeviceName(); + const base64 = await this.encryptDeviceName(deviceName); + + await this.server.updateDeviceName(base64); }, async deviceNameIsEncrypted() { await textsecure.storage.user.setDeviceNameEncrypted(); }, async maybeDeleteSignalingKey() { - throw new Error('Signal method called: maybeDeleteSignalingKey'); + const key = await textsecure.storage.user.getSignalingKey(); + if (key) { + await this.server.removeSignalingKey(); + } }, registerSingleDevice(mnemonic, mnemonicLanguage, profileName) { const createAccount = this.createAccount.bind(this); @@ -197,7 +212,19 @@ 'account_manager: registerSecondDevice has not been implemented!' ); }, - refreshPreKeys() {}, + refreshPreKeys() { + // const generateKeys = this.generateKeys.bind(this, 0); + // const registerKeys = this.server.registerKeys.bind(this.server); + // return this.queueTask(() => + // this.server.getMyKeys().then(preKeyCount => { + // window.log.info(`prekey count ${preKeyCount}`); + // if (preKeyCount < 10) { + // return generateKeys().then(registerKeys); + // } + // return null; + // }) + // ); + }, rotateSignedPreKey() { return this.queueTask(() => { const signedKeyId = textsecure.storage.get('signedKeyId', 1); @@ -485,6 +512,7 @@ keyId: res.keyId, publicKey: res.keyPair.pubKey, signature: res.signature, + // server.registerKeys doesn't use keyPair, confirmKeys does keyPair: res.keyPair, }; }) diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 94b14fc7b..ab0f9afb9 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -11,6 +11,7 @@ /* global HttpResource: false */ /* global ContactBuffer: false */ /* global GroupBuffer: false */ +/* global WebSocketResource: false */ /* global lokiPublicChatAPI: false */ /* global lokiMessageAPI: false */ /* global feeds: false */ @@ -31,7 +32,14 @@ function MessageReceiver(username, password, signalingKey, options = {}) { this.signalingKey = signalingKey; this.username = username; this.password = password; - this.server = WebAPI.connect(); + this.server = WebAPI.connect({ username, password }); + + if (!options.serverTrustRoot) { + throw new Error('Server trust root is required!'); + } + this.serverTrustRoot = window.Signal.Crypto.base64ToArrayBuffer( + options.serverTrustRoot + ); const address = libsignal.SignalProtocolAddress.fromString(username); this.number = address.getName(); @@ -102,6 +110,31 @@ MessageReceiver.prototype.extend({ feed.on('rssMessage', this.handleUnencryptedMessage.bind(this)); }); + // TODO: Rework this socket stuff to work with online messaging + const useWebSocket = false; + if (useWebSocket) { + if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { + this.socket.close(); + this.wsr.close(); + } + // initialize the socket and start listening for messages + this.socket = this.server.getMessageSocket(); + this.socket.onclose = this.onclose.bind(this); + this.socket.onerror = this.onerror.bind(this); + this.socket.onopen = this.onopen.bind(this); + this.wsr = new WebSocketResource(this.socket, { + handleRequest: this.handleRequest.bind(this), + keepalive: { + path: '/v1/keepalive', + disconnect: true, + }, + }); + + // Because sometimes the socket doesn't properly emit its close event + this._onClose = this.onclose.bind(this); + this.wsr.addEventListener('close', this._onClose); + } + // Ensures that an immediate 'empty' event from the websocket will fire only after // all cached envelopes are processed. this.incoming = [this.pending]; @@ -130,11 +163,29 @@ MessageReceiver.prototype.extend({ this.stoppingProcessing = true; return this.close(); }, - shutdown() {}, + shutdown() { + if (this.socket) { + this.socket.onclose = null; + this.socket.onerror = null; + this.socket.onopen = null; + this.socket = null; + } + + if (this.wsr) { + this.wsr.removeEventListener('close', this._onClose); + this.wsr = null; + } + }, async close() { window.log.info('MessageReceiver.close()'); this.calledClose = true; + // Our WebSocketResource instance will close the socket and emit a 'close' event + // if the socket doesn't emit one quickly enough. + if (this.wsr) { + this.wsr.close(3000, 'called close'); + } + // stop polling all open group rooms if (lokiPublicChatAPI) { await lokiPublicChatAPI.close(); @@ -169,6 +220,27 @@ MessageReceiver.prototype.extend({ 'calledClose:', this.calledClose ); + // TODO: handle properly + // this.shutdown(); + + // if (this.calledClose) { + // return Promise.resolve(); + // } + // if (ev.code === 3000) { + // return Promise.resolve(); + // } + // if (ev.code === 3001) { + // this.onEmpty(); + // } + // // possible 403 or network issue. Make an request to confirm + // return this.server + // .getDevices(this.number) + // .then(this.connect.bind(this)) // No HTTP error? Reconnect + // .catch(e => { + // const event = new Event('error'); + // event.error = e; + // return this.dispatchAndWait(event); + // }); }, handleRequest(request, options) { const { onSuccess, onFailure } = options; @@ -565,7 +637,9 @@ MessageReceiver.prototype.extend({ ? WebSocket.OPEN : WebSocket.CLOSED; } - if (this.hasConnected) { + if (this.socket) { + return this.socket.readyState; + } else if (this.hasConnected) { return WebSocket.CLOSED; } return -1; diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 5feb6d648..369f8ecee 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -178,6 +178,10 @@ OutgoingMessage.prototype = { return false; }) ); + // TODO: check if still applicable + // if (updateDevices === undefined) { + // return this.server.getKeysForNumber(number, '*').then(handleResult); + // } let promise = Promise.resolve(true); updateDevices.forEach(device => { promise = promise.then(() => diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index a0bfe4267..8775c9987 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -184,8 +184,8 @@ Message.prototype = { }, }; -function MessageSender() { - this.server = WebAPI.connect(); +function MessageSender(username, password) { + this.server = WebAPI.connect({ username, password }); this.pendingMessages = {}; } @@ -595,8 +595,16 @@ MessageSender.prototype = { ); }, - async getProfile() { - throw new Error('Signal code called. This needs to be removed'); + async getProfile(number, { accessKey } = {}) { + if (accessKey) { + return this.server.getProfileUnauth(number, { accessKey }); + } + + return this.server.getProfile(number); + }, + + getAvatar(path) { + return this.server.getAvatar(path); }, uploadAvatar(attachment) { @@ -1330,6 +1338,7 @@ textsecure.MessageSender = function MessageSenderWrapper(username, password) { this.leaveGroup = sender.leaveGroup.bind(sender); this.sendSyncMessage = sender.sendSyncMessage.bind(sender); this.getProfile = sender.getProfile.bind(sender); + this.getAvatar = sender.getAvatar.bind(sender); this.uploadAvatar = sender.uploadAvatar.bind(sender); this.syncReadMessages = sender.syncReadMessages.bind(sender); this.syncVerification = sender.syncVerification.bind(sender); diff --git a/libtextsecure/test/fake_web_api.js b/libtextsecure/test/fake_web_api.js index 3c38a806d..29e2a932f 100644 --- a/libtextsecure/test/fake_web_api.js +++ b/libtextsecure/test/fake_web_api.js @@ -1,11 +1,56 @@ window.setImmediate = window.nodeSetImmediate; +const getKeysForNumberMap = {}; +const messagesSentMap = {}; + const fakeCall = () => Promise.resolve(); const fakeAPI = { + confirmCode: fakeCall, getAttachment: fakeCall, + getAvatar: fakeCall, + getDevices: fakeCall, + // getKeysForNumber: fakeCall, + getMessageSocket: fakeCall, + getMyKeys: fakeCall, + getProfile: fakeCall, + getProvisioningSocket: fakeCall, putAttachment: fakeCall, - putAvatar: fakeCall, + registerKeys: fakeCall, + requestVerificationSMS: fakeCall, + requestVerificationVoice: fakeCall, + // sendMessages: fakeCall, + setSignedPreKey: fakeCall, + + getKeysForNumber(number) { + const res = getKeysForNumberMap[number]; + if (res !== undefined) { + delete getKeysForNumberMap[number]; + return Promise.resolve(res); + } + throw new Error('getKeysForNumber of unknown/used number'); + }, + + sendMessages(destination, messageArray) { + for (let i = 0, max = messageArray.length; i < max; i += 1) { + const msg = messageArray[i]; + if ( + (msg.type !== 1 && msg.type !== 3) || + msg.destinationDeviceId === undefined || + msg.destinationRegistrationId === undefined || + msg.body === undefined || + msg.timestamp === undefined || + msg.relay !== undefined || + msg.destination !== undefined + ) { + throw new Error('Invalid message'); + } + + messagesSentMap[ + `${destination}.${messageArray[i].destinationDeviceId}` + ] = msg; + } + }, }; window.WebAPI = { diff --git a/package.json b/package.json index 2ffe24b82..224bd06f6 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "dependencies": { "@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#b10f232fac62ba7f8775c9e086bb5558fe7d948b", "@sindresorhus/is": "0.8.0", + "@types/dompurify": "^2.0.0", + "@types/rc-slider": "^8.6.5", "backbone": "1.3.3", "blob-util": "1.3.0", "blueimp-canvas-to-blob": "3.14.0", @@ -81,8 +83,13 @@ "form-data": "^3.0.0", "fs-extra": "9.0.0", "glob": "7.1.2", + "google-libphonenumber": "3.2.2", + "got": "8.2.0", "he": "1.2.0", + "intl-tel-input": "12.1.15", "jquery": "3.3.1", + "js-sha512": "0.8.0", + "js-yaml": "3.13.0", "jsbn": "1.1.0", "libsodium-wrappers": "^0.7.6", "linkify-it": "2.0.3", @@ -91,10 +98,12 @@ "moment": "2.21.0", "mustache": "2.3.0", "node-fetch": "2.3.0", + "node-gyp": "3.8.0", "node-sass": "4.9.3", "os-locale": "2.1.0", "pify": "3.0.0", "protobufjs": "6.8.6", + "proxy-agent": "3.0.3", "rc-slider": "^8.7.1", "react": "16.8.3", "react-contextmenu": "2.11.0", @@ -111,28 +120,31 @@ "rimraf": "2.6.2", "semver": "5.4.1", "tar": "4.4.8", + "testcheck": "1.0.0-rc.2", "tmp": "0.0.33", "to-arraybuffer": "1.0.1", "underscore": "1.9.0", - "uuid": "3.3.2" + "uuid": "3.3.2", + "websocket": "1.0.28" }, "devDependencies": { "@types/chai": "4.1.2", "@types/classnames": "2.2.3", "@types/color": "^3.0.0", "@types/config": "0.0.34", - "@types/dompurify": "^2.0.0", "@types/electron-is-dev": "^1.1.1", "@types/filesize": "3.6.0", "@types/fs-extra": "5.0.5", + "@types/google-libphonenumber": "7.4.14", + "@types/got": "9.4.1", "@types/jquery": "3.3.29", + "@types/js-yaml": "3.12.0", "@types/linkify-it": "2.0.3", "@types/lodash": "4.14.106", "@types/mkdirp": "0.5.2", "@types/mocha": "5.0.0", "@types/pify": "3.0.2", "@types/qs": "6.5.1", - "@types/rc-slider": "^8.6.5", "@types/react": "16.8.5", "@types/react-dom": "16.8.2", "@types/react-portal": "^4.0.2", @@ -170,7 +182,6 @@ "grunt-sass": "3.0.1", "mocha": "4.1.0", "mocha-testcheck": "1.0.0-rc.0", - "node-gyp": "3.8.0", "node-sass-import-once": "1.2.0", "nyc": "11.4.1", "prettier": "1.12.0", diff --git a/preload.js b/preload.js index 4d513718c..cb4109a09 100644 --- a/preload.js +++ b/preload.js @@ -312,7 +312,13 @@ window.nodeSetImmediate = setImmediate; const { initialize: initializeWebAPI } = require('./js/modules/web_api'); -window.WebAPI = initializeWebAPI(); +window.WebAPI = initializeWebAPI({ + url: config.serverUrl, + cdnUrl: config.cdnUrl, + certificateAuthority: config.certificateAuthority, + contentProxyUrl: config.contentProxyUrl, + proxyUrl: config.proxyUrl, +}); window.seedNodeList = JSON.parse(config.seedNodeList); const LokiSnodeAPI = require('./js/modules/loki_snode_api'); @@ -356,6 +362,8 @@ window.dataURLToBlobSync = require('blueimp-canvas-to-blob'); window.emojiData = require('emoji-datasource'); window.EmojiPanel = require('emoji-panel'); window.filesize = require('filesize'); +window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInstance(); +window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat; window.loadImage = require('blueimp-load-image'); window.getGuid = require('uuid/v4'); window.profileImages = require('./app/profile_images'); diff --git a/stylesheets/_options.scss b/stylesheets/_options.scss index 51b78dccd..a28bbafcb 100644 --- a/stylesheets/_options.scss +++ b/stylesheets/_options.scss @@ -1,2 +1,15 @@ @import 'variables'; +@import '../node_modules/intl-tel-input/build/css/intlTelInput.css'; @import 'progress'; +.iti-flag { + // override intlTelInput's flags image location + background: url('../node_modules/intl-tel-input/build/img/flags.png'); +} + +.intl-tel-input .country-list { + text-align: left; +} + +.intl-tel-input .country-list .country .country-name { + color: #000; +} diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index 6ff2f4e76..39de2faad 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -1670,6 +1670,10 @@ body.dark-theme { // _options + .intl-tel-input .country-list .country .country-name { + color: #000; + } + // _progress // Not sure we need to change anything there - it's blue diff --git a/test/index.html b/test/index.html index a779fba6d..13477e485 100644 --- a/test/index.html +++ b/test/index.html @@ -500,6 +500,7 @@ + @@ -547,6 +548,7 @@ + @@ -574,6 +576,7 @@ + diff --git a/test/libphonenumber_util_test.js b/test/libphonenumber_util_test.js new file mode 100644 index 000000000..0e478e4eb --- /dev/null +++ b/test/libphonenumber_util_test.js @@ -0,0 +1,26 @@ +/* global libphonenumber */ + +'use strict'; + +describe('libphonenumber util', () => { + describe('parseNumber', () => { + it('numbers with + are valid without providing regionCode', () => { + const result = libphonenumber.util.parseNumber('+14155555555'); + assert.isTrue(result.isValidNumber); + assert.strictEqual(result.nationalNumber, '4155555555'); + assert.strictEqual(result.e164, '+14155555555'); + assert.strictEqual(result.regionCode, 'US'); + assert.strictEqual(result.countryCode, '1'); + }); + it('variant numbers with the right regionCode are valid', () => { + ['4155555555', '14155555555', '+14155555555'].forEach(number => { + const result = libphonenumber.util.parseNumber(number, 'US'); + assert.isTrue(result.isValidNumber); + assert.strictEqual(result.nationalNumber, '4155555555'); + assert.strictEqual(result.e164, '+14155555555'); + assert.strictEqual(result.regionCode, 'US'); + assert.strictEqual(result.countryCode, '1'); + }); + }); + }); +}); diff --git a/test/modules/privacy_test.js b/test/modules/privacy_test.js index e51959f6a..df3490bd3 100644 --- a/test/modules/privacy_test.js +++ b/test/modules/privacy_test.js @@ -7,27 +7,18 @@ const Privacy = require('../../js/modules/privacy'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); describe('Privacy', () => { - describe('redactSessionID', () => { - it('should redact all session IDs', () => { + describe('redactPhoneNumbers', () => { + it('should redact all phone numbers', () => { const text = - 'This is a log line with a session ID 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827\n' + - 'and another one 05766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab62'; + 'This is a log line with a phone number +12223334455\n' + + 'and another one +13334445566'; - const actual = Privacy.redactSessionID(text); + const actual = Privacy.redactPhoneNumbers(text); const expected = - 'This is a log line with a session ID [REDACTED]\n' + - 'and another one [REDACTED]'; + 'This is a log line with a phone number +[REDACTED]455\n' + + 'and another one +[REDACTED]566'; assert.equal(actual, expected); }); - - it('should not redact non session IDS', () => { - const text = - 'This is a log line with a non-session ID sadsad0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827888\n' + - 'and another one 766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab6234'; - - const actual = Privacy.redactSessionID(text); - assert.equal(actual, text); - }); }); describe('redactGroupIds', () => { @@ -62,20 +53,20 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${APP_ROOT_PATH}/main.js\n` + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + `path2 file:///${encodedAppRootPath}/js/background.js.` + - 'phone2 0531033dc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 lorem\n' + + 'phone2 +13334445566 lorem\n' + 'group2 group(abcdefghij) doloret\n'; const actual = Privacy.redactAll(text); const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 [REDACTED] ipsum\n' + + 'phone1 +[REDACTED]455 ipsum\n' + 'group1 group([REDACTED]789) doloret\n' + 'path2 file:///[REDACTED]/js/background.js.' + - 'phone2 [REDACTED] lorem\n' + + 'phone2 +[REDACTED]566 lorem\n' + 'group2 group([REDACTED]hij) doloret\n'; assert.equal(actual, expected); }); @@ -87,13 +78,13 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${testPath}/main.js\n` + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n'; + 'phone1 +12223334455 ipsum\n'; const actual = Privacy._redactPath(testPath)(text); const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n'; + 'phone1 +12223334455 ipsum\n'; assert.equal(actual, expected); }); @@ -103,7 +94,7 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${testPath}/main.js\n` + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + `path2 file:///${encodedTestPath}/js/background.js.`; @@ -111,7 +102,7 @@ describe('Privacy', () => { const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + 'path2 file:///[REDACTED]/js/background.js.'; assert.equal(actual, expected); diff --git a/ts/util/libphonenumberInstance.ts b/ts/util/libphonenumberInstance.ts new file mode 100644 index 000000000..40855ac23 --- /dev/null +++ b/ts/util/libphonenumberInstance.ts @@ -0,0 +1,6 @@ +import libphonenumber from 'google-libphonenumber'; + +const instance = libphonenumber.PhoneNumberUtil.getInstance(); +const PhoneNumberFormat = libphonenumber.PhoneNumberFormat; + +export { instance, PhoneNumberFormat }; diff --git a/ts/util/lint/linter.ts b/ts/util/lint/linter.ts index a0773fbc6..1ada85d60 100644 --- a/ts/util/lint/linter.ts +++ b/ts/util/lint/linter.ts @@ -122,6 +122,7 @@ const excludedFiles = [ '^node_modules/hpack\\.js/*', '^node_modules/http-proxy-middlewar/*', '^node_modules/icss-utils/*', + '^node_modules/intl-tel-input/examples/*', '^node_modules/istanbul*', '^node_modules/jimp/*', '^node_modules/jquery/*', @@ -160,6 +161,7 @@ const excludedFiles = [ '^node_modules/spectron/*', '^node_modules/style-loader/*', '^node_modules/svgo/*', + '^node_modules/testcheck/*', '^node_modules/text-encoding/*', '^node_modules/tinycolor2/*', '^node_modules/to-ast/*', diff --git a/yarn.lock b/yarn.lock index f49865784..6a4f27191 100644 --- a/yarn.lock +++ b/yarn.lock @@ -113,6 +113,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" + integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -242,6 +247,19 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/google-libphonenumber@7.4.14": + version "7.4.14" + resolved "https://registry.yarnpkg.com/@types/google-libphonenumber/-/google-libphonenumber-7.4.14.tgz#3625d7aed0c16df920588428c86f0538bd0612ec" + integrity sha512-HGQobTn8CGn0/j7lezIHlGTE1czxQU6NeT7TYtC3OWcbyyUUSfdt4tXSTEGbXmNZlE+xczTDbyR8P6aYGPzG1A== + +"@types/got@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@types/got/-/got-9.4.1.tgz#7479a3a321599b5e17647f3bd9d402b8c55bfee1" + integrity sha512-m7Uc07bG/bZ+Dis7yI3mGssYDcAdUvP4irF3ZmBzf0ig7zEd1FyADfnELVGcf+p1Ol/iPCXbZYwcSNOJA2a+Qg== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + "@types/jquery@3.3.29": version "3.3.29" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd" @@ -249,6 +267,11 @@ dependencies: "@types/sizzle" "*" +"@types/js-yaml@3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.0.tgz#3494ce97358e2675e24e97a747ec23478eeaf8b6" + integrity sha512-UGEe/6RsNAxgWdknhzFZbCxuYc5I7b/YEKlfKbo+76SM8CJzGs7XKCj7zyugXViRbKYpXhSXhCYVQZL5tmDbpQ== + "@types/linkify-it@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.0.3.tgz#5352a2d7a35d7c77b527483cd6e68da9148bd780" @@ -409,6 +432,11 @@ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== +"@types/tough-cookie@*": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.6.tgz#c880579e087d7a0db13777ff8af689f4ffc7b0d5" + integrity sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ== + "@types/trusted-types@*": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.4.tgz#922d092c84a776a59acb0bd6785fd82b59b9bad5" @@ -516,6 +544,20 @@ address@^1.0.1: resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== +agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" @@ -886,6 +928,11 @@ ast-types@0.9.11: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.11.tgz#371177bb59232ff5ceaa1d09ee5cad705b1a5aa9" integrity sha1-NxF3u1kjL/XOqh0J7lytcFsaWqk= +ast-types@0.x.x: + version "0.13.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== + ast-types@^0.7.2: version "0.7.8" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" @@ -1548,6 +1595,19 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" + integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -1859,7 +1919,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-response@^1.0.2: +clone-response@1.0.2, clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= @@ -2492,6 +2552,11 @@ dashdash@1.14.1, dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== + dateformat@~1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" @@ -2505,7 +2570,7 @@ debug-log@^1.0.1: resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" integrity sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8= -debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2662,6 +2727,15 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= +degenerator@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + del@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" @@ -3169,6 +3243,13 @@ es6-promise@^4.0.3, es6-promise@^4.2.4: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + escape-goat@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" @@ -3189,7 +3270,7 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^1.9.1: +escodegen@1.x.x, escodegen@^1.9.1: version "1.14.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== @@ -3328,6 +3409,11 @@ espree@^3.5.2: acorn "^5.5.0" acorn-jsx "^3.0.0" +esprima@3.x.x: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + esprima@^2.1.0, esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -3658,7 +3744,7 @@ file-type@^3.1.0: resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= -file-uri-to-path@1.0.0: +file-uri-to-path@1, file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== @@ -3868,7 +3954,7 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -from2@^2.1.0: +from2@^2.1.0, from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= @@ -3979,6 +4065,14 @@ fstream@^1.0.0, fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4045,7 +4139,7 @@ get-stdin@^5.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= -get-stream@^3.0.0: +get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= @@ -4064,6 +4158,18 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-uri@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.4.tgz#d4937ab819e218d4cb5ae18e4f5962bef169cc6a" + integrity sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q== + dependencies: + data-uri-to-buffer "1" + debug "2" + extend "~3.0.2" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "2" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4283,6 +4389,34 @@ glogg@^1.0.1: dependencies: sparkles "^1.0.0" +google-libphonenumber@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.2.tgz#3d9d7ba727e99a50812f21b0ed313723b76c5c54" + integrity sha512-ubjGeosYPeusjYbUHy76lCniGTTI0k1rIFc+uKBX+jHQLDmWOSUtlFUxaeoLJ+Y+PAMM6dWp+C1HjHx5BI8kEw== + +got@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/got/-/got-8.2.0.tgz#0d11a071d05046348a2f5c0a5fa047fb687fdfc6" + integrity sha512-giadqJpXIwjY+ZsuWys8p2yjZGhOHiU4hiJHjS/oeCxw1u8vANQz3zPlrxW2Zw/siCXsSMI3hvzWGcnFyujyAg== + dependencies: + "@sindresorhus/is" "^0.7.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -4499,11 +4633,23 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -4651,6 +4797,11 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -4672,6 +4823,17 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" @@ -4690,22 +4852,19 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - "http-parser-js@>=0.4.0 <0.4.11": version "0.4.10" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + http-proxy-middleware@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -4739,6 +4898,22 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +https-proxy-agent@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz#b8c286433e87602311b01c8ea34413d856a4af81" + integrity sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + hyphenate-style-name@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" @@ -4903,6 +5078,19 @@ internal-ip@1.2.0: dependencies: meow "^3.3.0" +intl-tel-input@12.1.15: + version "12.1.15" + resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-12.1.15.tgz#7393e6b77572731bbc65ca4585782e8ba3d74de4" + integrity sha512-9TN9x6aGdO1eL6iGFpobuLU4UymZqjSnS9UnsOSi//LU3A8nkLOcokSYBYjak18Uu8OM59HsGYDd1jKmwRsskw== + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" + integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" + invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -4920,7 +5108,7 @@ ip-regex@^1.0.1: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0= -ip@^1.1.0, ip@^1.1.5: +ip@1.1.5, ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -5187,6 +5375,11 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -5255,6 +5448,11 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== +is-retry-allowed@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + is-root@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5" @@ -5409,6 +5607,14 @@ istanbul-reports@^1.1.3: dependencies: handlebars "^4.0.3" +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + javascript-stringify@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" @@ -5451,6 +5657,11 @@ js-base64@^2.1.8, js-base64@^2.1.9: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== +js-sha512@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" + integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== + "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -5461,6 +5672,14 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= +js-yaml@3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e" + integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@^3.13.1, js-yaml@^3.2.7, js-yaml@^3.7.0, js-yaml@^3.9.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" @@ -5664,6 +5883,13 @@ keyboardevents-areequal@^0.2.1: resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194" integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw== +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" + integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== + dependencies: + json-buffer "3.0.0" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -5975,6 +6201,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lowercase-keys@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -5985,7 +6216,7 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^4.0.1, lru-cache@^4.1.1: +lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -6478,7 +6709,7 @@ mz@^2.3.1: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.10.0, nan@^2.12.1, nan@^2.14.0: +nan@^2.10.0, nan@^2.11.0, nan@^2.12.1, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -6536,6 +6767,11 @@ neo-async@^2.5.0, neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +netmask@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= + nise@^1.2.0: version "1.5.3" resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" @@ -6708,6 +6944,15 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + normalize-url@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -7034,6 +7279,11 @@ osenv@0, osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -7044,6 +7294,11 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= + p-limit@^1.0.0, p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -7077,6 +7332,13 @@ p-map@^1.1.1: resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== + dependencies: + p-finally "^1.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -7087,6 +7349,31 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz#115b1e58f92576cac2eba718593ca7b0e37de2ad" + integrity sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ== + dependencies: + agent-base "^4.2.0" + debug "^4.1.1" + get-uri "^2.0.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^3.0.0" + pac-resolver "^3.0.0" + raw-body "^2.2.0" + socks-proxy-agent "^4.0.1" + +pac-resolver@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" + integrity sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA== + dependencies: + co "^4.6.0" + degenerator "^1.0.4" + ip "^1.1.5" + netmask "^1.0.6" + thunkify "^2.1.2" + package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -7801,6 +8088,25 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" +proxy-agent@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.3.tgz#1c1a33db60ef5f2e9e35b876fd63c2bc681c611d" + integrity sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA== + dependencies: + agent-base "^4.2.0" + debug "^3.1.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + lru-cache "^4.1.2" + pac-proxy-agent "^3.0.0" + proxy-from-env "^1.0.0" + socks-proxy-agent "^4.0.1" + +proxy-from-env@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -7927,6 +8233,15 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -7993,6 +8308,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + raw-body@~2.1.5: version "2.1.7" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" @@ -8364,7 +8689,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8377,7 +8702,7 @@ read-pkg@^2.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^1.1.8, readable-stream@~1.1.9: +readable-stream@1.1.x, readable-stream@^1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -8794,7 +9119,7 @@ resolve@~1.1.0: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -responselike@^1.0.2: +responselike@1.0.2, responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= @@ -9210,6 +9535,11 @@ slide@^1.1.5: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -9260,6 +9590,22 @@ sockjs@0.3.19: faye-websocket "^0.10.0" uuid "^3.0.1" +socks-proxy-agent@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" + integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== + dependencies: + ip "1.1.5" + smart-buffer "^4.1.0" + sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -9267,6 +9613,13 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -9858,7 +10211,7 @@ test-exclude@^4.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -testcheck@^1.0.0-rc: +testcheck@1.0.0-rc.2, testcheck@^1.0.0-rc: version "1.0.0-rc.2" resolved "https://registry.yarnpkg.com/testcheck/-/testcheck-1.0.0-rc.2.tgz#11356a25b84575efe0b0857451e85b5fa74ee4e4" integrity sha1-ETVqJbhFde/gsIV0UehbX6dO5OQ= @@ -9913,6 +10266,11 @@ through@2, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +thunkify@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -9923,6 +10281,11 @@ time-stamp@^2.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz#917e0a66905688790ec7bbbde04046259af83f57" integrity sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA== +timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + timers-browserify@^2.0.4: version "2.0.11" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" @@ -10477,6 +10840,11 @@ url-regex@^3.0.0: dependencies: ip-regex "^1.0.1" +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -10751,6 +11119,16 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== +websocket@1.0.28: + version "1.0.28" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3" + integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA== + dependencies: + debug "^2.2.0" + nan "^2.11.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + wgxpath@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690" @@ -10909,6 +11287,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -10931,6 +11314,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" From 82fdd6570d08cbef910cbc101ac0ca127455e26d Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 3 Apr 2020 16:27:50 +1100 Subject: [PATCH 25/30] update package.json to v1.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e4b371f68..5bc9ba2a7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "session-messenger-desktop", "productName": "Session", "description": "Private messaging from your desktop", - "version": "1.0.5", + "version": "1.0.6", "license": "GPL-3.0", "author": { "name": "Loki Project", From fead4fffeff9e5d81705ab2904176822f6cf2d49 Mon Sep 17 00:00:00 2001 From: Vince Date: Fri, 3 Apr 2020 16:41:59 +1100 Subject: [PATCH 26/30] Revert "Clearnet --> Master for v1.0.6" --- .eslintignore | 1 + .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/build-binaries.yml | 1 - .github/workflows/pull-request.yml | 3 +- .github/workflows/release.yml | 1 - BUILDING.md | 61 +- CONTRIBUTING.md | 63 +- Gruntfile.js | 23 +- _locales/en/messages.json | 52 +- app/config.js | 27 +- app/password_util.js | 8 +- app/spell_check.js | 88 - app/sql.js | 64 - app/sql_channel.js | 5 +- app/user_config.js | 26 +- background.html | 2 + background_test.html | 529 ----- bower.json | 1 + config/default.json | 1 + config/development-1.json | 16 + config/development-2.json | 16 + config/development.json | 1 + ...uction-devprod.json => local-devprod.json} | 1 + config/local-devprod1.json | 6 + config/production-devprod1.json | 4 - config/staging.json | 4 + config/swarm-testing.json | 1 + config/swarm-testing2.json | 11 + config/test-integration.json | 4 - integration_test/add_friends_test.js | 119 -- integration_test/closed_group_test.js | 129 -- integration_test/common.js | 498 ----- integration_test/integration_test.js | 23 - integration_test/link_device_test.js | 48 - integration_test/open_group_test.js | 151 -- integration_test/page-objects/common.page.js | 24 - .../page-objects/conversation.page.js | 121 -- .../page-objects/registration.page.js | 40 - integration_test/registration_test.js | 143 -- .../stubs/stub_app_dot_net_api.js | 166 -- integration_test/stubs/stub_message_api.js | 46 - js/background.js | 80 +- js/conversation_controller.js | 1 - js/expire.js | 12 +- js/libphonenumber-util.js | 295 +++ js/models/conversations.js | 17 +- js/modules/data.js | 12 - js/modules/debuglogs.js | 44 +- js/modules/loki_app_dot_net_api.js | 38 +- js/modules/loki_file_server_api.js | 7 +- js/modules/loki_message_api.js | 30 +- js/modules/loki_public_chat_api.js | 7 +- js/modules/loki_rpc.js | 352 +--- js/modules/loki_snode_api.js | 522 +---- js/modules/privacy.js | 10 +- js/modules/refresh_sender_certificate.js | 120 ++ js/modules/signal.js | 2 + js/modules/web_api.js | 576 +++++- js/spell_check.js | 180 ++ js/views/create_group_dialog_view.js | 4 +- js/views/phone-input-view.js | 39 + js/views/standalone_registration_view.js | 490 +++++ libloki/crypto.js | 126 +- libloki/storage.js | 10 - libloki/test/index.html | 1 + libloki/test/snode_channel_test.js | 141 ++ libtextsecure/account_manager.js | 64 +- libtextsecure/message_receiver.js | 85 +- libtextsecure/outgoing_message.js | 4 + libtextsecure/sendmessage.js | 17 +- libtextsecure/stringview.js | 1 - libtextsecure/test/fake_web_api.js | 47 +- main.js | 124 +- package.json | 69 +- password_preload.js | 2 +- preload.js | 109 +- prepare_beta_build.js | 66 + prepare_import_build.js | 69 + settings.html | 184 ++ stylesheets/_mentions.scss | 4 +- stylesheets/_options.scss | 13 + stylesheets/_password.scss | 50 + stylesheets/_session.scss | 170 +- stylesheets/_session_left_pane.scss | 22 +- stylesheets/_session_password.scss | 89 - stylesheets/_theme_dark.scss | 4 + stylesheets/manifest.scss | 9 +- test/index.html | 4 + test/libphonenumber_util_test.js | 26 + test/modules/privacy_test.js | 39 +- test/spellcheck_test.js | 12 + ts/components/DevicePairingDialog.tsx | 21 +- ts/components/MainViewController.tsx | 9 +- .../conversation/ConversationHeader.tsx | 39 +- ts/components/conversation/Message.tsx | 10 +- .../conversation/UpdateGroupMembersDialog.tsx | 14 +- ts/components/session/ActionsPanel.tsx | 30 +- .../session/LeftPaneSectionHeader.tsx | 28 +- ts/components/session/RegistrationTabs.tsx | 102 +- .../session/SessionClosableOverlay.tsx | 3 +- ts/components/session/SessionConfirm.tsx | 4 +- .../session/SessionGroupSettings.tsx | 2 +- ts/components/session/SessionIdEditable.tsx | 3 +- ts/components/session/SessionInput.tsx | 4 - .../session/SessionNotificationCount.tsx | 77 - .../session/SessionPasswordModal.tsx | 53 +- .../session/SessionPasswordPrompt.tsx | 40 +- ts/components/session/SessionQRModal.tsx | 2 +- ts/components/session/SessionSeedModal.tsx | 2 +- .../session/icon/SessionIconButton.tsx | 17 +- .../session/settings/SessionSettings.tsx | 21 +- .../settings/SessionSettingsHeader.tsx | 21 +- ts/updater/common.ts | 27 +- ts/updater/updater.ts | 2 +- ts/util/libphonenumberInstance.ts | 6 + ts/util/lint/linter.ts | 2 + yarn.lock | 1820 ++++++++++------- 117 files changed, 4383 insertions(+), 4905 deletions(-) delete mode 100644 app/spell_check.js delete mode 100644 background_test.html create mode 100644 config/development-1.json create mode 100644 config/development-2.json rename config/{production-devprod.json => local-devprod.json} (58%) create mode 100644 config/local-devprod1.json delete mode 100644 config/production-devprod1.json create mode 100644 config/staging.json create mode 100644 config/swarm-testing2.json delete mode 100644 config/test-integration.json delete mode 100644 integration_test/add_friends_test.js delete mode 100644 integration_test/closed_group_test.js delete mode 100644 integration_test/common.js delete mode 100644 integration_test/integration_test.js delete mode 100644 integration_test/link_device_test.js delete mode 100644 integration_test/open_group_test.js delete mode 100644 integration_test/page-objects/common.page.js delete mode 100644 integration_test/page-objects/conversation.page.js delete mode 100644 integration_test/page-objects/registration.page.js delete mode 100644 integration_test/registration_test.js delete mode 100644 integration_test/stubs/stub_app_dot_net_api.js delete mode 100644 integration_test/stubs/stub_message_api.js create mode 100644 js/libphonenumber-util.js create mode 100644 js/modules/refresh_sender_certificate.js create mode 100644 js/spell_check.js create mode 100644 js/views/phone-input-view.js create mode 100644 js/views/standalone_registration_view.js create mode 100644 libloki/test/snode_channel_test.js create mode 100644 prepare_beta_build.js create mode 100644 prepare_import_build.js create mode 100644 settings.html create mode 100644 stylesheets/_password.scss delete mode 100644 stylesheets/_session_password.scss create mode 100644 test/libphonenumber_util_test.js create mode 100644 test/spellcheck_test.js delete mode 100644 ts/components/session/SessionNotificationCount.tsx create mode 100644 ts/util/libphonenumberInstance.ts diff --git a/.eslintignore b/.eslintignore index 9957d65d8..a1a37d7f5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -19,6 +19,7 @@ test/test.js # Third-party files js/Mp3LameEncoder.min.js js/WebAudioRecorderMp3.js +js/libphonenumber-util.js js/libsignal-protocol-worker.js libtextsecure/libsignal-protocol.js libtextsecure/test/blanket_mocha.js diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c95c54b3b..650a4416a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,7 +14,7 @@ Remember, you can preview this before saving it. ### Contributor checklist: * [ ] My commits are in nice logical chunks with [good commit messages](http://chris.beams.io/posts/git-commit/) -* [ ] My changes are [rebased](https://blog.axosoft.com/golden-rule-of-rebasing-in-git/) on the latest [`clearnet`](https://github.com/loki-project/loki-messenger/tree/clearnet) branch +* [ ] My changes are [rebased](https://medium.freecodecamp.org/git-rebase-and-the-golden-rule-explained-70715eccc372) on the latest [`clearnet`](https://github.com/loki-project/loki-messenger/tree/development) branch * [ ] A `yarn ready` run passes successfully ([more about tests here](https://github.com/loki-project/loki-messenger/blob/master/CONTRIBUTING.md#tests)) * [ ] My changes are ready to be shipped to users diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 01b436bed..19760fd1a 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -46,7 +46,6 @@ jobs: run: yarn generate - name: Lint Files - if: runner.os != 'Windows' run: yarn lint-full - name: Build windows production binaries diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7e74cb96d..9cab74b71 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -47,9 +47,8 @@ jobs: run: yarn generate - name: Lint Files - if: runner.os != 'Windows' run: | - yarn format-full + yarn format-full --list-different yarn eslint yarn tslint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7fe998f3f..4f56cc97b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,6 @@ jobs: run: yarn generate - name: Lint Files - if: runner.os != 'Windows' run: yarn lint-full - name: Build windows production binaries diff --git a/BUILDING.md b/BUILDING.md index f205e7dc0..ae7ed1c51 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,17 +1,15 @@ # Building -## Automated +Building session binaries is done using github actions. Windows and linux binaries will build right out of the box but there are some extra steps needed for Mac OS -Automatic building of session binaries is done using github actions. Windows and linux binaries will build right out of the box but there are some extra steps needed for Mac OS - -### Mac OS +## Mac OS The build script for Mac OS requires you to have a valid `Developer ID Application` certificate. Without this the build script cannot sign and notarize the mac binary which is needed for Catalina 10.15 and above. If you would like to disable this then comment out `"afterSign": "build/notarize.js",` in package.json. You will also need an [App-specific password](https://support.apple.com/en-al/HT204397) for the apple account you wish to notarize with -#### Setup +### Setup Once you have your `Developer ID Application` you need to export it into a `.p12` file. Keep a note of the password used to encrypt this file as it will be needed later. @@ -42,56 +40,3 @@ base64 -i certificate.p12 -o encoded.txt 5. Team ID (Optional) * Name: `SIGNING_TEAM_ID` * Value: The apple team id if you're sigining the application for a team - -## Manual - -### Node version - -You will need node `10.13.0`. -This can be done by using [nvm](https://github.com/nvm-sh/nvm) and running `nvm use` or you can install it manually. - -### Prerequisites - -
-Windows - -Building on windows should work straight out of the box, but if it fails then you will need to run the following: - -``` -npm install --global --production windows-build-tools@4.0.0 -npm install --global node-gyp@latest -npm config set python python2.7 -npm config set msvs_version 2015 -``` - -
- -
-Mac - -If you are going to distribute the binary then make sure you have a `Developer ID Application` certificate in your keychain. - -You will then need to generate an [app specific password](https://support.apple.com/HT204397) for your Apple ID. - -Then run the following to export the variables - -``` -export SIGNING_APPLE_ID= -export SIGNING_APP_PASSWORD= -export SIGNING_TEAM_ID= -``` - -
- -### Commands - -Run the following to build the binaries for your specific system OS. - -``` -npm install yarn --no-save -yarn install --frozen-lockfile -yarn generate -yarn build-release -``` - -The binaries will be placed inside the `release/` folder. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a13f984f..04557db73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,46 +82,40 @@ while you make changes: yarn grunt dev # runs until you stop it, re-generating built assets on file changes ``` -## Multiple instances +## Additional storage profiles Since there is no registration for Session, you can create as many accounts as you -can public keys. Each client however has a dedicated storage profile which is determined by the environment and instance variables. +can public keys. To test the P2P functionality on the same machine, however, requries +that each client binds their message server to a different port. -This profile will change [userData](https://electron.atom.io/docs/all/#appgetpathname) -directory from `%appData%/Session` to `%appData%/Session-{environment}-{instance}`. - -There are a few scripts which you can use: - -``` -yarn start - Start development -yarn start-multi - Start second instance of development -yarn start-prod - Start production but in development mode -yarn start-prod-multi - Start another instance of production -``` - -For more than 2 clients, you may run the above command with `NODE_APP_INSTANCE` set before them. -For example, running: +You can use the following command to start a client bound to a different port. ``` -NODE_APP_INSTANCE=alice yarn start +yarn start-multi ``` -Will run the development environment with the `alice` instance and thus create a seperate storage profile. - -If a fixed profile is needed (in the case of tests), you can specify it using `storageProfile` in the config file. If the change is local then put it in `local-{instance}.json` otherwise put it in `default-{instance}.json` or `{env}-{instance}.json`. +For more than 2 clients, you can setup additional storage profiles and switch +between them using the `NODE_APP_INSTANCE` environment variable and specifying a +new localServerPort in the config. -Local config files will be ignored by default in git. - -For example, to create an 'alice' profile locally, put a file called `local-alice.json` in the +For example, to create an 'alice' profile, put a file called `local-alice.json` in the `config` directory: ``` { - "storageProfile": "alice-profile", + "storageProfile": "aliceProfile", + "localServerPort": "8082", } ``` -This will then set the `userData` directory to `%appData%/Session-alice-profile` when running the `alice` instance. +Then you can start up the application a little differently to load the profile: + +``` +NODE_APP_INSTANCE=alice yarn run start +``` + +This changes the [userData](https://electron.atom.io/docs/all/#appgetpathname) +directory from `%appData%/Session` to `%appData%/Session-aliceProfile`. # Making changes @@ -193,11 +187,26 @@ Above all, spend some time with the repository. Follow the pull request template your pull request description automatically. Take a look at recent approved pull requests, see how they did things. -## Production Builds +## Testing Production Builds -You can build a production binary by running the following: +To test changes to the build system, build a release using ``` yarn generate yarn build-release ``` + +Then, run the tests using `grunt test-release:osx --dir=release`, replacing `osx` with `linux` or `win` depending on your platform. + + diff --git a/Gruntfile.js b/Gruntfile.js index aa2289f1b..1665bbef8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -313,11 +313,6 @@ module.exports = grunt => { NODE_ENV: environment, }, requireName: 'unused', - chromeDriverArgs: [ - `remote-debugging-port=${Math.floor( - Math.random() * (9999 - 9000) + 9000 - )}`, - ], }); function getMochaResults() { @@ -373,18 +368,11 @@ module.exports = grunt => { logs.forEach(log => { console.log(log); }); - try { - return app.stop(); - } catch (err) { - return Promise.resolve(); - } + + return app.stop(); }); } - try { - return app.stop(); - } catch (err) { - return Promise.resolve(); - } + return app.stop(); }) .then(() => { if (failure) { @@ -477,11 +465,6 @@ module.exports = grunt => { const app = new Application({ path: [dir, config.exe].join('/'), requireName: 'unused', - chromeDriverArgs: [ - `remote-debugging-port=${Math.floor( - Math.random() * (9999 - 9000) + 9000 - )}`, - ], }); app diff --git a/_locales/en/messages.json b/_locales/en/messages.json index b0a57a3dd..b176ebefb 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -166,11 +166,6 @@ "description": "Only available on development modes, menu option to open up the standalone device setup sequence" }, - "contextMenuNoSuggestions": { - "message": "No Suggestions", - "description": - "Shown in the context menu for a misspelled word to indicate that there are no suggestions to replace the misspelled word" - }, "connectingLoad": { "message": "Connecting To Server", "description": @@ -987,17 +982,10 @@ "message": "Pair New Device" }, "devicePairingAccepted": { - "message": "Device Linking Accepted" + "message": "Device Pairing Accepted" }, "devicePairingReceived": { - "message": "Device Linking Received" - }, - "devicePairingRequestReceivedNoListenerTitle": { - "message": "Device linking request received." - }, - "devicePairingRequestReceivedNoListenerDescription": { - "message": - "Device linking request received but you are not on the device linking screen. \nFirst go to Settings -> Device -> Link New Device." + "message": "Device Pairing Received" }, "waitingForDeviceToRegister": { "message": "Waiting for device to register..." @@ -1006,37 +994,34 @@ "message": "Scan the QR Code on your secondary device" }, "pairedDevices": { - "message": "Linked Devices" + "message": "Paired Devices" }, "noPairedDevices": { - "message": "No linked devices" - }, - "deviceIsSecondaryNoPairing": { - "message": "This device is a secondary device and so cannot be linked." + "message": "No paired devices" }, "allowPairing": { - "message": "Allow Linking" + "message": "Allow Pairing" }, "allowPairingWithDevice": { - "message": "Allow linking with this device?" + "message": "Allow pairing with this device?" }, "provideDeviceAlias": { - "message": "Please provide an alias for this linked device" + "message": "Please provide an alias for this paired device" }, "showPairingWordsTitle": { - "message": "Linking Secret Words" + "message": "Pairing Secret Words" }, "secretPrompt": { "message": "Here is your secret" }, "confirmUnpairingTitle": { - "message": "Please confirm you want to unlink the following device:" + "message": "Please confirm you want to unpair the following device:" }, "unpairDevice": { - "message": "Unlink Device" + "message": "Unpair Device" }, "deviceUnpaired": { - "message": "Device Unlinked" + "message": "Device Unpaired" }, "clear": { "message": "Clear" @@ -1419,11 +1404,6 @@ "message": "Enable spell check of text entered in message composition box", "description": "Description of the media permission description" }, - "spellCheckDirty": { - "message": "You must restart Session to apply your new settings", - "description": - "Shown when the user changes their spellcheck setting to indicate that they must restart Signal." - }, "clearDataHeader": { "message": "Clear All Local Data", "description": @@ -2309,12 +2289,6 @@ "confirmPassword": { "message": "Confirm password" }, - "pasteLongPasswordToastTitle": { - "message": - "The clipboard content exceeds the maximum password length of $max_pwd_len$ characters.", - "description": - "Shown when user pastes a password which is longer than MAX_PASSWORD_LEN" - }, "showSeedPasswordRequest": { "message": "Please enter your password", "description": "Request for user to enter password to show seed." @@ -2528,7 +2502,7 @@ "description": "Indicates that a friend request is pending" }, "notFriends": { - "message": "Not Friends", + "message": "not friends", "description": "Indicates that a conversation is not friends with us" }, "emptyGroupNameError": { @@ -2840,7 +2814,7 @@ "message": "Filter received requests" }, "secretWords": { - "message": "Secret words" + "message": "Secret words:" }, "pairingDevice": { "message": "Pairing Device" diff --git a/app/config.js b/app/config.js index 760f20e33..3a865109d 100644 --- a/app/config.js +++ b/app/config.js @@ -1,13 +1,12 @@ const path = require('path'); -const isDevelopment = require('electron-is-dev'); +const electronIsDev = require('electron-is-dev'); let environment; // In production mode, NODE_ENV cannot be customized by the user -if (isDevelopment) { +if (electronIsDev) { environment = process.env.NODE_ENV || 'development'; - process.env.LOKI_DEV = 1; } else { environment = 'production'; } @@ -19,14 +18,12 @@ process.env.NODE_CONFIG_DIR = path.join(__dirname, '..', 'config'); if (environment === 'production') { // harden production config against the local env process.env.NODE_CONFIG = ''; - process.env.NODE_CONFIG_STRICT_MODE = !isDevelopment; + process.env.NODE_CONFIG_STRICT_MODE = true; process.env.HOSTNAME = ''; process.env.ALLOW_CONFIG_MUTATIONS = ''; process.env.SUPPRESS_NO_CONFIG_WARNING = ''; process.env.NODE_TLS_REJECT_UNAUTHORIZED = ''; - - // We could be running againt production but still be in dev mode, we need to handle that - if (!isDevelopment) { + if (!process.env.LOKI_DEV) { process.env.NODE_APP_INSTANCE = ''; } } @@ -37,10 +34,16 @@ const config = require('config'); config.environment = environment; // Log resulting env vars in use by config -['NODE_ENV', 'NODE_APP_INSTANCE', 'NODE_CONFIG_DIR', 'NODE_CONFIG'].forEach( - s => { - console.log(`${s} ${config.util.getEnv(s)}`); - } -); +[ + 'NODE_ENV', + 'NODE_CONFIG_DIR', + 'NODE_CONFIG', + 'ALLOW_CONFIG_MUTATIONS', + 'HOSTNAME', + 'NODE_APP_INSTANCE', + 'SUPPRESS_NO_CONFIG_WARNING', +].forEach(s => { + console.log(`${s} ${config.util.getEnv(s)}`); +}); module.exports = config; diff --git a/app/password_util.js b/app/password_util.js index 915f017fe..944769c60 100644 --- a/app/password_util.js +++ b/app/password_util.js @@ -1,4 +1,4 @@ -const crypto = require('crypto'); +const { sha512 } = require('js-sha512'); const ERRORS = { TYPE: 'Password must be a string', @@ -6,12 +6,6 @@ const ERRORS = { CHARACTER: 'Password must only contain letters, numbers and symbols', }; -const sha512 = text => { - const hash = crypto.createHash('sha512'); - hash.update(text.trim()); - return hash.digest('hex'); -}; - const generateHash = phrase => phrase && sha512(phrase.trim()); const matchesHash = (phrase, hash) => phrase && sha512(phrase.trim()) === hash.trim(); diff --git a/app/spell_check.js b/app/spell_check.js deleted file mode 100644 index 695c171a6..000000000 --- a/app/spell_check.js +++ /dev/null @@ -1,88 +0,0 @@ -/* global exports, require */ -/* eslint-disable strict */ - -const { Menu } = require('electron'); -const osLocale = require('os-locale'); - -exports.setup = (browserWindow, messages) => { - const { session } = browserWindow.webContents; - const userLocale = osLocale.sync().replace(/_/g, '-'); - const userLocales = [userLocale, userLocale.split('-')[0]]; - - const available = session.availableSpellCheckerLanguages; - const languages = userLocales.filter(l => available.includes(l)); - console.log(`spellcheck: user locale: ${userLocale}`); - console.log('spellcheck: available spellchecker languages: ', available); - console.log('spellcheck: setting languages to: ', languages); - session.setSpellCheckerLanguages(languages); - - browserWindow.webContents.on('context-menu', (_event, params) => { - const { editFlags } = params; - const isMisspelled = Boolean(params.misspelledWord); - const showMenu = params.isEditable || editFlags.canCopy; - - // Popup editor menu - if (showMenu) { - const template = []; - - if (isMisspelled) { - if (params.dictionarySuggestions.length > 0) { - template.push( - ...params.dictionarySuggestions.map(label => ({ - label, - click: () => { - browserWindow.webContents.replaceMisspelling(label); - }, - })) - ); - } else { - template.push({ - label: messages.contextMenuNoSuggestions.message, - enabled: false, - }); - } - template.push({ type: 'separator' }); - } - - if (params.isEditable) { - if (editFlags.canUndo) { - template.push({ label: messages.editMenuUndo.message, role: 'undo' }); - } - // This is only ever `true` if undo was triggered via the context menu - // (not ctrl/cmd+z) - if (editFlags.canRedo) { - template.push({ label: messages.editMenuRedo.message, role: 'redo' }); - } - if (editFlags.canUndo || editFlags.canRedo) { - template.push({ type: 'separator' }); - } - if (editFlags.canCut) { - template.push({ label: messages.editMenuCut.message, role: 'cut' }); - } - } - - if (editFlags.canPaste) { - template.push({ label: messages.editMenuPaste.message, role: 'paste' }); - } - - if (editFlags.canPaste) { - template.push({ - label: messages.editMenuPasteAndMatchStyle.message, - role: 'pasteAndMatchStyle', - }); - } - - // Only enable select all in editors because select all in non-editors - // results in all the UI being selected - if (editFlags.canSelectAll && params.isEditable) { - template.push({ - label: messages.editMenuSelectAll.message, - role: 'selectall', - }); - } - - const menu = Menu.buildFromTemplate(template); - menu.popup(browserWindow); - } - }); -}; diff --git a/app/sql.js b/app/sql.js index d8ecea1c7..478ec44d3 100644 --- a/app/sql.js +++ b/app/sql.js @@ -98,8 +98,6 @@ module.exports = { getAllSessions, getSwarmNodesByPubkey, - getGuardNodes, - updateGuardNodes, getConversationCount, saveConversation, @@ -809,7 +807,6 @@ async function updateSchema(instance) { const LOKI_SCHEMA_VERSIONS = [ updateToLokiSchemaVersion1, updateToLokiSchemaVersion2, - updateToLokiSchemaVersion3, ]; async function updateToLokiSchemaVersion1(currentVersion, instance) { @@ -978,33 +975,6 @@ async function updateToLokiSchemaVersion2(currentVersion, instance) { console.log('updateToLokiSchemaVersion2: success!'); } -async function updateToLokiSchemaVersion3(currentVersion, instance) { - if (currentVersion >= 3) { - return; - } - - await instance.run( - `CREATE TABLE ${GUARD_NODE_TABLE}( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - ed25519PubKey VARCHAR(64) - );` - ); - - console.log('updateToLokiSchemaVersion3: starting...'); - await instance.run('BEGIN TRANSACTION;'); - - await instance.run( - `INSERT INTO loki_schema ( - version - ) values ( - 3 - );` - ); - - await instance.run('COMMIT TRANSACTION;'); - console.log('updateToLokiSchemaVersion3: success!'); -} - async function updateLokiSchema(instance) { const result = await instance.get( "SELECT name FROM sqlite_master WHERE type = 'table' AND name='loki_schema';" @@ -1430,9 +1400,6 @@ async function removeAllSignedPreKeys() { } const PAIRING_AUTHORISATIONS_TABLE = 'pairingAuthorisations'; - -const GUARD_NODE_TABLE = 'guardNodes'; - async function getAuthorisationForSecondaryPubKey(pubKey, options) { const granted = options && options.granted; let filter = ''; @@ -1503,37 +1470,6 @@ async function getSecondaryDevicesFor(primaryDevicePubKey) { return map(authorisations, row => row.secondaryDevicePubKey); } -async function getGuardNodes() { - const nodes = await db.all(`SELECT ed25519PubKey FROM ${GUARD_NODE_TABLE};`); - - if (!nodes) { - return null; - } - - return nodes; -} - -async function updateGuardNodes(nodes) { - await db.run('BEGIN TRANSACTION;'); - - await db.run(`DELETE FROM ${GUARD_NODE_TABLE}`); - - await Promise.all( - nodes.map(edkey => - db.run( - `INSERT INTO ${GUARD_NODE_TABLE} ( - ed25519PubKey - ) values ($ed25519PubKey)`, - { - $ed25519PubKey: edkey, - } - ) - ) - ); - - await db.run('END TRANSACTION;'); -} - async function getPrimaryDeviceFor(secondaryDevicePubKey) { const row = await db.get( `SELECT primaryDevicePubKey FROM ${PAIRING_AUTHORISATIONS_TABLE} WHERE secondaryDevicePubKey = $secondaryDevicePubKey AND isGranted = 1;`, diff --git a/app/sql_channel.js b/app/sql_channel.js index f4b99ecea..128ab3a59 100644 --- a/app/sql_channel.js +++ b/app/sql_channel.js @@ -36,10 +36,7 @@ function initialize() { console.log( `sql channel error with call ${callName}: ${errorForDisplay}` ); - // FIXME this line cause the test-integration to fail and we probably don't need it during test - if (!process.env.NODE_ENV.includes('test-integration')) { - event.sender.send(`${SQL_CHANNEL_KEY}-done`, jobId, errorForDisplay); - } + event.sender.send(`${SQL_CHANNEL_KEY}-done`, jobId, errorForDisplay); } }); diff --git a/app/user_config.js b/app/user_config.js index cb434935c..12cdded84 100644 --- a/app/user_config.js +++ b/app/user_config.js @@ -1,35 +1,15 @@ const path = require('path'); -const process = require('process'); const { app } = require('electron'); const { start } = require('./base_config'); const config = require('./config'); -let storageProfile; - -// Node makes sure all environment variables are strings -const { NODE_ENV: environment, NODE_APP_INSTANCE: instance } = process.env; - -// We need to make sure instance is not empty -const isValidInstance = typeof instance === 'string' && instance.length > 0; -const isProduction = environment === 'production' && !isValidInstance; - -// Use seperate data directories for each different environment and app instances -// We should prioritise config values first -if (config.has(storageProfile)) { - storageProfile = config.get('storageProfile'); -} else if (!isProduction) { - storageProfile = environment; - if (isValidInstance) { - storageProfile = storageProfile.concat(`-${instance}`); - } -} - -if (storageProfile) { +// Use separate data directory for development +if (config.has('storageProfile')) { const userData = path.join( app.getPath('appData'), - `Session-${storageProfile}` + `Loki-Messenger-${config.get('storageProfile')}` ); app.setPath('userData', userData); diff --git a/background.html b/background.html index c3994d233..785d40ec2 100644 --- a/background.html +++ b/background.html @@ -454,6 +454,7 @@ + @@ -500,6 +501,7 @@ + diff --git a/background_test.html b/background_test.html deleted file mode 100644 index 8433a9af4..000000000 --- a/background_test.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - - - - - - - Session - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - diff --git a/bower.json b/bower.json index 751d468d0..8b2fbba06 100644 --- a/bower.json +++ b/bower.json @@ -56,6 +56,7 @@ "node_modules/backbone/backbone.js", "components/indexeddb-backbonejs-adapter/**/*.js", "components/qrcode/**/*.js", + "node_modules/intl-tel-input/build/js/intlTelInput.js", "components/autosize/**/*.js", "components/webaudiorecorder/lib/WebAudioRecorder.js", "components/multibase/dist/index.js" diff --git a/config/default.json b/config/default.json index b8a2dcc33..b08784f25 100644 --- a/config/default.json +++ b/config/default.json @@ -3,6 +3,7 @@ "localUrl": "localhost.loki", "cdnUrl": "random.snode", "contentProxyUrl": "", + "localServerPort": "8081", "defaultPoWDifficulty": "1", "seedNodeList": [ { diff --git a/config/development-1.json b/config/development-1.json new file mode 100644 index 000000000..2ff930238 --- /dev/null +++ b/config/development-1.json @@ -0,0 +1,16 @@ +{ + "storageProfile": "development1", + "localServerPort": "8082", + "seedNodeList": [ + { + "ip": "public.loki.foundation", + "port": "38157" + }, + { + "ip": "storage.testnetseed1.loki.network", + "port": "38157" + } + ], + "openDevTools": true, + "defaultPublicChatServer": "https://chat-dev.lokinet.org/" +} diff --git a/config/development-2.json b/config/development-2.json new file mode 100644 index 000000000..bf4c93258 --- /dev/null +++ b/config/development-2.json @@ -0,0 +1,16 @@ +{ + "storageProfile": "development2", + "localServerPort": "8083", + "seedNodeList": [ + { + "ip": "public.loki.foundation", + "port": "38157" + }, + { + "ip": "storage.testnetseed1.loki.network", + "port": "38157" + } + ], + "openDevTools": true, + "defaultPublicChatServer": "https://chat-dev.lokinet.org/" +} diff --git a/config/development.json b/config/development.json index ba141816b..89ac6c6f7 100644 --- a/config/development.json +++ b/config/development.json @@ -1,4 +1,5 @@ { + "storageProfile": "development", "seedNodeList": [ { "ip": "public.loki.foundation", diff --git a/config/production-devprod.json b/config/local-devprod.json similarity index 58% rename from config/production-devprod.json rename to config/local-devprod.json index 62437286b..45797645e 100644 --- a/config/production-devprod.json +++ b/config/local-devprod.json @@ -1,4 +1,5 @@ { + "storageProfile": "devprodProfile", "openDevTools": true, "updatesEnabled": false } diff --git a/config/local-devprod1.json b/config/local-devprod1.json new file mode 100644 index 000000000..9d3b12053 --- /dev/null +++ b/config/local-devprod1.json @@ -0,0 +1,6 @@ +{ + "storageProfile": "devprod1Profile", + "localServerPort": "8082", + "openDevTools": true, + "updatesEnabled": false +} diff --git a/config/production-devprod1.json b/config/production-devprod1.json deleted file mode 100644 index 62437286b..000000000 --- a/config/production-devprod1.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "openDevTools": true, - "updatesEnabled": false -} diff --git a/config/staging.json b/config/staging.json new file mode 100644 index 000000000..f74f4b48e --- /dev/null +++ b/config/staging.json @@ -0,0 +1,4 @@ +{ + "storageProfile": "staging", + "openDevTools": true +} diff --git a/config/swarm-testing.json b/config/swarm-testing.json index 4e7ab7fee..9df9b3340 100644 --- a/config/swarm-testing.json +++ b/config/swarm-testing.json @@ -1,4 +1,5 @@ { + "storageProfile": "swarm-testing", "seedNodeList": [ { "ip": "localhost", diff --git a/config/swarm-testing2.json b/config/swarm-testing2.json new file mode 100644 index 000000000..5469d9dab --- /dev/null +++ b/config/swarm-testing2.json @@ -0,0 +1,11 @@ +{ + "storageProfile": "swarm-testing2", + "seedNodeList": [ + { + "ip": "localhost", + "port": "22129" + } + ], + "openDevTools": true, + "defaultPublicChatServer": "https://team-chat.lokinet.org/" +} diff --git a/config/test-integration.json b/config/test-integration.json deleted file mode 100644 index d2b1f6973..000000000 --- a/config/test-integration.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "openDevTools": false, - "updatesEnabled": false -} diff --git a/integration_test/add_friends_test.js b/integration_test/add_friends_test.js deleted file mode 100644 index aa8c04d4d..000000000 --- a/integration_test/add_friends_test.js +++ /dev/null @@ -1,119 +0,0 @@ -/* eslint-disable func-names */ -/* eslint-disable import/no-extraneous-dependencies */ -const common = require('./common'); -const { afterEach, beforeEach, describe, it } = require('mocha'); -const ConversationPage = require('./page-objects/conversation.page'); - -describe('Add friends', function() { - let app; - let app2; - this.timeout(60000); - this.slow(15000); - - beforeEach(async () => { - await common.killallElectron(); - await common.stopStubSnodeServer(); - - const app1Props = { - mnemonic: common.TEST_MNEMONIC1, - displayName: common.TEST_DISPLAY_NAME1, - stubSnode: true, - }; - - const app2Props = { - mnemonic: common.TEST_MNEMONIC2, - displayName: common.TEST_DISPLAY_NAME2, - stubSnode: true, - }; - - [app, app2] = await Promise.all([ - common.startAndStub(app1Props), - common.startAndStub2(app2Props), - ]); - }); - - afterEach(async () => { - await common.stopApp(app); - await common.killallElectron(); - await common.stopStubSnodeServer(); - }); - - it('addFriends: can add a friend by sessionID', async () => { - const textMessage = common.generateSendMessageText(); - - await app.client.element(ConversationPage.contactsButtonSection).click(); - await app.client.element(ConversationPage.addContactButton).click(); - await app.client.isExisting(ConversationPage.leftPaneOverlay).should - .eventually.be.true; - - await common.setValueWrapper( - app, - ConversationPage.sessionIDInput, - common.TEST_PUBKEY2 - ); - await app.client - .element(ConversationPage.sessionIDInput) - .getValue() - .should.eventually.equal(common.TEST_PUBKEY2); - - await app.client.element(ConversationPage.nextButton).click(); - await app.client.waitForExist( - ConversationPage.sendFriendRequestTextarea, - 1000 - ); - - // send a text message to that user (will be a friend request) - await app.client - .element(ConversationPage.sendFriendRequestTextarea) - .setValue(textMessage); - await app.client.keys('Enter'); - await app.client.waitForExist( - ConversationPage.existingFriendRequestText(textMessage), - 1000 - ); - - // assure friend request message has been sent - await common.timeout(3000); - await app.client.isExisting(ConversationPage.retrySendButton).should - .eventually.be.false; - - // wait for left notification Friend Request count to go to 1 and click it - await app2.client.waitForExist( - ConversationPage.oneNotificationFriendRequestLeft, - 5000 - ); - await app2.client - .element(ConversationPage.oneNotificationFriendRequestLeft) - .click(); - // open the dropdown from the top friend request count - await app2.client.isExisting( - ConversationPage.oneNotificationFriendRequestTop - ).should.eventually.be.true; - await app2.client - .element(ConversationPage.oneNotificationFriendRequestTop) - .click(); - - // we should have our app1 friend request here - await app2.client.isExisting( - ConversationPage.friendRequestFromUser( - common.TEST_DISPLAY_NAME1, - common.TEST_PUBKEY1 - ) - ).should.eventually.be.true; - await app2.client.isExisting(ConversationPage.acceptFriendRequestButton) - .should.eventually.be.true; - - // accept the friend request and validate that on both side the "accepted FR" message is shown - await app2.client - .element(ConversationPage.acceptFriendRequestButton) - .click(); - await app2.client.waitForExist( - ConversationPage.acceptedFriendRequestMessage, - 1000 - ); - await app.client.waitForExist( - ConversationPage.acceptedFriendRequestMessage, - 5000 - ); - }); -}); diff --git a/integration_test/closed_group_test.js b/integration_test/closed_group_test.js deleted file mode 100644 index c937c97ff..000000000 --- a/integration_test/closed_group_test.js +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint-disable func-names */ -/* eslint-disable import/no-extraneous-dependencies */ -const common = require('./common'); -const { afterEach, beforeEach, describe, it } = require('mocha'); -const ConversationPage = require('./page-objects/conversation.page'); - -describe('Closed groups', function() { - let app; - let app2; - this.timeout(60000); - this.slow(15000); - - beforeEach(async () => { - await common.killallElectron(); - await common.stopStubSnodeServer(); - - [app, app2] = await common.startAppsAsFriends(); - }); - - afterEach(async () => { - await common.stopApp(app); - await common.killallElectron(); - await common.stopStubSnodeServer(); - }); - - it('closedGroup: can create a closed group with a friend and send/receive a message', async () => { - await app.client.element(ConversationPage.globeButtonSection).click(); - await app.client.element(ConversationPage.createClosedGroupButton).click(); - - // fill the groupname - await common.setValueWrapper( - app, - ConversationPage.closedGroupNameTextarea, - common.VALID_CLOSED_GROUP_NAME1 - ); - await app.client - .element(ConversationPage.closedGroupNameTextarea) - .getValue() - .should.eventually.equal(common.VALID_CLOSED_GROUP_NAME1); - - await app.client - .element(ConversationPage.createClosedGroupMemberItem) - .isVisible(); - - // select the first friend as a member of the groups being created - await app.client - .element(ConversationPage.createClosedGroupMemberItem) - .click(); - await app.client - .element(ConversationPage.createClosedGroupMemberItemSelected) - .isVisible(); - - // trigger the creation of the group - await app.client - .element(ConversationPage.validateCreationClosedGroupButton) - .click(); - - await app.client.waitForExist( - ConversationPage.sessionToastGroupCreatedSuccess, - 1000 - ); - await app.client.isExisting( - ConversationPage.headerTitleGroupName(common.VALID_CLOSED_GROUP_NAME1) - ).should.eventually.be.true; - await app.client - .element(ConversationPage.headerTitleMembers(2)) - .isVisible(); - - // validate overlay is closed - await app.client.isExisting(ConversationPage.leftPaneOverlay).should - .eventually.be.false; - - // move back to the conversation section - await app.client - .element(ConversationPage.conversationButtonSection) - .click(); - - // validate open chat has been added - await app.client.isExisting( - ConversationPage.rowOpenGroupConversationName( - common.VALID_CLOSED_GROUP_NAME1 - ) - ).should.eventually.be.true; - - // next check app2 has been invited and has the group in its conversations - await app2.client.waitForExist( - ConversationPage.rowOpenGroupConversationName( - common.VALID_CLOSED_GROUP_NAME1 - ), - 6000 - ); - // open the closed group conversation on app2 - await app2.client - .element(ConversationPage.conversationButtonSection) - .click(); - await common.timeout(500); - await app2.client - .element( - ConversationPage.rowOpenGroupConversationName( - common.VALID_CLOSED_GROUP_NAME1 - ) - ) - .click(); - - // send a message from app and validate it is received on app2 - const textMessage = common.generateSendMessageText(); - await app.client - .element(ConversationPage.sendMessageTextarea) - .setValue(textMessage); - await app.client - .element(ConversationPage.sendMessageTextarea) - .getValue() - .should.eventually.equal(textMessage); - // send the message - await app.client.keys('Enter'); - - // validate that the message has been added to the message list view - await app.client.waitForExist( - ConversationPage.existingSendMessageText(textMessage), - 2000 - ); - - // validate that the message has been added to the message list view - await app2.client.waitForExist( - ConversationPage.existingReceivedMessageText(textMessage), - 5000 - ); - }); -}); diff --git a/integration_test/common.js b/integration_test/common.js deleted file mode 100644 index 306ae9bf1..000000000 --- a/integration_test/common.js +++ /dev/null @@ -1,498 +0,0 @@ -/* eslint-disable no-console */ -/* eslint-disable import/no-extraneous-dependencies */ -/* eslint-disable prefer-destructuring */ - -const { Application } = require('spectron'); -const path = require('path'); - -const chai = require('chai'); -const chaiAsPromised = require('chai-as-promised'); -const RegistrationPage = require('./page-objects/registration.page'); -const ConversationPage = require('./page-objects/conversation.page'); -const { exec } = require('child_process'); -const url = require('url'); -const http = require('http'); -const fse = require('fs-extra'); - -chai.should(); -chai.use(chaiAsPromised); -chai.config.includeStack = true; - -const STUB_SNODE_SERVER_PORT = 3000; -const ENABLE_LOG = false; - -module.exports = { - /* ************** USERS ****************** */ - TEST_MNEMONIC1: - 'faxed mechanic mocked agony unrest loincloth pencil eccentric boyfriend oasis speedy ribbon faxed', - TEST_PUBKEY1: - '0552b85a43fb992f6bdb122a5a379505a0b99a16f0628ab8840249e2a60e12a413', - TEST_DISPLAY_NAME1: 'integration_tester_1', - - TEST_MNEMONIC2: - 'guide inbound jerseys bays nouns basin sulking awkward stockpile ostrich ascend pylons ascend', - TEST_PUBKEY2: - '054e1ca8681082dbd9aad1cf6fc89a32254e15cba50c75b5a73ac10a0b96bcbd2a', - TEST_DISPLAY_NAME2: 'integration_tester_2', - - /* ************** OPEN GROUPS ****************** */ - VALID_GROUP_URL: 'https://chat.getsession.org', - VALID_GROUP_URL2: 'https://chat-dev.lokinet.org', - VALID_GROUP_NAME: 'Session Public Chat', - VALID_GROUP_NAME2: 'Loki Dev Chat', - - /* ************** CLOSED GROUPS ****************** */ - VALID_CLOSED_GROUP_NAME1: 'Closed Group 1', - - USER_DATA_ROOT_FOLDER: '', - - async timeout(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - }, - - // a wrapper to work around electron/spectron bug - async setValueWrapper(app, selector, value) { - await app.client.element(selector).click(); - // keys, setValue and addValue hang on certain platforms - // could put a branch here to use one of those - // if we know what platforms are good and which ones are broken - await app.client.execute( - (slctr, val) => { - // eslint-disable-next-line no-undef - const iter = document.evaluate( - slctr, - // eslint-disable-next-line no-undef - document, - null, - // eslint-disable-next-line no-undef - XPathResult.UNORDERED_NODE_ITERATOR_TYPE, - null - ); - const elem = iter.iterateNext(); - if (elem) { - elem.value = val; - } else { - console.error('Cant find', slctr, elem, iter); - } - }, - selector, - value - ); - // let session js detect the text change - await app.client.element(selector).click(); - }, - - async startApp(environment = 'test-integration-session') { - const env = environment.startsWith('test-integration') - ? 'test-integration' - : environment; - const instance = environment.replace('test-integration-', ''); - - const app1 = new Application({ - path: path.join(__dirname, '..', 'node_modules', '.bin', 'electron'), - args: ['.'], - env: { - NODE_ENV: env, - NODE_APP_INSTANCE: instance, - USE_STUBBED_NETWORK: true, - ELECTRON_ENABLE_LOGGING: true, - ELECTRON_ENABLE_STACK_DUMPING: true, - ELECTRON_DISABLE_SANDBOX: 1, - }, - requireName: 'electronRequire', - // chromeDriverLogPath: '../chromedriverlog.txt', - chromeDriverArgs: [ - `remote-debugging-port=${Math.floor( - Math.random() * (9999 - 9000) + 9000 - )}`, - ], - }); - - chaiAsPromised.transferPromiseness = app1.transferPromiseness; - - await app1.start(); - await app1.client.waitUntilWindowLoaded(); - - return app1; - }, - - async startApp2() { - const app2 = await this.startApp('test-integration-session-2'); - return app2; - }, - - async stopApp(app1) { - if (app1 && app1.isRunning()) { - await app1.stop(); - } - }, - - async killallElectron() { - // rtharp - my 2nd client on MacOs needs: pkill -f "node_modules/.bin/electron" - // node_modules/electron/dist/electron is node_modules/electron/dist/Electron.app on MacOS - const killStr = - process.platform === 'win32' - ? 'taskkill /im electron.exe /t /f' - : 'pkill -f "node_modules/electron/dist/electron" | pkill -f "node_modules/.bin/electron"'; - return new Promise(resolve => { - exec(killStr, (err, stdout, stderr) => { - if (err) { - resolve({ stdout, stderr }); - } else { - resolve({ stdout, stderr }); - } - }); - }); - }, - - async rmFolder(folder) { - await fse.remove(folder); - }, - - async startAndAssureCleanedApp2() { - const app2 = await this.startAndAssureCleanedApp( - 'test-integration-session-2' - ); - return app2; - }, - - async startAndAssureCleanedApp(env = 'test-integration-session') { - const userData = path.join(this.USER_DATA_ROOT_FOLDER, `Session-${env}`); - - await this.rmFolder(userData); - - const app1 = await this.startApp(env); - await app1.client.waitForExist( - RegistrationPage.registrationTabSignIn, - 4000 - ); - - return app1; - }, - - async startAndStub({ - mnemonic, - displayName, - stubSnode = false, - stubOpenGroups = false, - env = 'test-integration-session', - }) { - const app = await this.startAndAssureCleanedApp(env); - - if (stubSnode) { - await this.startStubSnodeServer(); - this.stubSnodeCalls(app); - } - - if (stubOpenGroups) { - this.stubOpenGroupsCalls(app); - } - - if (mnemonic && displayName) { - await this.restoreFromMnemonic(app, mnemonic, displayName); - // not sure we need this - rtharp. - await this.timeout(2000); - } - - return app; - }, - - async startAndStub2(props) { - const app2 = await this.startAndStub({ - env: 'test-integration-session-2', - ...props, - }); - - return app2; - }, - - async restoreFromMnemonic(app, mnemonic, displayName) { - await app.client.element(RegistrationPage.registrationTabSignIn).click(); - await app.client.element(RegistrationPage.restoreFromSeedMode).click(); - await this.setValueWrapper( - app, - RegistrationPage.recoveryPhraseInput, - mnemonic - ); - - await this.setValueWrapper( - app, - RegistrationPage.displayNameInput, - displayName - ); - - await app.client.element(RegistrationPage.continueSessionButton).click(); - await app.client.waitForExist( - RegistrationPage.conversationListContainer, - 4000 - ); - }, - - async startAppsAsFriends() { - const app1Props = { - mnemonic: this.TEST_MNEMONIC1, - displayName: this.TEST_DISPLAY_NAME1, - stubSnode: true, - }; - - const app2Props = { - mnemonic: this.TEST_MNEMONIC2, - displayName: this.TEST_DISPLAY_NAME2, - stubSnode: true, - }; - - const [app1, app2] = await Promise.all([ - this.startAndStub(app1Props), - this.startAndStub2(app2Props), - ]); - - /** add each other as friends */ - const textMessage = this.generateSendMessageText(); - - await app1.client.element(ConversationPage.contactsButtonSection).click(); - await app1.client.element(ConversationPage.addContactButton).click(); - - await this.setValueWrapper( - app1, - ConversationPage.sessionIDInput, - this.TEST_PUBKEY2 - ); - await app1.client.element(ConversationPage.nextButton).click(); - await app1.client.waitForExist( - ConversationPage.sendFriendRequestTextarea, - 1000 - ); - - // send a text message to that user (will be a friend request) - await this.setValueWrapper( - app1, - ConversationPage.sendFriendRequestTextarea, - textMessage - ); - await app1.client.keys('Enter'); - await app1.client.waitForExist( - ConversationPage.existingFriendRequestText(textMessage), - 1000 - ); - - // wait for left notification Friend Request count to go to 1 and click it - await app2.client.waitForExist( - ConversationPage.oneNotificationFriendRequestLeft, - 5000 - ); - await app2.client - .element(ConversationPage.oneNotificationFriendRequestLeft) - .click(); - // open the dropdown from the top friend request count - await app2.client.isExisting( - ConversationPage.oneNotificationFriendRequestTop - ).should.eventually.be.true; - await app2.client - .element(ConversationPage.oneNotificationFriendRequestTop) - .click(); - - // accept the friend request and validate that on both side the "accepted FR" message is shown - await app2.client - .element(ConversationPage.acceptFriendRequestButton) - .click(); - await app2.client.waitForExist( - ConversationPage.acceptedFriendRequestMessage, - 1000 - ); - await app1.client.waitForExist( - ConversationPage.acceptedFriendRequestMessage, - 5000 - ); - - return [app1, app2]; - }, - - async linkApp2ToApp(app1, app2) { - // app needs to be logged in as user1 and app2 needs to be logged out - // start the pairing dialog for the first app - await app1.client.element(ConversationPage.settingsButtonSection).click(); - await app1.client.element(ConversationPage.deviceSettingsRow).click(); - - await app1.client.isVisible(ConversationPage.noPairedDeviceMessage); - // we should not find the linkDeviceButtonDisabled button (as DISABLED) - await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled) - .should.eventually.be.false; - await app1.client.element(ConversationPage.linkDeviceButton).click(); - - // validate device pairing dialog is shown and has a qrcode - await app1.client.isVisible(ConversationPage.devicePairingDialog); - await app1.client.isVisible(ConversationPage.qrImageDiv); - - // next trigger the link request from the app2 with the app1 pubkey - await app2.client.element(RegistrationPage.registrationTabSignIn).click(); - await app2.client.element(RegistrationPage.linkDeviceMode).click(); - - await this.setValueWrapper( - app2, - RegistrationPage.textareaLinkDevicePubkey, - this.TEST_PUBKEY1 - ); - await app2.client.element(RegistrationPage.linkDeviceTriggerButton).click(); - await app1.client.waitForExist(RegistrationPage.toastWrapper, 7000); - let secretWordsapp1 = await app1.client - .element(RegistrationPage.secretToastDescription) - .getText(); - secretWordsapp1 = secretWordsapp1.split(': ')[1]; - - await app2.client.waitForExist(RegistrationPage.toastWrapper, 6000); - await app2.client - .element(RegistrationPage.secretToastDescription) - .getText() - .should.eventually.be.equal(secretWordsapp1); - await app1.client.element(ConversationPage.allowPairingButton).click(); - await app1.client.element(ConversationPage.okButton).click(); - // validate device paired in settings list with correct secrets - await app1.client.waitForExist( - ConversationPage.devicePairedDescription(secretWordsapp1), - 2000 - ); - - await app1.client.isExisting(ConversationPage.unpairDeviceButton).should - .eventually.be.true; - await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled) - .should.eventually.be.true; - - // validate app2 (secondary device) is linked successfully - await app2.client.waitForExist( - RegistrationPage.conversationListContainer, - 4000 - ); - - // validate primary pubkey of app2 is the same that in app1 - await app2.webContents - .executeJavaScript("window.storage.get('primaryDevicePubKey')") - .should.eventually.be.equal(this.TEST_PUBKEY1); - }, - - async triggerUnlinkApp2FromApp(app1, app2) { - // check app2 is loggedin - await app2.client.isExisting(RegistrationPage.conversationListContainer) - .should.eventually.be.true; - - await app1.client.element(ConversationPage.settingsButtonSection).click(); - await app1.client.element(ConversationPage.deviceSettingsRow).click(); - await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled) - .should.eventually.be.true; - // click the unlink button - await app1.client.element(ConversationPage.unpairDeviceButton).click(); - await app1.client.element(ConversationPage.validateUnpairDevice).click(); - - await app1.client.waitForExist( - ConversationPage.noPairedDeviceMessage, - 2000 - ); - await app1.client.element(ConversationPage.linkDeviceButton).isEnabled() - .should.eventually.be.true; - - // let time to app2 to catch the event and restart dropping its data - await this.timeout(5000); - - // check that the app restarted - // (did not find a better way than checking the app no longer being accessible) - let isApp2Joinable = true; - try { - await app2.client.isExisting(RegistrationPage.registrationTabSignIn) - .should.eventually.be.true; - } catch (err) { - // if we get an error here, it means Spectron is lost. - // this is a good thing because it means app2 restarted - isApp2Joinable = false; - } - - if (isApp2Joinable) { - throw new Error( - 'app2 is still joinable so it did not restart, so it did not unlink correctly' - ); - } - }, - - generateSendMessageText: () => - `Test message from integration tests ${Date.now()}`, - - stubOpenGroupsCalls: app1 => { - app1.webContents.executeJavaScript( - 'window.LokiAppDotNetServerAPI = window.StubAppDotNetAPI;' - ); - }, - - stubSnodeCalls(app1) { - app1.webContents.executeJavaScript( - 'window.LokiMessageAPI = window.StubMessageAPI;' - ); - }, - - logsContainsString: async (app1, str) => { - const logs = JSON.stringify(await app1.client.getRenderProcessLogs()); - return logs.includes(str); - }, - - async startStubSnodeServer() { - if (!this.stubSnode) { - this.messages = {}; - this.stubSnode = http.createServer((request, response) => { - const { query } = url.parse(request.url, true); - const { pubkey, data, timestamp } = query; - - if (pubkey) { - if (request.method === 'POST') { - if (ENABLE_LOG) { - console.warn('POST', [data, timestamp]); - } - - let ori = this.messages[pubkey]; - if (!this.messages[pubkey]) { - ori = []; - } - - this.messages[pubkey] = [...ori, { data, timestamp }]; - - response.writeHead(200, { 'Content-Type': 'text/html' }); - response.end(); - } else { - const retrievedMessages = { messages: this.messages[pubkey] }; - if (ENABLE_LOG) { - console.warn('GET', pubkey, retrievedMessages); - } - if (this.messages[pubkey]) { - response.writeHead(200, { 'Content-Type': 'application/json' }); - response.write(JSON.stringify(retrievedMessages)); - this.messages[pubkey] = []; - } - response.end(); - } - } - response.end(); - }); - this.stubSnode.listen(STUB_SNODE_SERVER_PORT); - } else { - this.messages = {}; - } - }, - - async stopStubSnodeServer() { - if (this.stubSnode) { - this.stubSnode.close(); - this.stubSnode = null; - } - }, - - // async killStubSnodeServer() { - // return new Promise(resolve => { - // exec( - // `lsof -ti:${STUB_SNODE_SERVER_PORT} |xargs kill -9`, - // (err, stdout, stderr) => { - // if (err) { - // resolve({ stdout, stderr }); - // } else { - // resolve({ stdout, stderr }); - // } - // } - // ); - // }); - // }, -}; diff --git a/integration_test/integration_test.js b/integration_test/integration_test.js deleted file mode 100644 index 87eee0fcc..000000000 --- a/integration_test/integration_test.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable no-console */ -/* eslint-disable more/no-then */ -/* eslint-disable global-require */ -/* eslint-disable import/no-extraneous-dependencies */ - -const { before } = require('mocha'); -const common = require('./common'); - -require('./registration_test'); -require('./open_group_test'); -require('./add_friends_test'); -require('./link_device_test'); -require('./closed_group_test'); - -before(async () => { - // start the app once before all tests to get the platform-dependent - // path of user data and store it to common.USER_DATA_ROOT_FOLDER - const app1 = await common.startApp(); - common.USER_DATA_ROOT_FOLDER = await app1.electron.remote.app.getPath( - 'appData' - ); - await common.stopApp(app1); -}); diff --git a/integration_test/link_device_test.js b/integration_test/link_device_test.js deleted file mode 100644 index a206a5e9c..000000000 --- a/integration_test/link_device_test.js +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-disable prefer-destructuring */ -/* eslint-disable more/no-then */ -/* eslint-disable func-names */ -/* eslint-disable import/no-extraneous-dependencies */ -const common = require('./common'); -const { afterEach, beforeEach, describe, it } = require('mocha'); - -describe('Link Device', function() { - let app; - let app2; - this.timeout(60000); - this.slow(15000); - - beforeEach(async () => { - await common.killallElectron(); - await common.stopStubSnodeServer(); - - const app1Props = { - mnemonic: common.TEST_MNEMONIC1, - displayName: common.TEST_DISPLAY_NAME1, - stubSnode: true, - }; - - const app2Props = { - stubSnode: true, - }; - - [app, app2] = await Promise.all([ - common.startAndStub(app1Props), - common.startAndStub2(app2Props), - ]); - }); - - afterEach(async () => { - await common.killallElectron(); - await common.stopStubSnodeServer(); - }); - - it('linkDevice: link two desktop devices', async () => { - await common.linkApp2ToApp(app, app2); - }); - - it('linkDevice: unlink two devices', async () => { - await common.linkApp2ToApp(app, app2); - await common.timeout(1000); - await common.triggerUnlinkApp2FromApp(app, app2); - }); -}); diff --git a/integration_test/open_group_test.js b/integration_test/open_group_test.js deleted file mode 100644 index 9910fd68e..000000000 --- a/integration_test/open_group_test.js +++ /dev/null @@ -1,151 +0,0 @@ -/* eslint-disable func-names */ -/* eslint-disable import/no-extraneous-dependencies */ -const common = require('./common'); -const { afterEach, beforeEach, describe, it } = require('mocha'); -const ConversationPage = require('./page-objects/conversation.page'); - -describe('Open groups', function() { - let app; - this.timeout(30000); - this.slow(15000); - - beforeEach(async () => { - await common.killallElectron(); - const login = { - mnemonic: common.TEST_MNEMONIC1, - displayName: common.TEST_DISPLAY_NAME1, - stubOpenGroups: true, - }; - app = await common.startAndStub(login); - }); - - afterEach(async () => { - await common.killallElectron(); - }); - - // reduce code duplication to get the initial join - async function joinOpenGroup(url, name) { - await app.client.element(ConversationPage.globeButtonSection).click(); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - - await common.setValueWrapper(app, ConversationPage.openGroupInputUrl, url); - await app.client - .element(ConversationPage.openGroupInputUrl) - .getValue() - .should.eventually.equal(url); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - - // validate session loader is shown - await app.client.isExisting(ConversationPage.sessionLoader).should - .eventually.be.true; - // account for slow home internet connection delays... - await app.client.waitForExist( - ConversationPage.sessionToastJoinOpenGroupSuccess, - 60 * 1000 - ); - - // validate overlay is closed - await app.client.isExisting(ConversationPage.leftPaneOverlay).should - .eventually.be.false; - - // validate open chat has been added - await app.client.waitForExist( - ConversationPage.rowOpenGroupConversationName(name), - 4000 - ); - } - - it('openGroup: works with valid open group url', async () => { - await joinOpenGroup(common.VALID_GROUP_URL, common.VALID_GROUP_NAME); - }); - - it('openGroup: cannot join two times the same open group', async () => { - await joinOpenGroup(common.VALID_GROUP_URL2, common.VALID_GROUP_NAME2); - - // adding a second time the same open group - await app.client.element(ConversationPage.globeButtonSection).click(); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - - await common.setValueWrapper( - app, - ConversationPage.openGroupInputUrl, - common.VALID_GROUP_URL2 - ); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - // validate session loader is not shown - await app.client.isExisting(ConversationPage.sessionLoader).should - .eventually.be.false; - - await app.client.waitForExist( - ConversationPage.sessionToastJoinOpenGroupAlreadyExist, - 1 * 1000 - ); - - // validate overlay is still opened - await app.client.isExisting(ConversationPage.leftPaneOverlay).should - .eventually.be.true; - }); - - it('openGroup: can send message to open group', async () => { - // join dev-chat group - await app.client.element(ConversationPage.globeButtonSection).click(); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - - await common.setValueWrapper( - app, - ConversationPage.openGroupInputUrl, - common.VALID_GROUP_URL2 - ); - await app.client.element(ConversationPage.joinOpenGroupButton).click(); - - // wait for toast to appear - await app.client.waitForExist( - ConversationPage.sessionToastJoinOpenGroupSuccess, - 30 * 1000 - ); - await common.timeout(5 * 1000); // wait for toast to clear - - await app.client.waitForExist( - ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2), - 10 * 1000 - ); - // generate a message containing the current timestamp so we can find it in the list of messages - const textMessage = common.generateSendMessageText(); - await app.client - .element(ConversationPage.conversationButtonSection) - .click(); - - await app.client.isExisting( - ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2) - ); - - await app.client - .element( - ConversationPage.rowOpenGroupConversationName(common.VALID_GROUP_NAME2) - ) - .click(); - - await common.setValueWrapper( - app, - ConversationPage.sendMessageTextarea, - textMessage - ); - await app.client - .element(ConversationPage.sendMessageTextarea) - .getValue() - .should.eventually.equal(textMessage); - // allow some time to fetch some messages - await common.timeout(3000); - - // send the message - await app.client.keys('Enter'); - await common.timeout(5000); - // validate that the message has been added to the message list view - await app.client.waitForExist( - ConversationPage.existingSendMessageText(textMessage), - 3 * 1000 - ); - // we should validate that the message has been added effectively sent - // (checking the check icon on the metadata part of the message?) - }); -}); diff --git a/integration_test/page-objects/common.page.js b/integration_test/page-objects/common.page.js deleted file mode 100644 index e5374f6d6..000000000 --- a/integration_test/page-objects/common.page.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = { - // generics - objWithClassAndText: (obj, classname, text) => - `//${obj}[contains(string(), "${text}")][contains(@class, "${classname}")]`, - - divRoleButtonWithText: text => - `//div[contains(string(), "${text}")][contains(@role, "button")]`, - divRoleButtonWithTextDisabled: text => - `//div[contains(string(), "${text}")][contains(@role, "button")][contains(@class, "disabled")]`, - divRoleButtonDangerWithText: text => - `${module.exports.divRoleButtonWithText(text)}[contains(@class, "danger")]`, - inputWithPlaceholder: placeholder => - `//input[contains(@placeholder, "${placeholder}")]`, - textAreaWithPlaceholder: placeholder => - `//textarea[contains(@placeholder, "${placeholder}")]`, - byId: id => `//*[@id="${id}"]`, - divWithClass: classname => `//div[contains(@class, "${classname}")]`, - divWithClassAndText: (classname, text) => - module.exports.objWithClassAndText('div', classname, text), - spanWithClassAndText: (classname, text) => - module.exports.objWithClassAndText('span', classname, text), - toastWithText: text => - module.exports.divWithClassAndText('session-toast-wrapper', text), -}; diff --git a/integration_test/page-objects/conversation.page.js b/integration_test/page-objects/conversation.page.js deleted file mode 100644 index a58b5b9e0..000000000 --- a/integration_test/page-objects/conversation.page.js +++ /dev/null @@ -1,121 +0,0 @@ -const commonPage = require('./common.page'); - -module.exports = { - // conversation view - sessionLoader: commonPage.divWithClass('session-loader'), - leftPaneOverlay: commonPage.divWithClass('module-left-pane-overlay'), - sendMessageTextarea: commonPage.textAreaWithPlaceholder('Type your message'), - sendFriendRequestTextarea: commonPage.textAreaWithPlaceholder( - 'Send your first message' - ), - existingSendMessageText: textMessage => - `//*[contains(@class, "module-message__text--outgoing")and .//span[contains(@class, "text-selectable")][contains(string(), '${textMessage}')]]`, - existingFriendRequestText: textMessage => - `//*[contains(@class, "module-message-friend-request__container")and .//span[contains(@class, "text-selectable")][contains(string(), '${textMessage}')]]`, - existingReceivedMessageText: textMessage => - `//*[contains(@class, "module-message__text--incoming")and .//span[contains(@class, "text-selectable")][contains(string(), '${textMessage}')]]`, - - // conversations - conversationButtonSection: - '//*[contains(@class,"session-icon-button") and .//*[contains(@class, "chatBubble")]]', - retrySendButton: commonPage.divWithClassAndText( - 'module-friend-request__buttonContainer--outgoing', - 'Retry Send' - ), - headerTitleMembers: number => - commonPage.spanWithClassAndText( - 'module-conversation-header__title-text', - `${number} members` - ), - - // channels - globeButtonSection: - '//*[contains(@class,"session-icon-button") and .//*[contains(@class, "globe")]]', - joinOpenGroupButton: commonPage.divRoleButtonWithText('Join Open Group'), - openGroupInputUrl: commonPage.textAreaWithPlaceholder('chat.getsession.org'), - sessionToastJoinOpenGroupSuccess: commonPage.toastWithText( - 'Successfully connected to new open group server' - ), - sessionToastJoinOpenGroupAlreadyExist: commonPage.toastWithText( - 'You are already connected to this public channel' - ), - rowOpenGroupConversationName: groupName => - commonPage.spanWithClassAndText( - 'module-conversation__user__profile-number', - groupName - ), - - // closed group - createClosedGroupButton: commonPage.divRoleButtonWithText( - 'Create Closed Group' - ), - closedGroupNameTextarea: commonPage.textAreaWithPlaceholder( - 'Enter a group name' - ), - createClosedGroupMemberItem: commonPage.divWithClass('session-member-item'), - createClosedGroupMemberItemSelected: commonPage.divWithClass( - 'session-member-item selected' - ), - validateCreationClosedGroupButton: commonPage.divRoleButtonWithText( - 'Create Closed Group' - ), - sessionToastGroupCreatedSuccess: commonPage.toastWithText( - 'Group created successfully' - ), - headerTitleGroupName: groupname => - commonPage.spanWithClassAndText( - 'module-contact-name__profile-name', - groupname - ), - - // contacts - contactsButtonSection: - '//*[contains(@class,"session-icon-button") and .//*[contains(@class, "users")]]', - addContactButton: commonPage.divRoleButtonWithText('Add Contact'), - sessionIDInput: commonPage.textAreaWithPlaceholder('Enter a Session ID'), - nextButton: commonPage.divRoleButtonWithText('Next'), - oneNotificationFriendRequestLeft: - '//*[contains(@class,"session-icon-button") and .//*[contains(@class, "users")] and .//*[contains(@class, "notification-count") and contains(string(), "1")] ]', - oneNotificationFriendRequestTop: - '//*[contains(@class,"notification-count hover") and contains(string(), "1")]', - friendRequestFromUser: (displayName, pubkey) => - `//*[contains(@class,"module-left-pane__list-popup") and .//*[contains(@class, "module-conversation__user") and .//*[contains(string(), "${displayName}")] and .//*[contains(string(), "(...${pubkey.substring( - 60 - )})")]]]`, - acceptFriendRequestButton: - '//*[contains(@role, "button")][contains(@class, "session-button")][contains(string(), "Accept")]', - acceptedFriendRequestMessage: - '//*[contains(@class, "module-friend-request__title")][contains(string(), "Friend request accepted")]', - - // settings - settingsButtonSection: - '//*[contains(@class,"session-icon-button") and .//*[contains(@class, "gear")]]', - deviceSettingsRow: - '//*[contains(@class, "left-pane-setting-category-list-item")][contains(string(), "Devices")]', - - descriptionDeleteAccount: commonPage.spanWithClassAndText( - 'session-confirm-main-message', - 'Are you sure you want to delete your account?' - ), - validateDeleteAccount: commonPage.divRoleButtonDangerWithText('OK'), - - // device linking - noPairedDeviceMessage: - '//*[contains(@class, "session-settings-item__title")][contains(string(), "No linked devices")]', - linkDeviceButton: commonPage.divRoleButtonWithText('Link New Device'), - linkDeviceButtonDisabled: commonPage.divRoleButtonWithTextDisabled( - 'Link New Device' - ), - devicePairingDialog: '//*[contains(@class,"device-pairing-dialog")]', - qrImageDiv: commonPage.divWithClass('qr-image'), - allowPairingButton: commonPage.divRoleButtonWithText('Allow Linking'), - okButton: commonPage.divRoleButtonWithText('OK'), - devicePairedDescription: secretWords => - commonPage.divWithClassAndText( - 'session-settings-item__description', - secretWords - ), - unpairDeviceButton: commonPage.divRoleButtonDangerWithText('Unlink Device'), - deleteAccountButton: commonPage.divRoleButtonDangerWithText('Delete Account'), - validateUnpairDevice: commonPage.divRoleButtonDangerWithText('Unlink'), -}; diff --git a/integration_test/page-objects/registration.page.js b/integration_test/page-objects/registration.page.js deleted file mode 100644 index 0ab617d84..000000000 --- a/integration_test/page-objects/registration.page.js +++ /dev/null @@ -1,40 +0,0 @@ -const commonPage = require('./common.page'); - -module.exports = { - registrationTabSignIn: - '//div[contains(string(), "Sign In")][contains(@class, "session-registration__tab")][contains(@role, "tab")]', - - // create new account - createSessionIDButton: commonPage.divRoleButtonWithText('Create Session ID'), - continueButton: commonPage.divRoleButtonWithText('Continue'), - textareaGeneratedPubkey: - '//textarea[contains(@class, "session-id-editable-textarea")]', - getStartedButton: commonPage.divRoleButtonWithText('Get started'), - - // restore from seed - restoreFromSeedMode: commonPage.divRoleButtonWithText( - 'Restore From Recovery' - ), - - recoveryPhraseInput: commonPage.inputWithPlaceholder('Enter Recovery Phrase'), - displayNameInput: commonPage.inputWithPlaceholder('Enter a display name'), - passwordInput: commonPage.inputWithPlaceholder('Enter password (optional)'), - continueSessionButton: commonPage.divRoleButtonWithText( - 'Continue Your Session' - ), - conversationListContainer: commonPage.divWithClass( - 'module-conversations-list-content' - ), - - // device linking - linkDeviceMode: commonPage.divRoleButtonWithText( - 'Link Device to Existing Session ID' - ), - textareaLinkDevicePubkey: commonPage.textAreaWithPlaceholder( - 'Enter other device’s Session ID here' - ), - linkDeviceTriggerButton: commonPage.divRoleButtonWithText('Link Device'), - toastWrapper: '//*[contains(@class,"session-toast-wrapper")]', - - secretToastDescription: '//p[contains(@class, "description")]', -}; diff --git a/integration_test/registration_test.js b/integration_test/registration_test.js deleted file mode 100644 index a60a5cb52..000000000 --- a/integration_test/registration_test.js +++ /dev/null @@ -1,143 +0,0 @@ -/* eslint-disable prefer-arrow-callback */ -/* eslint-disable func-names */ -/* eslint-disable import/no-extraneous-dependencies */ - -const common = require('./common'); -const { afterEach, beforeEach, describe, it } = require('mocha'); -const RegistrationPage = require('./page-objects/registration.page'); -const ConversationPage = require('./page-objects/conversation.page'); - -describe('Window Test and Login', function() { - let app; - this.timeout(20000); - this.slow(15000); - - beforeEach(async () => { - await common.killallElectron(); - }); - - afterEach(async () => { - await common.stopApp(app); - await common.killallElectron(); - }); - - it('registration: opens one window', async () => { - app = await common.startAndAssureCleanedApp(); - app.client.getWindowCount().should.eventually.be.equal(1); - }); - - it('registration: window title is correct', async () => { - app = await common.startAndAssureCleanedApp(); - - app.client - .getTitle() - .should.eventually.be.equal('Session - test-integration-session'); - }); - - it('registration: can restore from seed', async () => { - app = await common.startAndAssureCleanedApp(); - - await app.client.element(RegistrationPage.registrationTabSignIn).click(); - await app.client.element(RegistrationPage.restoreFromSeedMode).click(); - await app.client - .element(RegistrationPage.recoveryPhraseInput) - .setValue(common.TEST_MNEMONIC1); - await app.client - .element(RegistrationPage.displayNameInput) - .setValue(common.TEST_DISPLAY_NAME1); - - // validate fields are filled - await app.client - .element(RegistrationPage.recoveryPhraseInput) - .getValue() - .should.eventually.equal(common.TEST_MNEMONIC1); - await app.client - .element(RegistrationPage.displayNameInput) - .getValue() - .should.eventually.equal(common.TEST_DISPLAY_NAME1); - - // trigger login - await app.client.element(RegistrationPage.continueSessionButton).click(); - await app.client.waitForExist( - RegistrationPage.conversationListContainer, - 4000 - ); - - await common.timeout(2000); - - await app.webContents - .executeJavaScript("window.storage.get('primaryDevicePubKey')") - .should.eventually.be.equal(common.TEST_PUBKEY1); - }); - - it('registration: can create new account', async () => { - app = await common.startAndAssureCleanedApp(); - await app.client.element(RegistrationPage.createSessionIDButton).click(); - // wait for the animation of generated pubkey to finish - await common.timeout(2000); - const pubkeyGenerated = await app.client - .element(RegistrationPage.textareaGeneratedPubkey) - .getValue(); - // validate generated pubkey - pubkeyGenerated.should.have.lengthOf(66); - pubkeyGenerated.substr(0, 2).should.be.equal('05'); - await app.client.element(RegistrationPage.continueButton).click(); - await app.client.isExisting(RegistrationPage.displayNameInput).should - .eventually.be.true; - await app.client - .element(RegistrationPage.displayNameInput) - .setValue(common.TEST_DISPLAY_NAME1); - await app.client.element(RegistrationPage.getStartedButton).click(); - await app.client.waitForExist( - ConversationPage.conversationButtonSection, - 5000 - ); - - await app.webContents - .executeJavaScript("window.storage.get('primaryDevicePubKey')") - .should.eventually.be.equal(pubkeyGenerated); - }); - - it('registration: can delete account when logged in', async () => { - // login as user1 - const login = { - mnemonic: common.TEST_MNEMONIC1, - displayName: common.TEST_DISPLAY_NAME1, - stubOpenGroups: true, - }; - app = await common.startAndStub(login); - - await app.client.waitForExist( - RegistrationPage.conversationListContainer, - 4000 - ); - - await app.webContents - .executeJavaScript("window.storage.get('primaryDevicePubKey')") - .should.eventually.be.equal(common.TEST_PUBKEY1); - // delete account - await app.client.element(ConversationPage.settingsButtonSection).click(); - await app.client.element(ConversationPage.deleteAccountButton).click(); - await app.client.isExisting(ConversationPage.descriptionDeleteAccount) - .should.eventually.be.true; - // click on the modal OK button to delete the account - await app.client.element(ConversationPage.validateDeleteAccount).click(); - // wait for the app restart - await common.timeout(2000); - - // Spectron will loose the connection with the app during the app restart. - // We have to restart the app without altering the logged in user or anything here, just to get a valid new ref to the app. - await common.stopApp(app); - app = await common.startApp(); - - // validate that on app start, the registration sign in is shown - await app.client.waitForExist(RegistrationPage.registrationTabSignIn, 3000); - // validate that no pubkey are set in storage - await app.webContents - .executeJavaScript("window.storage.get('primaryDevicePubKey')") - .should.eventually.be.equal(null); - // and that the conversation list is not shown - await app.client.isExisting(RegistrationPage.conversationListContainer) - .should.eventually.be.false; - }); -}); diff --git a/integration_test/stubs/stub_app_dot_net_api.js b/integration_test/stubs/stub_app_dot_net_api.js deleted file mode 100644 index 48414c6de..000000000 --- a/integration_test/stubs/stub_app_dot_net_api.js +++ /dev/null @@ -1,166 +0,0 @@ -/* global clearTimeout, Buffer, TextDecoder, process */ - -const OriginalAppDotNetApi = require('../../js/modules/loki_app_dot_net_api.js'); - -const sampleFeed = - ' FeedForAll Sample Feed'; - -const samplesGetMessages = { - meta: { code: 200 }, - data: [ - { - channel_id: 1, - created_at: '2020-03-18T04:48:44.000Z', - entities: { - mentions: [], - hashtags: [], - links: [], - }, - id: 3662, - machine_only: false, - num_replies: 0, - source: {}, - thread_id: 3662, - reply_to: null, - text: 'hgt', - html: 'hgt', - annotations: [ - { - type: 'network.loki.messenger.publicChat', - value: { - timestamp: 1584506921361, - sig: - '262ab113810564d7ff6474dea264e10e2143d91c004903d06d8d9fddb5b74b2c6245865544d5cf76ee16a3fca045bc028a48c51f8a290508a29b6013d014dc83', - sigver: 1, - }, - }, - ], - user: { - id: 2448, - username: - '050cd79763303bcc251bd489a6f7da823a2b8555402b01a7959ebca550d048600f', - created_at: '2020-03-18T02:42:05.000Z', - canonical_url: null, - type: null, - timezone: null, - locale: null, - avatar_image: { - url: null, - width: null, - height: null, - is_default: false, - }, - cover_image: { - url: null, - width: null, - height: null, - is_default: false, - }, - counts: { - following: 0, - posts: 0, - followers: 0, - stars: 0, - }, - name: 'asdf', - annotations: [], - }, - }, - ], -}; - -class StubAppDotNetAPI extends OriginalAppDotNetApi { - // make a request to the server - async serverRequest(endpoint, options = {}) { - const { method } = options; - // console.warn('STUBBED ', method, ':', endpoint); - - if ( - endpoint === 'loki/v1/rss/messenger' || - endpoint === 'loki/v1/rss/loki' - ) { - return { - statusCode: 200, - response: { - data: sampleFeed, - }, - }; - } - - if (endpoint === 'channels/1/messages') { - if (!method) { - return { - statusCode: 200, - response: samplesGetMessages, - }; - } - return { - statusCode: 200, - response: { - data: [], - meta: { - max_id: 0, - }, - }, - }; - } - - if ( - endpoint === 'loki/v1/channel/1/deletes' || - endpoint === 'loki/v1/channel/1/moderators' - ) { - return { - statusCode: 200, - response: { - data: [], - meta: { - max_id: 0, - }, - }, - }; - } - - if (endpoint === 'channels/1') { - let name = 'Unknown group'; - if (this.baseServerUrl.includes('/chat-dev.lokinet.org')) { - name = 'Loki Dev Chat'; - } else if (this.baseServerUrl.includes('/chat.getsession.org')) { - name = 'Session Public Chat'; - } - return { - statusCode: 200, - response: { - data: { - annotations: [ - { - type: 'net.patter-app.settings', - value: { - name, - }, - }, - ], - }, - }, - }; - } - if (endpoint === 'token') { - return { - statusCode: 200, - response: { - data: { - user: { - name: 'unknown name', - }, - }, - }, - }; - } - - return { - statusCode: 200, - response: {}, - }; - } -} - -module.exports = StubAppDotNetAPI; diff --git a/integration_test/stubs/stub_message_api.js b/integration_test/stubs/stub_message_api.js deleted file mode 100644 index c44694785..000000000 --- a/integration_test/stubs/stub_message_api.js +++ /dev/null @@ -1,46 +0,0 @@ -/* global clearTimeout, dcodeIO, Buffer, TextDecoder, process */ -const nodeFetch = require('node-fetch'); - -class StubMessageAPI { - constructor(ourKey) { - this.ourKey = ourKey; - this.baseUrl = 'http://localhost:3000'; - } - - // eslint-disable-next-line no-unused-vars - async sendMessage(pubKey, data, messageTimeStamp, ttl, options = {}) { - // console.warn('STUBBED message api ', pubKey, ttl); - const post = { - method: 'POST', - }; - - const data64 = dcodeIO.ByteBuffer.wrap(data).toString('base64'); - - await nodeFetch( - `${ - this.baseUrl - }/messages?pubkey=${pubKey}×tamp=${messageTimeStamp}&data=${encodeURIComponent( - data64 - )}`, - post - ); - } - - async startLongPolling(numConnections, stopPolling, callback) { - const ourPubkey = this.ourKey; - - const get = { - method: 'GET', - }; - const res = await nodeFetch( - `${this.baseUrl}/messages?pubkey=${ourPubkey}`, - get - ); - const json = await res.json(); - // console.warn('STUBBED polling messages ', json.messages); - - callback(json.messages || []); - } -} - -module.exports = StubMessageAPI; diff --git a/js/background.js b/js/background.js index e494311ed..6d52f87c3 100644 --- a/js/background.js +++ b/js/background.js @@ -320,6 +320,7 @@ getSpellCheck: () => storage.get('spell-check', true), setSpellCheck: value => { storage.put('spell-check', value); + startSpellCheck(); }, addDarkOverlay: () => { @@ -418,6 +419,19 @@ } }); + const startSpellCheck = () => { + if (!window.enableSpellCheck || !window.disableSpellCheck) { + return; + } + + if (window.Events.getSpellCheck()) { + window.enableSpellCheck(); + } else { + window.disableSpellCheck(); + } + }; + startSpellCheck(); + const themeSetting = window.Events.getThemeSetting(); const newThemeSetting = mapOldThemeToNew(themeSetting); window.Events.setThemeSetting(newThemeSetting); @@ -569,6 +583,12 @@ // listeners Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); + // window.Signal.RefreshSenderCertificate.initialize({ + // events: Whisper.events, + // storage, + // navigator, + // logger: window.log, + // }); connect(true); }); @@ -591,6 +611,12 @@ ) { // listeners Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); + // window.Signal.RefreshSenderCertificate.initialize({ + // events: Whisper.events, + // storage, + // navigator, + // logger: window.log, + // }); connect(); appView.openInbox({ @@ -1011,16 +1037,8 @@ }; window.toggleSpellCheck = () => { - const currentValue = window.getSettingValue('spell-check'); - // if undefined, it means 'default' so true. but we have to toggle it, so false - // if not undefined, we take the opposite - const newValue = currentValue !== undefined ? !currentValue : false; + const newValue = !window.getSettingValue('spell-check'); window.Events.setSpellCheck(newValue); - window.pushToast({ - description: window.i18n('spellCheckDirty'), - type: 'info', - id: 'spellCheckDirty', - }); }; window.toggleLinkPreview = () => { @@ -1322,18 +1340,6 @@ } }); - Whisper.events.on('devicePairingRequestReceivedNoListener', async () => { - window.pushToast({ - title: window.i18n('devicePairingRequestReceivedNoListenerTitle'), - description: window.i18n( - 'devicePairingRequestReceivedNoListenerDescription' - ), - type: 'info', - id: 'pairingRequestNoListener', - shouldFade: false, - }); - }); - Whisper.events.on('devicePairingRequestAccepted', async (pubKey, cb) => { try { await getAccountManager().authoriseSecondaryDevice(pubKey); @@ -1422,9 +1428,6 @@ async function connect(firstRun) { window.log.info('connect'); - // Initialize paths for onion requests - await window.lokiSnodeAPI.buildNewOnionPaths(); - // Bootstrap our online/offline detection, only the first time we connect if (connectCount === 0 && navigator.onLine) { window.addEventListener('offline', onOffline); @@ -1535,8 +1538,35 @@ textsecure.storage.user.getDeviceId() != '1' ) { window.getSyncRequest(); + + try { + const manager = window.getAccountManager(); + await Promise.all([ + manager.maybeUpdateDeviceName(), + manager.maybeDeleteSignalingKey(), + ]); + } catch (e) { + window.log.error( + 'Problem with account manager updates after starting new version: ', + e && e.stack ? e.stack : e + ); + } } + // const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery'; + // if (!storage.get(udSupportKey)) { + // const server = WebAPI.connect({ username: USERNAME, password: PASSWORD }); + // try { + // await server.registerSupportForUnauthenticatedDelivery(); + // storage.put(udSupportKey, true); + // } catch (error) { + // window.log.error( + // 'Error: Unable to register for unauthenticated delivery support.', + // error && error.stack ? error.stack : error + // ); + // } + // } + const deviceId = textsecure.storage.user.getDeviceId(); if (firstRun === true && deviceId !== '1') { const hasThemeSetting = Boolean(storage.get('theme-setting')); @@ -2115,7 +2145,7 @@ const shouldSendReceipt = !isError && data.unidentifiedDeliveryReceived && - !data.friendRequest && + !data.isFriendRequest && !isGroup; // Send the receipt async and hope that it succeeds diff --git a/js/conversation_controller.js b/js/conversation_controller.js index 171703716..5e102420d 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -187,7 +187,6 @@ } await conversation.destroyMessages(); - await window.Signal.Data.removeConversation(id, { Conversation: Whisper.Conversation, }); diff --git a/js/expire.js b/js/expire.js index 228508caf..c068921e5 100644 --- a/js/expire.js +++ b/js/expire.js @@ -16,7 +16,7 @@ LokiFileServerAPI.secureRpcPubKey ); - let nextWaitSeconds = 5; + let nextWaitSeconds = 1; const checkForUpgrades = async () => { const result = await window.tokenlessFileServerAdnAPI.serverRequest( 'loki/v1/version/client/desktop' @@ -67,7 +67,9 @@ return res(expiredVersion); } log.info( - `Delaying sending checks for ${nextWaitSeconds}s, no version yet` + 'Delaying sending checks for', + nextWaitSeconds, + 's, no version yet' ); setTimeout(waitForVersion, nextWaitSeconds * 1000); return true; @@ -83,7 +85,11 @@ window.extension.expired = cb => { if (expiredVersion === null) { // just give it another second - log.info(`Delaying expire banner determination for ${nextWaitSeconds}s`); + log.info( + 'Delaying expire banner determination for', + nextWaitSeconds, + 's' + ); setTimeout(() => { window.extension.expired(cb); }, nextWaitSeconds * 1000); diff --git a/js/libphonenumber-util.js b/js/libphonenumber-util.js new file mode 100644 index 000000000..ac13013c8 --- /dev/null +++ b/js/libphonenumber-util.js @@ -0,0 +1,295 @@ +(function() { + 'use strict'; + + /* + * This file extends the libphonenumber object with a set of phonenumbery + * utility functions. libphonenumber must be included before you call these + * functions, but the order of the files/script-tags doesn't matter. + */ + + window.libphonenumber = window.libphonenumber || {}; + window.libphonenumber.util = { + getRegionCodeForNumber: function(number) { + try { + var parsedNumber = libphonenumber.parse(number); + return libphonenumber.getRegionCodeForNumber(parsedNumber); + } catch (e) { + return 'ZZ'; + } + }, + + splitCountryCode: function(number) { + var parsedNumber = libphonenumber.parse(number); + return { + country_code: parsedNumber.values_[1], + national_number: parsedNumber.values_[2], + }; + }, + + getCountryCode: function(regionCode) { + var cc = libphonenumber.getCountryCodeForRegion(regionCode); + return cc !== 0 ? cc : ''; + }, + + parseNumber: function(number, defaultRegionCode) { + try { + var parsedNumber = libphonenumber.parse(number, defaultRegionCode); + + return { + isValidNumber: libphonenumber.isValidNumber(parsedNumber), + regionCode: libphonenumber.getRegionCodeForNumber(parsedNumber), + countryCode: '' + parsedNumber.getCountryCode(), + nationalNumber: '' + parsedNumber.getNationalNumber(), + e164: libphonenumber.format( + parsedNumber, + libphonenumber.PhoneNumberFormat.E164 + ), + }; + } catch (ex) { + return { error: ex, isValidNumber: false }; + } + }, + + getAllRegionCodes: function() { + return { + AD: 'Andorra', + AE: 'United Arab Emirates', + AF: 'Afghanistan', + AG: 'Antigua and Barbuda', + AI: 'Anguilla', + AL: 'Albania', + AM: 'Armenia', + AO: 'Angola', + AR: 'Argentina', + AS: 'AmericanSamoa', + AT: 'Austria', + AU: 'Australia', + AW: 'Aruba', + AX: 'Åland Islands', + AZ: 'Azerbaijan', + BA: 'Bosnia and Herzegovina', + BB: 'Barbados', + BD: 'Bangladesh', + BE: 'Belgium', + BF: 'Burkina Faso', + BG: 'Bulgaria', + BH: 'Bahrain', + BI: 'Burundi', + BJ: 'Benin', + BL: 'Saint Barthélemy', + BM: 'Bermuda', + BN: 'Brunei Darussalam', + BO: 'Bolivia, Plurinational State of', + BR: 'Brazil', + BS: 'Bahamas', + BT: 'Bhutan', + BW: 'Botswana', + BY: 'Belarus', + BZ: 'Belize', + CA: 'Canada', + CC: 'Cocos (Keeling) Islands', + CD: 'Congo, The Democratic Republic of the', + CF: 'Central African Republic', + CG: 'Congo', + CH: 'Switzerland', + CI: "Cote d'Ivoire", + CK: 'Cook Islands', + CL: 'Chile', + CM: 'Cameroon', + CN: 'China', + CO: 'Colombia', + CR: 'Costa Rica', + CU: 'Cuba', + CV: 'Cape Verde', + CX: 'Christmas Island', + CY: 'Cyprus', + CZ: 'Czech Republic', + DE: 'Germany', + DJ: 'Djibouti', + DK: 'Denmark', + DM: 'Dominica', + DO: 'Dominican Republic', + DZ: 'Algeria', + EC: 'Ecuador', + EE: 'Estonia', + EG: 'Egypt', + ER: 'Eritrea', + ES: 'Spain', + ET: 'Ethiopia', + FI: 'Finland', + FJ: 'Fiji', + FK: 'Falkland Islands (Malvinas)', + FM: 'Micronesia, Federated States of', + FO: 'Faroe Islands', + FR: 'France', + GA: 'Gabon', + GB: 'United Kingdom', + GD: 'Grenada', + GE: 'Georgia', + GF: 'French Guiana', + GG: 'Guernsey', + GH: 'Ghana', + GI: 'Gibraltar', + GL: 'Greenland', + GM: 'Gambia', + GN: 'Guinea', + GP: 'Guadeloupe', + GQ: 'Equatorial Guinea', + GR: 'Ελλάδα', + GT: 'Guatemala', + GU: 'Guam', + GW: 'Guinea-Bissau', + GY: 'Guyana', + HK: 'Hong Kong', + HN: 'Honduras', + HR: 'Croatia', + HT: 'Haiti', + HU: 'Magyarország', + ID: 'Indonesia', + IE: 'Ireland', + IL: 'Israel', + IM: 'Isle of Man', + IN: 'India', + IO: 'British Indian Ocean Territory', + IQ: 'Iraq', + IR: 'Iran, Islamic Republic of', + IS: 'Iceland', + IT: 'Italy', + JE: 'Jersey', + JM: 'Jamaica', + JO: 'Jordan', + JP: 'Japan', + KE: 'Kenya', + KG: 'Kyrgyzstan', + KH: 'Cambodia', + KI: 'Kiribati', + KM: 'Comoros', + KN: 'Saint Kitts and Nevis', + KP: "Korea, Democratic People's Republic of", + KR: 'Korea, Republic of', + KW: 'Kuwait', + KY: 'Cayman Islands', + KZ: 'Kazakhstan', + LA: "Lao People's Democratic Republic", + LB: 'Lebanon', + LC: 'Saint Lucia', + LI: 'Liechtenstein', + LK: 'Sri Lanka', + LR: 'Liberia', + LS: 'Lesotho', + LT: 'Lithuania', + LU: 'Luxembourg', + LV: 'Latvia', + LY: 'Libyan Arab Jamahiriya', + MA: 'Morocco', + MC: 'Monaco', + MD: 'Moldova, Republic of', + ME: 'Црна Гора', + MF: 'Saint Martin', + MG: 'Madagascar', + MH: 'Marshall Islands', + MK: 'Macedonia, The Former Yugoslav Republic of', + ML: 'Mali', + MM: 'Myanmar', + MN: 'Mongolia', + MO: 'Macao', + MP: 'Northern Mariana Islands', + MQ: 'Martinique', + MR: 'Mauritania', + MS: 'Montserrat', + MT: 'Malta', + MU: 'Mauritius', + MV: 'Maldives', + MW: 'Malawi', + MX: 'Mexico', + MY: 'Malaysia', + MZ: 'Mozambique', + NA: 'Namibia', + NC: 'New Caledonia', + NE: 'Niger', + NF: 'Norfolk Island', + NG: 'Nigeria', + NI: 'Nicaragua', + NL: 'Netherlands', + NO: 'Norway', + NP: 'Nepal', + NR: 'Nauru', + NU: 'Niue', + NZ: 'New Zealand', + OM: 'Oman', + PA: 'Panama', + PE: 'Peru', + PF: 'French Polynesia', + PG: 'Papua New Guinea', + PH: 'Philippines', + PK: 'Pakistan', + PL: 'Polska', + PM: 'Saint Pierre and Miquelon', + PR: 'Puerto Rico', + PS: 'Palestinian Territory, Occupied', + PT: 'Portugal', + PW: 'Palau', + PY: 'Paraguay', + QA: 'Qatar', + RE: 'Réunion', + RO: 'Romania', + RS: 'Србија', + RU: 'Russia', + RW: 'Rwanda', + SA: 'Saudi Arabia', + SB: 'Solomon Islands', + SC: 'Seychelles', + SD: 'Sudan', + SE: 'Sweden', + SG: 'Singapore', + SH: 'Saint Helena, Ascension and Tristan Da Cunha', + SI: 'Slovenia', + SJ: 'Svalbard and Jan Mayen', + SK: 'Slovakia', + SL: 'Sierra Leone', + SM: 'San Marino', + SN: 'Senegal', + SO: 'Somalia', + SR: 'Suriname', + ST: 'Sao Tome and Principe', + SV: 'El Salvador', + SY: 'Syrian Arab Republic', + SZ: 'Swaziland', + TC: 'Turks and Caicos Islands', + TD: 'Chad', + TG: 'Togo', + TH: 'Thailand', + TJ: 'Tajikistan', + TK: 'Tokelau', + TL: 'Timor-Leste', + TM: 'Turkmenistan', + TN: 'Tunisia', + TO: 'Tonga', + TR: 'Turkey', + TT: 'Trinidad and Tobago', + TV: 'Tuvalu', + TW: 'Taiwan, Province of China', + TZ: 'Tanzania, United Republic of', + UA: 'Ukraine', + UG: 'Uganda', + US: 'United States', + UY: 'Uruguay', + UZ: 'Uzbekistan', + VA: 'Holy See (Vatican City State)', + VC: 'Saint Vincent and the Grenadines', + VE: 'Venezuela', + VG: 'Virgin Islands, British', + VI: 'Virgin Islands, U.S.', + VN: 'Viet Nam', + VU: 'Vanuatu', + WF: 'Wallis and Futuna', + WS: 'Samoa', + YE: 'Yemen', + YT: 'Mayotte', + ZA: 'South Africa', + ZM: 'Zambia', + ZW: 'Zimbabwe', + }; + }, // getAllRegionCodes + }; // libphonenumber.util +})(); diff --git a/js/models/conversations.js b/js/models/conversations.js index 48c6781df..fac026b1a 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -247,12 +247,9 @@ async acceptFriendRequest() { const messages = await window.Signal.Data.getMessagesByConversation( this.id, - { - limit: 1, - MessageCollection: Whisper.MessageCollection, - type: 'friend-request', - } + { limit: 1, MessageCollection: Whisper.MessageCollection } ); + const lastMessageModel = messages.at(0); if (lastMessageModel) { lastMessageModel.acceptFriendRequest(); @@ -267,11 +264,7 @@ async declineFriendRequest() { const messages = await window.Signal.Data.getMessagesByConversation( this.id, - { - limit: 1, - MessageCollection: Whisper.MessageCollection, - type: 'friend-request', - } + { limit: 1, MessageCollection: Whisper.MessageCollection } ); const lastMessageModel = messages.at(0); @@ -2774,9 +2767,7 @@ window.confirmationDialog({ title, message, - resolve: () => { - ConversationController.deleteContact(this.id); - }, + resolve: () => ConversationController.deleteContact(this.id), }); }, diff --git a/js/modules/data.js b/js/modules/data.js index 5d97acd9c..729f8c60e 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -101,9 +101,6 @@ module.exports = { getPrimaryDeviceFor, getPairedDevicesFor, - getGuardNodes, - updateGuardNodes, - createOrUpdateItem, getItemById, getAllItems, @@ -120,7 +117,6 @@ module.exports = { removeAllSessions, getAllSessions, - // Doesn't look like this is used at all getSwarmNodesByPubkey, getConversationCount, @@ -651,14 +647,6 @@ function getSecondaryDevicesFor(primaryDevicePubKey) { return channels.getSecondaryDevicesFor(primaryDevicePubKey); } -function getGuardNodes() { - return channels.getGuardNodes(); -} - -function updateGuardNodes(nodes) { - return channels.updateGuardNodes(nodes); -} - function getPrimaryDeviceFor(secondaryDevicePubKey) { return channels.getPrimaryDeviceFor(secondaryDevicePubKey); } diff --git a/js/modules/debuglogs.js b/js/modules/debuglogs.js index bea65a431..81b00a5b8 100644 --- a/js/modules/debuglogs.js +++ b/js/modules/debuglogs.js @@ -2,24 +2,46 @@ /* global window */ const FormData = require('form-data'); -const fetch = require('node-fetch'); +const got = require('got'); const BASE_URL = 'https://debuglogs.org'; const VERSION = window.getVersion(); const USER_AGENT = `Session ${VERSION}`; +// Workaround: Submitting `FormData` using native `FormData::submit` procedure +// as integration with `got` results in S3 error saying we haven’t set the +// `Content-Length` header: +// https://github.com/sindresorhus/got/pull/466 +const submitFormData = (form, url) => + new Promise((resolve, reject) => { + form.submit(url, (error, response) => { + if (error) { + return reject(error); + } + + const { statusCode } = response; + if (statusCode !== 204) { + return reject( + new Error(`Failed to upload to S3, got status ${statusCode}`) + ); + } + + return resolve(); + }); + }); + // upload :: String -> Promise URL exports.upload = async content => { - const signedForm = await fetch(BASE_URL, { + const signedForm = await got.get(BASE_URL, { + json: true, headers: { 'user-agent': USER_AGENT, }, }); - const json = await signedForm.json(); - if (!signedForm.ok || !json) { + if (!signedForm.body) { throw new Error('Failed to retrieve token'); } - const { fields, url } = json; + const { fields, url } = signedForm.body; const form = new FormData(); // The API expects `key` to be the first field: @@ -38,15 +60,9 @@ exports.upload = async content => { filename: `session-desktop-debug-log-${VERSION}.txt`, }); - const result = await fetch(url, { - method: 'POST', - body: form, - }); - - const { status } = result; - if (status !== 204) { - throw new Error(`Failed to upload to S3, got status ${status}`); - } + // WORKAROUND: See comment on `submitFormData`: + // await got.post(url, { body: form }); + await submitFormData(form, url); return `${BASE_URL}/${fields.key}`; }; diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index ecc8e9d3c..fe2813df2 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -32,8 +32,6 @@ const snodeHttpsAgent = new https.Agent({ rejectUnauthorized: false, }); -const timeoutDelay = ms => new Promise(resolve => setTimeout(resolve, ms)); - const sendToProxy = async ( srvPubKey, endpoint, @@ -46,6 +44,8 @@ const sendToProxy = async ( ); return {}; } + const randSnode = await lokiSnodeAPI.getRandomSnodeAddress(); + const url = `https://${randSnode.ip}:${randSnode.port}/file_proxy`; const fetchOptions = pFetchOptions; // make lint happy // safety issue with file server, just safer to have this @@ -61,7 +61,6 @@ const sendToProxy = async ( }; // from https://github.com/sindresorhus/is-stream/blob/master/index.js - let fileUpload = false; if ( payloadObj.body && typeof payloadObj.body === 'object' && @@ -75,22 +74,8 @@ const sendToProxy = async ( payloadObj.body = { fileUpload: fData.toString('base64'), }; - fileUpload = true; } - // use nodes that support more than 1mb - const randomFunc = fileUpload - ? 'getRandomProxySnodeAddress' - : 'getRandomSnodeAddress'; - const randSnode = await lokiSnodeAPI[randomFunc](); - if (randSnode === false) { - log.warn('proxy random snode pool is not ready, retrying 10s', endpoint); - // no nodes in the pool yet, give it some time and retry - await timeoutDelay(1000); - return sendToProxy(srvPubKey, endpoint, pFetchOptions, options); - } - const url = `https://${randSnode.ip}:${randSnode.port}/file_proxy`; - // convert our payload to binary buffer const payloadData = Buffer.from( dcodeIO.ByteBuffer.wrap(JSON.stringify(payloadObj)).toArrayBuffer() @@ -153,7 +138,7 @@ const sendToProxy = async ( ); // retry (hopefully with new snode) // FIXME: max number of retries... - return sendToProxy(srvPubKey, endpoint, fetchOptions, options); + return sendToProxy(srvPubKey, endpoint, fetchOptions); } let response = {}; @@ -257,8 +242,8 @@ const serverRequest = async (endpoint, options = {}) => { FILESERVER_HOSTS.includes(host) ) { mode = 'sendToProxy'; + // strip trailing slash const search = url.search ? `?${url.search}` : ''; - // strip first slash const endpointWithQS = `${url.pathname}${search}`.replace(/^\//, ''); // log.info('endpointWithQS', endpointWithQS) ({ response, txtResponse, result } = await sendToProxy( @@ -269,7 +254,7 @@ const serverRequest = async (endpoint, options = {}) => { )); } else { // disable check for .loki - process.env.NODE_TLS_REJECT_UNAUTHORIZED = host.match(/\.loki$/i) + process.env.NODE_TLS_REJECT_UNAUTHORIZED = url.host.match(/\.loki$/i) ? '0' : '1'; result = await nodeFetch(url, fetchOptions); @@ -298,7 +283,7 @@ const serverRequest = async (endpoint, options = {}) => { url ); } - if (mode === 'sendToProxy') { + if (mode === '_sendToProxy') { // if we can detect, certain types of failures, we can retry... if (e.code === 'ECONNRESET') { // retry with counter? @@ -312,7 +297,7 @@ const serverRequest = async (endpoint, options = {}) => { if (result.status !== 200) { if (!forceFreshToken && (!response.meta || response.meta.code === 401)) { // retry with forcing a fresh token - return serverRequest(endpoint, { + return this.serverRequest(endpoint, { ...options, forceFreshToken: true, }); @@ -658,7 +643,7 @@ class LokiAppDotNetServerAPI { try { const res = await this.proxyFetch( - new URL(`${this.baseServerUrl}/loki/v1/submit_challenge`), + `${this.baseServerUrl}/loki/v1/submit_challenge`, fetchOptions, { textResponse: true } ); @@ -683,8 +668,7 @@ class LokiAppDotNetServerAPI { } const urlStr = urlObj.toString(); const endpoint = urlStr.replace(`${this.baseServerUrl}/`, ''); - const { response, result } = await sendToProxy( - this.pubKey, + const { response, result } = await this._sendToProxy( endpoint, finalOptions, options @@ -695,8 +679,8 @@ class LokiAppDotNetServerAPI { json: () => response, }; } - const host = urlObj.host.toLowerCase(); - if (host.match(/\.loki$/)) { + const urlStr = urlObj.toString(); + if (urlStr.match(/\.loki\//)) { process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } const result = nodeFetch(urlObj, fetchOptions, options); diff --git a/js/modules/loki_file_server_api.js b/js/modules/loki_file_server_api.js index 47adf81eb..5d3dfdde5 100644 --- a/js/modules/loki_file_server_api.js +++ b/js/modules/loki_file_server_api.js @@ -4,7 +4,6 @@ /* global log: false */ const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); -const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); const DEVICE_MAPPING_USER_ANNOTATION_TYPE = 'network.loki.messenger.devicemapping'; @@ -59,11 +58,7 @@ class LokiFileServerInstance { // LokiAppDotNetAPI (base) should not know about LokiFileServer. async establishConnection(serverUrl, options) { // why don't we extend this? - if (process.env.USE_STUBBED_NETWORK) { - this._server = new StubAppDotNetAPI(this.ourKey, serverUrl); - } else { - this._server = new LokiAppDotNetAPI(this.ourKey, serverUrl); - } + this._server = new LokiAppDotNetAPI(this.ourKey, serverUrl); // configure proxy this._server.pubKey = this.pubKey; diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index fc9352ea7..c3ccf6ae8 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -7,6 +7,7 @@ const { lokiRpc } = require('./loki_rpc'); const DEFAULT_CONNECTIONS = 3; const MAX_ACCEPTABLE_FAILURES = 1; +const LOKI_LONGPOLL_HEADER = 'X-Loki-Long-Poll'; function sleepFor(time) { return new Promise(resolve => { @@ -206,18 +207,6 @@ class LokiMessageAPI { targetNode ); - // do not return true if we get false here... - if (result === false) { - log.warn( - `loki_message:::_sendToNode - Got false from ${targetNode.ip}:${ - targetNode.port - }` - ); - successiveFailures += 1; - // eslint-disable-next-line no-continue - continue; - } - // Make sure we aren't doing too much PoW const currentDifficulty = window.storage.get('PoWDifficulty', null); if ( @@ -294,6 +283,8 @@ class LokiMessageAPI { !stopPollingResult && successiveFailures < MAX_ACCEPTABLE_FAILURES ) { + await sleepFor(successiveFailures * 1000); + // TODO: Revert back to using snode address instead of IP try { // in general, I think we want exceptions to bubble up @@ -346,9 +337,6 @@ class LokiMessageAPI { } successiveFailures += 1; } - - // Always wait a bit as we are no longer long-polling - await sleepFor(Math.max(successiveFailures, 2) * 1000); } if (successiveFailures >= MAX_ACCEPTABLE_FAILURES) { const remainingSwarmSnodes = await lokiSnodeAPI.unreachableNode( @@ -386,6 +374,9 @@ class LokiMessageAPI { const options = { timeout: 40000, ourPubKey: this.ourKey, + headers: { + [LOKI_LONGPOLL_HEADER]: true, + }, }; // let exceptions bubble up @@ -399,15 +390,6 @@ class LokiMessageAPI { nodeData ); - if (result === false) { - // make a note of it because of caller doesn't care... - log.warn( - `loki_message:::_retrieveNextMessages - lokiRpc returned false to ${ - nodeData.ip - }:${nodeData.port}` - ); - } - return result.messages || []; } diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index 20aa2338d..a92a7b31d 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); -const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); class LokiPublicChatFactoryAPI extends EventEmitter { constructor(ourKey) { @@ -60,11 +59,7 @@ class LokiPublicChatFactoryAPI extends EventEmitter { } // after verification then we can start up all the pollers - if (process.env.USE_STUBBED_NETWORK) { - thisServer = new StubAppDotNetAPI(this.ourKey, serverUrl); - } else { - thisServer = new LokiAppDotNetAPI(this.ourKey, serverUrl); - } + thisServer = new LokiAppDotNetAPI(this.ourKey, serverUrl); const gotToken = await thisServer.getOrRefreshServerToken(); if (!gotToken) { diff --git a/js/modules/loki_rpc.js b/js/modules/loki_rpc.js index 5ee2794bd..cb49b975b 100644 --- a/js/modules/loki_rpc.js +++ b/js/modules/loki_rpc.js @@ -1,231 +1,45 @@ /* global log, libloki, textsecure, getStoragePubKey, lokiSnodeAPI, StringView, - libsignal, window, TextDecoder, TextEncoder, dcodeIO, process, crypto */ + libsignal, window, TextDecoder, TextEncoder, dcodeIO, process */ const nodeFetch = require('node-fetch'); const https = require('https'); +const { parse } = require('url'); const snodeHttpsAgent = new https.Agent({ rejectUnauthorized: false, }); +const LOKI_EPHEMKEY_HEADER = 'X-Loki-EphemKey'; const endpointBase = '/storage_rpc/v1'; -// Request index for debugging -let onionReqIdx = 0; - -const timeoutDelay = ms => new Promise(resolve => setTimeout(resolve, ms)); - -const encryptForNode = async (node, payload) => { - const textEncoder = new TextEncoder(); - const plaintext = textEncoder.encode(payload); - - const ephemeral = libloki.crypto.generateEphemeralKeyPair(); - - const snPubkey = StringView.hexToArrayBuffer(node.pubkey_x25519); - - const ephemeralSecret = libsignal.Curve.calculateAgreement( - snPubkey, - ephemeral.privKey - ); - - const salt = window.Signal.Crypto.bytesFromString('LOKI'); - - const key = await crypto.subtle.importKey( - 'raw', - salt, - { name: 'HMAC', hash: { name: 'SHA-256' } }, - false, - ['sign'] - ); - const symmetricKey = await crypto.subtle.sign( - { name: 'HMAC', hash: 'SHA-256' }, - key, - ephemeralSecret - ); - - const ciphertext = await window.libloki.crypto.EncryptGCM( - symmetricKey, - plaintext - ); - - return { ciphertext, symmetricKey, ephemeral_key: ephemeral.pubKey }; -}; - -// Returns the actual ciphertext, symmetric key that will be used -// for decryption, and an ephemeral_key to send to the next hop -const encryptForDestination = async (node, payload) => { - // Do we still need "headers"? - const reqStr = JSON.stringify({ body: payload, headers: '' }); - - return encryptForNode(node, reqStr); -}; - -// `ctx` holds info used by `node` to relay further -const encryptForRelay = async (node, nextNode, ctx) => { - const payload = ctx.ciphertext; - - const reqJson = { - ciphertext: dcodeIO.ByteBuffer.wrap(payload).toString('base64'), - ephemeral_key: StringView.arrayBufferToHex(ctx.ephemeral_key), - destination: nextNode.pubkey_ed25519, - }; - - const reqStr = JSON.stringify(reqJson); - - return encryptForNode(node, reqStr); -}; - -const BAD_PATH = 'bad_path'; - -// May return false BAD_PATH, indicating that we should try a new -const sendOnionRequest = async (reqIdx, nodePath, targetNode, plaintext) => { - log.debug('Sending an onion request'); - - const ctx1 = await encryptForDestination(targetNode, plaintext); - const ctx2 = await encryptForRelay(nodePath[2], targetNode, ctx1); - const ctx3 = await encryptForRelay(nodePath[1], nodePath[2], ctx2); - const ctx4 = await encryptForRelay(nodePath[0], nodePath[1], ctx3); - - const ciphertextBase64 = dcodeIO.ByteBuffer.wrap(ctx4.ciphertext).toString( - 'base64' - ); - - const payload = { - ciphertext: ciphertextBase64, - ephemeral_key: StringView.arrayBufferToHex(ctx4.ephemeral_key), - }; - - const fetchOptions = { - method: 'POST', - body: JSON.stringify(payload), - }; - - const url = `https://${nodePath[0].ip}:${nodePath[0].port}/onion_req`; - - // we only proxy to snodes... - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - const response = await nodeFetch(url, fetchOptions); - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; - - return processOnionResponse(reqIdx, response, ctx1.symmetricKey, true); -}; - -// Process a response as it arrives from `nodeFetch`, handling -// http errors and attempting to decrypt the body with `sharedKey` -const processOnionResponse = async (reqIdx, response, sharedKey, useAesGcm) => { - log.debug(`(${reqIdx}) [path] processing onion response`); - - // detect SNode is not ready (not in swarm; not done syncing) - if (response.status === 503) { - log.warn(`(${reqIdx}) [path] Got 503: snode not ready`); - - return BAD_PATH; - } - - if (response.status === 504) { - log.warn(`(${reqIdx}) [path] Got 504: Gateway timeout`); - return BAD_PATH; - } - - if (response.status === 404) { - // Why would we get this error on testnet? - log.warn(`(${reqIdx}) [path] Got 404: Gateway timeout`); - return BAD_PATH; - } - - if (response.status !== 200) { - log.warn( - `(${reqIdx}) [path] fetch unhandled error code: ${response.status}` - ); - return false; - } - - const ciphertext = await response.text(); - if (!ciphertext) { - log.warn(`(${reqIdx}) [path]: Target node return empty ciphertext`); - return false; - } - - let plaintext; - let ciphertextBuffer; +const decryptResponse = async (response, address) => { + let plaintext = false; try { - ciphertextBuffer = dcodeIO.ByteBuffer.wrap( - ciphertext, - 'base64' - ).toArrayBuffer(); - - const decryptFn = useAesGcm - ? window.libloki.crypto.DecryptGCM - : window.libloki.crypto.DHDecrypt; - - const plaintextBuffer = await decryptFn(sharedKey, ciphertextBuffer); - - const textDecoder = new TextDecoder(); - plaintext = textDecoder.decode(plaintextBuffer); - } catch (e) { - log.error(`(${reqIdx}) [path] decode error`); - if (ciphertextBuffer) { - log.error(`(${reqIdx}) [path] ciphertextBuffer`, ciphertextBuffer); - } - return false; - } - - try { - const jsonRes = JSON.parse(plaintext); - // emulate nodeFetch response... - jsonRes.json = () => { - try { - const res = JSON.parse(jsonRes.body); - return res; - } catch (e) { - log.error(`(${reqIdx}) [path] parse error json: `, jsonRes.body); - } - return false; - }; - return jsonRes; + const ciphertext = await response.text(); + plaintext = await libloki.crypto.snodeCipher.decrypt(address, ciphertext); + const result = plaintext === '' ? {} : JSON.parse(plaintext); + return result; } catch (e) { - log.error('[path] parse error', e.code, e.message, `json:`, plaintext); - return false; - } -}; - -const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { - const _ = window.Lodash; - - let snodePool = await lokiSnodeAPI.getRandomSnodePool(); - - if (snodePool.length < 2) { - log.error( - 'lokiRpc::sendToProxy - Not enough service nodes for a proxy request, only have:', - snodePool.length, - 'snode, attempting refresh' + log.warn( + `Could not decrypt response [${plaintext}] from [${address}],`, + e.code, + e.message ); - await lokiSnodeAPI.refreshRandomPool(); - snodePool = await lokiSnodeAPI.getRandomSnodePool(); - if (snodePool.length < 2) { - log.error( - 'lokiRpc::sendToProxy - Not enough service nodes for a proxy request, only have:', - snodePool.length, - 'failing' - ); - return false; - } } + return {}; +}; - // Making sure the proxy node is not the same as the target node: - const snodePoolSafe = _.without( - snodePool, - _.find(snodePool, { pubkey_ed25519: targetNode.pubkey_ed25519 }) - ); +const timeoutDelay = ms => new Promise(resolve => setTimeout(resolve, ms)); - const randSnode = window.Lodash.sample(snodePoolSafe); +const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { + const randSnode = await lokiSnodeAPI.getRandomSnodeAddress(); // Don't allow arbitrary URLs, only snodes and loki servers const url = `https://${randSnode.ip}:${randSnode.port}/proxy`; const snPubkeyHex = StringView.hexToArrayBuffer(targetNode.pubkey_x25519); - const myKeys = window.libloki.crypto.generateEphemeralKeyPair(); + const myKeys = window.libloki.crypto.snodeCipher._ephemeralKeyPair; const symmetricKey = libsignal.Curve.calculateAgreement( snPubkeyHex, @@ -261,9 +75,8 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { // we got a ton of randomPool nodes, let's just not worry about this one lokiSnodeAPI.markRandomNodeUnreachable(randSnode); const randomPoolRemainingCount = lokiSnodeAPI.getRandomPoolLength(); - const ciphertext = await response.text(); log.warn( - `lokiRpc:::sendToProxy -`, + `lokiRpc sendToProxy`, `snode ${randSnode.ip}:${randSnode.port} to ${targetNode.ip}:${ targetNode.port }`, @@ -279,13 +92,16 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { // detect SNode is not ready (not in swarm; not done syncing) if (response.status === 503 || response.status === 500) { - // this doesn't mean the random node is bad, it could be the target node - // but we got a ton of randomPool nodes, let's just not worry about this one + const ciphertext = await response.text(); + // we shouldn't do these, + // it's seems to be not the random node that's always bad + // but the target node + + // we got a ton of randomPool nodes, let's just not worry about this one lokiSnodeAPI.markRandomNodeUnreachable(randSnode); const randomPoolRemainingCount = lokiSnodeAPI.getRandomPoolLength(); - const ciphertext = await response.text(); log.warn( - `lokiRpc:::sendToProxy -`, + `lokiRpc sendToProxy`, `snode ${randSnode.ip}:${randSnode.port} to ${targetNode.ip}:${ targetNode.port }`, @@ -302,11 +118,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { // it's likely a net problem or an actual problem on the target node // lets mark the target node bad for now // we'll just rotate it back in if it's a net problem - log.warn( - `lokiRpc:::sendToProxy - Failing ${targetNode.ip}:${ - targetNode.port - } after 5 retries` - ); + log.warn(`Failing ${targetNode.ip}:${targetNode.port} after 5 retries`); if (options.ourPubKey) { lokiSnodeAPI.unreachableNode(options.ourPubKey, targetNode); } @@ -329,7 +141,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { if (response.status !== 200) { // let us know we need to create handlers for new unhandled codes log.warn( - 'lokiRpc:::sendToProxy - fetch non-200 statusCode', + 'lokiRpc sendToProxy fetch non-200 statusCode', response.status, `from snode ${randSnode.ip}:${randSnode.port} to ${targetNode.ip}:${ targetNode.port @@ -343,11 +155,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { // avoid base64 decode failure // usually a 500 but not always // could it be a timeout? - log.warn( - 'lokiRpc:::sendToProxy - Server did not return any data for', - options, - targetNode - ); + log.warn('Server did not return any data for', options, targetNode); return false; } @@ -368,7 +176,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { plaintext = textDecoder.decode(plaintextBuffer); } catch (e) { log.error( - 'lokiRpc:::sendToProxy - decode error', + 'lokiRpc sendToProxy decode error', e.code, e.message, `from ${randSnode.ip}:${randSnode.port} to ${targetNode.ip}:${ @@ -390,7 +198,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { return JSON.parse(jsonRes.body); } catch (e) { log.error( - 'lokiRpc:::sendToProxy - parse error', + 'lokiRpc sendToProxy parse error', e.code, e.message, `from ${randSnode.ip}:${randSnode.port} json:`, @@ -401,7 +209,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { }; if (retryNumber) { log.info( - `lokiRpc:::sendToProxy - request succeeded,`, + `lokiRpc sendToProxy request succeeded,`, `snode ${randSnode.ip}:${randSnode.port} to ${targetNode.ip}:${ targetNode.port }`, @@ -411,7 +219,7 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { return jsonRes; } catch (e) { log.error( - 'lokiRpc:::sendToProxy - parse error', + 'lokiRpc sendToProxy parse error', e.code, e.message, `from ${randSnode.ip}:${randSnode.port} json:`, @@ -422,11 +230,31 @@ const sendToProxy = async (options = {}, targetNode, retryNumber = 0) => { }; // A small wrapper around node-fetch which deserializes response -// returns nodeFetch response or false const lokiFetch = async (url, options = {}, targetNode = null) => { const timeout = options.timeout || 10000; const method = options.method || 'GET'; + const address = parse(url).hostname; + // const doEncryptChannel = address.endsWith('.snode'); + const doEncryptChannel = false; // ENCRYPTION DISABLED + if (doEncryptChannel) { + try { + // eslint-disable-next-line no-param-reassign + options.body = await libloki.crypto.snodeCipher.encrypt( + address, + options.body + ); + // eslint-disable-next-line no-param-reassign + options.headers = { + ...options.headers, + 'Content-Type': 'text/plain', + [LOKI_EPHEMKEY_HEADER]: libloki.crypto.snodeCipher.getChannelPublicKeyHex(), + }; + } catch (e) { + log.warn(`Could not encrypt channel for ${address}: `, e); + } + } + const fetchOptions = { ...options, timeout, @@ -434,61 +262,8 @@ const lokiFetch = async (url, options = {}, targetNode = null) => { }; try { - // Absence of targetNode indicates that we want a direct connection - // (e.g. to connect to a seed node for the first time) - if (window.lokiFeatureFlags.useOnionRequests && targetNode) { - // Loop until the result is not BAD_PATH - // eslint-disable-next-line no-constant-condition - while (true) { - // Get a path excluding `targetNode`: - // eslint-disable-next-line no-await-in-loop - const path = await lokiSnodeAPI.getOnionPath(targetNode); - const thisIdx = onionReqIdx; - onionReqIdx += 1; - - log.debug( - `(${thisIdx}) using path ${path[0].ip}:${path[0].port} -> ${ - path[1].ip - }:${path[1].port} -> ${path[2].ip}:${path[2].port} => ${ - targetNode.ip - }:${targetNode.port}` - ); - - // eslint-disable-next-line no-await-in-loop - const result = await sendOnionRequest( - thisIdx, - path, - targetNode, - fetchOptions.body - ); - - if (result === BAD_PATH) { - log.error('[path] Error on the path'); - lokiSnodeAPI.markPathAsBad(path); - } else { - return result ? result.json() : false; - } - } - } - if (window.lokiFeatureFlags.useSnodeProxy && targetNode) { const result = await sendToProxy(fetchOptions, targetNode); - if (result === false) { - // should we retry? - log.warn(`lokiRpc:::lokiFetch - sendToProxy returned false`); - // one case is: - // snodePool didn't have enough - // even after a refresh - // likely a network disconnect? - // but not all cases... - /* - log.warn( - 'lokiRpc:::lokiFetch - useSnodeProxy failure, could not refresh randomPool, offline?' - ); - */ - // pass the false value up - return false; - } // if not result, maybe we should throw?? return result ? result.json() : {}; } @@ -498,7 +273,7 @@ const lokiFetch = async (url, options = {}, targetNode = null) => { fetchOptions.agent = snodeHttpsAgent; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } else { - log.info('lokirpc:::lokiFetch - http communication', url); + log.info('lokiRpc http communication', url); } const response = await nodeFetch(url, fetchOptions); // restore TLS checking @@ -507,14 +282,22 @@ const lokiFetch = async (url, options = {}, targetNode = null) => { let result; // Wrong swarm if (response.status === 421) { - result = await response.json(); + if (doEncryptChannel) { + result = decryptResponse(response, address); + } else { + result = await response.json(); + } const newSwarm = result.snodes ? result.snodes : []; throw new textsecure.WrongSwarmError(newSwarm); } // Wrong PoW difficulty if (response.status === 432) { - result = await response.json(); + if (doEncryptChannel) { + result = decryptResponse(response, address); + } else { + result = await response.json(); + } const { difficulty } = result; throw new textsecure.WrongDifficultyError(difficulty); } @@ -533,6 +316,8 @@ const lokiFetch = async (url, options = {}, targetNode = null) => { result = await response.json(); } else if (options.responseType === 'arraybuffer') { result = await response.buffer(); + } else if (doEncryptChannel) { + result = decryptResponse(response, address); } else { result = await response.text(); } @@ -547,7 +332,6 @@ const lokiFetch = async (url, options = {}, targetNode = null) => { }; // Wrapper for a JSON RPC request -// Annoyngly, this is used for Lokid requests too const lokiRpc = ( address, port, diff --git a/js/modules/loki_snode_api.js b/js/modules/loki_snode_api.js index fe7457037..46b18b9ee 100644 --- a/js/modules/loki_snode_api.js +++ b/js/modules/loki_snode_api.js @@ -1,276 +1,23 @@ /* eslint-disable class-methods-use-this */ -/* global window, textsecure, ConversationController, _, log, clearTimeout, process, Buffer, StringView, dcodeIO */ +/* global window, ConversationController, _, log, clearTimeout */ const is = require('@sindresorhus/is'); const { lokiRpc } = require('./loki_rpc'); -const https = require('https'); -const nodeFetch = require('node-fetch'); -const semver = require('semver'); - -const snodeHttpsAgent = new https.Agent({ - rejectUnauthorized: false, -}); const RANDOM_SNODES_TO_USE_FOR_PUBKEY_SWARM = 3; +const RANDOM_SNODES_POOL_SIZE = 1024; const SEED_NODE_RETRIES = 3; -const timeoutDelay = ms => new Promise(resolve => setTimeout(resolve, ms)); - class LokiSnodeAPI { constructor({ serverUrl, localUrl }) { if (!is.string(serverUrl)) { - throw new Error('LokiSnodeAPI.initialize: Invalid server url'); + throw new Error('WebAPI.initialize: Invalid server url'); } this.serverUrl = serverUrl; // random.snode this.localUrl = localUrl; // localhost.loki this.randomSnodePool = []; this.swarmsPendingReplenish = {}; this.refreshRandomPoolPromise = false; - this.versionPools = {}; - this.versionMap = {}; // reverse version look up - this.versionsRetrieved = false; // to mark when it's done getting versions - - this.onionPaths = []; - this.guardNodes = []; - } - - async getRandomSnodePool() { - if (this.randomSnodePool.length === 0) { - await this.refreshRandomPool(); - } - return this.randomSnodePool; - } - - getRandomPoolLength() { - return this.randomSnodePool.length; - } - - async testGuardNode(snode) { - log.info('Testing a candidate guard node ', snode); - - // Send a post request and make sure it is OK - const endpoint = '/storage_rpc/v1'; - - const url = `https://${snode.ip}:${snode.port}${endpoint}`; - - const ourPK = textsecure.storage.user.getNumber(); - const pubKey = window.getStoragePubKey(ourPK); // truncate if testnet - - const method = 'get_snodes_for_pubkey'; - const params = { pubKey }; - const body = { - jsonrpc: '2.0', - id: '0', - method, - params, - }; - - const fetchOptions = { - method: 'POST', - body: JSON.stringify(body), - headers: { 'Content-Type': 'application/json' }, - timeout: 10000, // 10s, we want a smaller timeout for testing - }; - - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - - let response; - - try { - response = await nodeFetch(url, fetchOptions); - } catch (e) { - if (e.type === 'request-timeout') { - log.warn(`test timeout for node,`, snode); - } - return false; - } finally { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; - } - - if (!response.ok) { - log.info(`Node failed the guard test:`, snode); - } - - return response.ok; - } - - async selectGuardNodes() { - const _ = window.Lodash; - - let nodePool = await this.getRandomSnodePool(); - - if (nodePool.length === 0) { - log.error(`Could not select guarn nodes: node pool is empty`); - return []; - } - - let shuffled = _.shuffle(nodePool); - - let guardNodes = []; - - const DESIRED_GUARD_COUNT = 3; - if (shuffled.length < DESIRED_GUARD_COUNT) { - log.error( - `Could not select guarn nodes: node pool is not big enough, pool size ${ - shuffled.length - }, need ${DESIRED_GUARD_COUNT}, attempting to refresh randomPool` - ); - await this.refreshRandomPool(); - nodePool = await this.getRandomSnodePool(); - shuffled = _.shuffle(nodePool); - if (shuffled.length < DESIRED_GUARD_COUNT) { - log.error( - `Could not select guarn nodes: node pool is not big enough, pool size ${ - shuffled.length - }, need ${DESIRED_GUARD_COUNT}, failing...` - ); - return []; - } - } - - // The use of await inside while is intentional: - // we only want to repeat if the await fails - // eslint-disable-next-line-no-await-in-loop - while (guardNodes.length < 3) { - if (shuffled.length < DESIRED_GUARD_COUNT) { - log.error(`Not enought nodes in the pool`); - break; - } - - const candidateNodes = shuffled.splice(0, DESIRED_GUARD_COUNT); - - // Test all three nodes at once - // eslint-disable-next-line no-await-in-loop - const idxOk = await Promise.all( - candidateNodes.map(n => this.testGuardNode(n)) - ); - - const goodNodes = _.zip(idxOk, candidateNodes) - .filter(x => x[0]) - .map(x => x[1]); - - guardNodes = _.concat(guardNodes, goodNodes); - } - - if (guardNodes.length < DESIRED_GUARD_COUNT) { - log.error( - `COULD NOT get enough guard nodes, only have: ${guardNodes.length}` - ); - } - - log.info('new guard nodes: ', guardNodes); - - const edKeys = guardNodes.map(n => n.pubkey_ed25519); - - await window.libloki.storage.updateGuardNodes(edKeys); - - return guardNodes; - } - - async getOnionPath(toExclude = null) { - const _ = window.Lodash; - - const goodPaths = this.onionPaths.filter(x => !x.bad); - - if (goodPaths.length < 2) { - log.error( - `Must have at least 2 good onion paths, actual: ${goodPaths.length}` - ); - await this.buildNewOnionPaths(); - } - - const paths = _.shuffle(goodPaths); - - if (!toExclude) { - return paths[0]; - } - - // Select a path that doesn't contain `toExclude` - const otherPaths = paths.filter( - path => - !_.some(path, node => node.pubkey_ed25519 === toExclude.pubkey_ed25519) - ); - - if (otherPaths.length === 0) { - // This should never happen! - throw new Error('No onion paths available after filtering'); - } - - return otherPaths[0].path; - } - - async markPathAsBad(path) { - this.onionPaths.forEach(p => { - if (p.path === path) { - // eslint-disable-next-line no-param-reassign - p.bad = true; - } - }); - } - - async buildNewOnionPaths() { - // Note: this function may be called concurrently, so - // might consider blocking the other calls - - const _ = window.Lodash; - - log.info('building new onion paths'); - - const allNodes = await this.getRandomSnodePool(); - - if (this.guardNodes.length === 0) { - // Not cached, load from DB - const nodes = await window.libloki.storage.getGuardNodes(); - - if (nodes.length === 0) { - log.warn('no guard nodes in DB. Will be selecting new guards nodes...'); - } else { - // We only store the nodes' keys, need to find full entries: - const edKeys = nodes.map(x => x.ed25519PubKey); - this.guardNodes = allNodes.filter( - x => edKeys.indexOf(x.pubkey_ed25519) !== -1 - ); - - if (this.guardNodes.length < edKeys.length) { - log.warn( - `could not find some guard nodes: ${this.guardNodes.length}/${ - edKeys.length - } left` - ); - } - } - - // If guard nodes is still empty (the old nodes are now invalid), select new ones: - if (this.guardNodes.length === 0) { - this.guardNodes = await this.selectGuardNodes(); - } - } - - // TODO: select one guard node and 2 other nodes randomly - let otherNodes = _.difference(allNodes, this.guardNodes); - - if (otherNodes.length < 2) { - log.error('Too few nodes to build an onion path!'); - return; - } - - otherNodes = _.shuffle(otherNodes); - const guards = _.shuffle(this.guardNodes); - - // Create path for every guard node: - - // Each path needs 2 nodes in addition to the guard node: - const maxPath = Math.floor(Math.min(guards.length, otherNodes.length / 2)); - - // TODO: might want to keep some of the existing paths - this.onionPaths = []; - - for (let i = 0; i < maxPath; i += 1) { - const path = [guards[i], otherNodes[i * 2], otherNodes[i * 2 + 1]]; - this.onionPaths.push({ path, bad: false }); - } - - log.info('Built onion paths: ', this.onionPaths); } async getRandomSnodeAddress() { @@ -282,95 +29,11 @@ class LokiSnodeAPI { if (this.randomSnodePool.length === 0) { throw new window.textsecure.SeedNodeError('Invalid seed node response'); } - // FIXME: _.sample? return this.randomSnodePool[ Math.floor(Math.random() * this.randomSnodePool.length) ]; } - async getNodesMinVersion(minVersion) { - const _ = window.Lodash; - - return _.flatten( - _.entries(this.versionPools) - .filter(v => semver.gte(v[0], minVersion)) - .map(v => v[1]) - ); - } - - // use nodes that support more than 1mb - async getRandomProxySnodeAddress() { - /* resolve random snode */ - if (this.randomSnodePool.length === 0) { - // allow exceptions to pass through upwards - await this.refreshRandomPool(); - } - if (this.randomSnodePool.length === 0) { - throw new window.textsecure.SeedNodeError('Invalid seed node response'); - } - const goodVersions = Object.keys(this.versionPools).filter(version => - semver.gt(version, '2.0.1') - ); - if (!goodVersions.length) { - return false; - } - // FIXME: _.sample? - const goodVersion = - goodVersions[Math.floor(Math.random() * goodVersions.length)]; - const pool = this.versionPools[goodVersion]; - // FIXME: _.sample? - return pool[Math.floor(Math.random() * pool.length)]; - } - - // WARNING: this leaks our IP to all snodes but with no other identifying information - // except that a client started up or ran out of random pool snodes - async getVersion(node) { - try { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - const result = await nodeFetch( - `https://${node.ip}:${node.port}/get_stats/v1`, - { agent: snodeHttpsAgent } - ); - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; - const data = await result.json(); - if (data.version) { - if (this.versionPools[data.version] === undefined) { - this.versionPools[data.version] = [node]; - } else { - this.versionPools[data.version].push(node); - } - // set up reverse mapping for removal lookup - this.versionMap[`${node.ip}:${node.port}`] = data.version; - } - } catch (e) { - // ECONNREFUSED likely means it's just offline... - // ECONNRESET seems to retry and fail as ECONNREFUSED (so likely a node going offline) - // ETIMEDOUT not sure what to do about these - // retry for now but maybe we should be marking bad... - if (e.code === 'ECONNREFUSED') { - this.markRandomNodeUnreachable(node, { versionPoolFailure: true }); - const randomNodesLeft = this.getRandomPoolLength(); - // clean up these error messages to be a little neater - log.warn( - `loki_snode:::getVersion - ${node.ip}:${ - node.port - } is offline, removing, leaving ${randomNodesLeft} in the randomPool` - ); - } else { - // mostly ECONNRESETs - // ENOTFOUND could mean no internet or hiccup - log.warn( - 'loki_snode:::getVersion - Error', - e.code, - e.message, - `on ${node.ip}:${node.port} retrying in 1s` - ); - await timeoutDelay(1000); - await this.getVersion(node); - } - } - } - async refreshRandomPool(seedNodes = [...window.seedNodeList]) { // if currently not in progress if (this.refreshRandomPoolPromise === false) { @@ -379,10 +42,8 @@ class LokiSnodeAPI { let timeoutTimer = null; // private retry container const trySeedNode = async (consecutiveErrors = 0) => { - // Removed limit until there is a way to get snode info - // for individual nodes (needed for guard nodes); this way - // we get all active nodes const params = { + limit: RANDOM_SNODES_POOL_SIZE, active_only: true, fields: { public_ip: true, @@ -412,12 +73,6 @@ class LokiSnodeAPI { snodes = response.result.service_node_states.filter( snode => snode.public_ip !== '0.0.0.0' ); - // make sure order of the list is random, so we get version in a non-deterministic way - snodes = _.shuffle(snodes); - // commit changes to be live - // we'll update the version (in case they upgrade) every cycle - this.versionPools = {}; - this.versionsRetrieved = false; this.randomSnodePool = snodes.map(snode => ({ ip: snode.public_ip, port: snode.storage_port, @@ -435,35 +90,7 @@ class LokiSnodeAPI { clearTimeout(timeoutTimer); timeoutTimer = null; } - // start polling versions resolve(); - // now get version for all snodes - // also acts an early online test/purge of bad nodes - let c = 0; - const verionStart = Date.now(); - const t = this.randomSnodePool.length; - const noticeEvery = parseInt(t / 10, 10); - // eslint-disable-next-line no-restricted-syntax - for (const node of this.randomSnodePool) { - c += 1; - // eslint-disable-next-line no-await-in-loop - await this.getVersion(node); - if (c % noticeEvery === 0) { - // give stats - const diff = Date.now() - verionStart; - log.info( - `${c}/${t} pool version status update, has taken ${diff.toLocaleString()}ms` - ); - Object.keys(this.versionPools).forEach(version => { - const nodes = this.versionPools[version].length; - log.info( - `version ${version} has ${nodes.toLocaleString()} snodes` - ); - }); - } - } - log.info('Versions retrieved from network!'); - this.versionsRetrieved = true; } catch (e) { log.warn( 'loki_snodes:::refreshRandomPoolPromise - error', @@ -518,7 +145,6 @@ class LokiSnodeAPI { throw new window.textsecure.SeedNodeError('Failed to contact seed node'); } log.info('loki_snodes:::refreshRandomPoolPromise - RESOLVED'); - delete this.refreshRandomPoolPromise; // clear any lock } // unreachableNode.url is like 9hrje1bymy7hu6nmtjme9idyu3rm8gr3mkstakjyuw1997t7w4ny.snode @@ -554,45 +180,15 @@ class LokiSnodeAPI { return filteredNodes; } - markRandomNodeUnreachable(snode, options = {}) { - // avoid retries when we can't get the version because they're offline - if (!options.versionPoolFailure) { - const snodeVersion = this.versionMap[`${snode.ip}:${snode.port}`]; - if (this.versionPools[snodeVersion]) { - this.versionPools[snodeVersion] = _.without( - this.versionPools[snodeVersion], - snode - ); - } else { - if (snodeVersion) { - // reverse map (versionMap) is out of sync with versionPools - log.error( - 'loki_snode:::markRandomNodeUnreachable - No snodes for version', - snodeVersion, - 'retrying in 10s' - ); - } else { - // we don't know our version yet - // and if we're offline, we'll likely not get it until it restarts if it does... - log.warn( - 'loki_snode:::markRandomNodeUnreachable - No version for snode', - `${snode.ip}:${snode.port}`, - 'retrying in 10s' - ); - } - // make sure we don't retry past 15 mins (10s * 100 ~ 1000s) - const retries = options.retries || 0; - if (retries < 100) { - setTimeout(() => { - this.markRandomNodeUnreachable(snode, { - ...options, - retries: retries + 1, - }); - }, 10000); - } - } - } - this.randomSnodePool = _.without(this.randomSnodePool, snode); + markRandomNodeUnreachable(snode) { + this.randomSnodePool = _.without( + this.randomSnodePool, + _.find(this.randomSnodePool, { ip: snode.ip, port: snode.port }) + ); + } + + getRandomPoolLength() { + return this.randomSnodePool.length; } async updateLastHash(snode, hash, expiresAt) { @@ -653,98 +249,6 @@ class LokiSnodeAPI { return newSwarmNodes; } - // helper function - async _requestLnsMapping(node, nameHash) { - log.debug('[lns] lns requests to {}:{}', node.ip, node.port); - - try { - // Hm, in case of proxy/onion routing we - // are not even using ip/port... - return lokiRpc( - `https://${node.ip}`, - node.port, - 'get_lns_mapping', - { - name_hash: nameHash, - }, - {}, - '/storage_rpc/v1', - node - ); - } catch (e) { - log.warn('exception caught making lns requests to a node', node, e); - return false; - } - } - - async getLnsMapping(lnsName) { - const _ = window.Lodash; - - const input = Buffer.from(lnsName); - - const output = await window.blake2b(input); - - const nameHash = dcodeIO.ByteBuffer.wrap(output).toString('base64'); - - // Get nodes capable of doing LNS - let lnsNodes = await this.getNodesMinVersion('2.0.3'); - lnsNodes = _.shuffle(lnsNodes); - - // Loop until 3 confirmations - - // We don't trust any single node, so we accumulate - // answers here and select a dominating answer - const allResults = []; - let ciphertextHex = null; - - while (!ciphertextHex) { - if (lnsNodes.length < 3) { - log.error('Not enough nodes for lns lookup'); - return false; - } - - // extract 3 and make requests in parallel - const nodes = lnsNodes.splice(0, 3); - - // eslint-disable-next-line no-await-in-loop - const results = await Promise.all( - nodes.map(node => this._requestLnsMapping(node, nameHash)) - ); - - results.forEach(res => { - if ( - res && - res.result && - res.result.status === 'OK' && - res.result.entries && - res.result.entries.length > 0 - ) { - allResults.push(results[0].result.entries[0].encrypted_value); - } - }); - - const [winner, count] = _.maxBy( - _.entries(_.countBy(allResults)), - x => x[1] - ); - - if (count >= 3) { - // eslint-disable-next-lint prefer-destructuring - ciphertextHex = winner; - } - } - - const ciphertext = new Uint8Array( - StringView.hexToArrayBuffer(ciphertextHex) - ); - - const res = await window.decryptLnsEntry(lnsName, ciphertext); - - const pubkey = StringView.arrayBufferToHex(res); - - return pubkey; - } - async getSnodesForPubkey(snode, pubKey) { try { const result = await lokiRpc( diff --git a/js/modules/privacy.js b/js/modules/privacy.js index 1720b7420..d737f5d05 100644 --- a/js/modules/privacy.js +++ b/js/modules/privacy.js @@ -7,7 +7,7 @@ const { compose } = require('lodash/fp'); const { escapeRegExp } = require('lodash'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); -const SESSION_ID_PATTERN = /\b(05[0-9a-f]{64})\b/gi; +const PHONE_NUMBER_PATTERN = /\+\d{7,12}(\d{3})/g; const GROUP_ID_PATTERN = /(group\()([^)]+)(\))/g; const REDACTION_PLACEHOLDER = '[REDACTED]'; @@ -55,13 +55,13 @@ exports._pathToRegExp = filePath => { }; // Public API -// redactSessionID :: String -> String -exports.redactSessionID = text => { +// redactPhoneNumbers :: String -> String +exports.redactPhoneNumbers = text => { if (!is.string(text)) { throw new TypeError("'text' must be a string"); } - return text.replace(SESSION_ID_PATTERN, REDACTION_PLACEHOLDER); + return text.replace(PHONE_NUMBER_PATTERN, `+${REDACTION_PLACEHOLDER}$1`); }; // redactGroupIds :: String -> String @@ -84,7 +84,7 @@ exports.redactSensitivePaths = exports._redactPath(APP_ROOT_PATH); exports.redactAll = compose( exports.redactSensitivePaths, exports.redactGroupIds, - exports.redactSessionID + exports.redactPhoneNumbers ); const removeNewlines = text => text.replace(/\r?\n|\r/g, ''); diff --git a/js/modules/refresh_sender_certificate.js b/js/modules/refresh_sender_certificate.js new file mode 100644 index 000000000..275453fda --- /dev/null +++ b/js/modules/refresh_sender_certificate.js @@ -0,0 +1,120 @@ +/* global window, setTimeout, clearTimeout, textsecure, WebAPI, ConversationController */ + +module.exports = { + initialize, +}; + +const ONE_DAY = 24 * 60 * 60 * 1000; // one day +const MINIMUM_TIME_LEFT = 2 * 60 * 60 * 1000; // two hours + +let timeout = null; +let scheduledTime = null; +let scheduleNext = null; + +// We need to refresh our own profile regularly to account for newly-added devices which +// do not support unidentified delivery. +function refreshOurProfile() { + const ourNumber = textsecure.storage.user.getNumber(); + const conversation = ConversationController.getOrCreate(ourNumber, 'private'); + conversation.getProfiles(); +} + +function initialize({ events, storage, navigator, logger }) { + // We don't want to set up all of the below functions, but we do want to ensure that our + // refresh timer is up-to-date. + if (scheduleNext) { + scheduleNext(); + return; + } + + runWhenOnline(); + + events.on('timetravel', scheduleNextRotation); + + function scheduleNextRotation() { + const now = Date.now(); + const certificate = storage.get('senderCertificate'); + if (!certificate) { + setTimeoutForNextRun(now); + + return; + } + + // The useful information in a SenderCertificate is all serialized, so we + // need to do another layer of decoding. + const decoded = textsecure.protobuf.SenderCertificate.Certificate.decode( + certificate.certificate + ); + const expires = decoded.expires.toNumber(); + + const time = Math.min(now + ONE_DAY, expires - MINIMUM_TIME_LEFT); + + setTimeoutForNextRun(time); + } + + // Keeping this entrypoint around so more inialize() calls just kick the timing + scheduleNext = scheduleNextRotation; + + async function run() { + logger.info('refreshSenderCertificate: Getting new certificate...'); + try { + const username = storage.get('number_id'); + const password = storage.get('password'); + const server = WebAPI.connect({ username, password }); + + const { certificate } = await server.getSenderCertificate(); + const arrayBuffer = window.Signal.Crypto.base64ToArrayBuffer(certificate); + const decoded = textsecure.protobuf.SenderCertificate.decode(arrayBuffer); + + decoded.certificate = decoded.certificate.toArrayBuffer(); + decoded.signature = decoded.signature.toArrayBuffer(); + decoded.serialized = arrayBuffer; + + storage.put('senderCertificate', decoded); + scheduleNextRotation(); + } catch (error) { + logger.error( + 'refreshSenderCertificate: Get failed. Trying again in two minutes...', + error && error.stack ? error.stack : error + ); + setTimeout(runWhenOnline, 2 * 60 * 1000); + } + + refreshOurProfile(); + } + + function runWhenOnline() { + if (navigator.onLine) { + run(); + } else { + logger.info( + 'refreshSenderCertificate: Offline. Will update certificate when online...' + ); + const listener = () => { + logger.info( + 'refreshSenderCertificate: Online. Now updating certificate...' + ); + window.removeEventListener('online', listener); + run(); + }; + window.addEventListener('online', listener); + } + } + + function setTimeoutForNextRun(time = Date.now()) { + const now = Date.now(); + + if (scheduledTime !== time || !timeout) { + logger.info( + 'Next sender certificate refresh scheduled for', + new Date(time).toISOString() + ); + } + + scheduledTime = time; + const waitTime = Math.max(0, time - now); + + clearTimeout(timeout); + timeout = setTimeout(runWhenOnline, waitTime); + } +} diff --git a/js/modules/signal.js b/js/modules/signal.js index c676c5845..f5ddc198b 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -13,6 +13,7 @@ const Settings = require('./settings'); const Util = require('../../ts/util'); const { migrateToSQL } = require('./migrate_to_sql'); const Metadata = require('./metadata/SecretSessionCipher'); +const RefreshSenderCertificate = require('./refresh_sender_certificate'); const LinkPreviews = require('./link_previews'); const AttachmentDownloads = require('./attachment_downloads'); @@ -378,6 +379,7 @@ exports.setup = (options = {}) => { Migrations, Notifications, OS, + RefreshSenderCertificate, Settings, State, Types, diff --git a/js/modules/web_api.js b/js/modules/web_api.js index 0b89c3804..c92681b05 100644 --- a/js/modules/web_api.js +++ b/js/modules/web_api.js @@ -1,7 +1,11 @@ +const WebSocket = require('websocket').w3cwebsocket; const fetch = require('node-fetch'); +const ProxyAgent = require('proxy-agent'); const { Agent } = require('https'); const FormData = require('form-data'); +const is = require('@sindresorhus/is'); + /* global Buffer, setTimeout, log, _, lokiFileServerAPI */ /* eslint-disable more/no-then, no-bitwise, no-nested-ternary */ @@ -35,6 +39,89 @@ function _getString(thing) { return thing; } +function _b64ToUint6(nChr) { + return nChr > 64 && nChr < 91 + ? nChr - 65 + : nChr > 96 && nChr < 123 + ? nChr - 71 + : nChr > 47 && nChr < 58 + ? nChr + 4 + : nChr === 43 + ? 62 + : nChr === 47 + ? 63 + : 0; +} + +function _getStringable(thing) { + return ( + typeof thing === 'string' || + typeof thing === 'number' || + typeof thing === 'boolean' || + (thing === Object(thing) && + (_call(thing) === ArrayBufferToString || + _call(thing) === Uint8ArrayToString)) + ); +} + +function _ensureStringed(thing) { + if (_getStringable(thing)) { + return _getString(thing); + } else if (thing instanceof Array) { + const res = []; + for (let i = 0; i < thing.length; i += 1) { + res[i] = _ensureStringed(thing[i]); + } + return res; + } else if (thing === Object(thing)) { + const res = {}; + // eslint-disable-next-line guard-for-in, no-restricted-syntax + for (const key in thing) { + res[key] = _ensureStringed(thing[key]); + } + return res; + } else if (thing === null) { + return null; + } else if (thing === undefined) { + return undefined; + } + throw new Error(`unsure of how to jsonify object of type ${typeof thing}`); +} + +function _jsonThing(thing) { + return JSON.stringify(_ensureStringed(thing)); +} + +function _base64ToBytes(sBase64, nBlocksSize) { + const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ''); + const nInLen = sB64Enc.length; + const nOutLen = nBlocksSize + ? Math.ceil(((nInLen * 3 + 1) >> 2) / nBlocksSize) * nBlocksSize + : (nInLen * 3 + 1) >> 2; + const aBBytes = new ArrayBuffer(nOutLen); + const taBytes = new Uint8Array(aBBytes); + + for ( + let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; + nInIdx < nInLen; + nInIdx += 1 + ) { + nMod4 = nInIdx & 3; + nUint24 |= _b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4); + if (nMod4 === 3 || nInLen - nInIdx === 1) { + for ( + nMod3 = 0; + nMod3 < 3 && nOutIdx < nOutLen; + nMod3 += 1, nOutIdx += 1 + ) { + taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255; + } + nUint24 = 0; + } + } + return aBBytes; +} + function _validateResponse(response, schema) { try { // eslint-disable-next-line guard-for-in, no-restricted-syntax @@ -57,6 +144,30 @@ function _validateResponse(response, schema) { return true; } +function _createSocket(url, { certificateAuthority, proxyUrl, signature }) { + let requestOptions; + if (proxyUrl) { + requestOptions = { + ca: certificateAuthority, + agent: new ProxyAgent(proxyUrl), + }; + } else { + requestOptions = { + ca: certificateAuthority, + }; + } + // TODO: sign a timestamp + let headers; + if (signature) { + headers = { + signature, + }; + } + + // eslint-disable-next-line new-cap + return new WebSocket(url, null, null, headers, requestOptions); +} + const FIVE_MINUTES = 1000 * 60 * 5; const agents = { unauth: null, @@ -99,7 +210,9 @@ function _promiseAjax(providedUrl, options) { log.info(`Cycling agent for type ${cacheKey}`); } agents[cacheKey] = { - agent: new Agent({ keepAlive: true }), + agent: proxyUrl + ? new ProxyAgent(proxyUrl) + : new Agent({ keepAlive: true }), timestamp: Date.now(), }; } @@ -281,12 +394,45 @@ function HTTPError(message, providedCode, response, stack) { return e; } +const URL_CALLS = { + accounts: 'v1/accounts', + updateDeviceName: 'v1/accounts/name', + removeSignalingKey: 'v1/accounts/signaling_key', + attachment: 'v1/attachments', + deliveryCert: 'v1/certificate/delivery', + supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery', + devices: 'v1/devices', + keys: 'v2/keys', + messages: 'v1/messages', + profile: 'v1/profile', + signed: 'v2/keys/signed', +}; + module.exports = { initialize, }; // We first set up the data that won't change during this session of the app -function initialize() { +function initialize({ + url, + cdnUrl, + certificateAuthority, + contentProxyUrl, + proxyUrl, +}) { + if (!is.string(url)) { + throw new Error('WebAPI.initialize: Invalid server url'); + } + if (!is.string(cdnUrl)) { + throw new Error('WebAPI.initialize: Invalid cdnUrl'); + } + if (!is.string(certificateAuthority)) { + throw new Error('WebAPI.initialize: Invalid certificateAuthority'); + } + if (!is.string(contentProxyUrl)) { + throw new Error('WebAPI.initialize: Invalid contentProxyUrl'); + } + // Thanks to function-hoisting, we can put this return statement before all of the // below function definitions. return { @@ -296,19 +442,413 @@ function initialize() { // Then we connect to the server with user-specific information. This is the only API // exposed to the browser context, ensuring that it can't connect to arbitrary // locations. - function connect() { + function connect({ username: initialUsername, password: initialPassword }) { + let username = initialUsername; + let password = initialPassword; + // Thanks, function hoisting! return { + confirmCode, getAttachment, + getAvatar, + getDevices, + getKeysForNumber, + getKeysForNumberUnauth, + getMessageSocket, + getMyKeys, + getProfile, + getProfileUnauth, + getProvisioningSocket, getProxiedSize, + getSenderCertificate, makeProxiedRequest, putAttachment, putAvatar, + registerKeys, + registerSupportForUnauthenticatedDelivery, + removeSignalingKey, + requestVerificationSMS, + requestVerificationVoice, + sendMessages, + sendMessagesUnauth, + setSignedPreKey, + updateDeviceName, }; + function _ajax(param) { + if (!param.urlParameters) { + // eslint-disable-next-line no-param-reassign + param.urlParameters = ''; + } + return _outerAjax(null, { + certificateAuthority, + contentType: 'application/json; charset=utf-8', + data: param.jsonData && _jsonThing(param.jsonData), + host: url, + password, + path: URL_CALLS[param.call] + param.urlParameters, + proxyUrl, + responseType: param.responseType, + timeout: param.timeout, + type: param.httpType, + user: username, + validateResponse: param.validateResponse, + unauthenticated: param.unauthenticated, + accessKey: param.accessKey, + }).catch(e => { + const { code } = e; + if (code === 200) { + // happens sometimes when we get no response + // (TODO: Fix server to return 204? instead) + return null; + } + let message; + switch (code) { + case -1: + message = + 'Failed to connect to the server, please check your network connection.'; + break; + case 413: + message = 'Rate limit exceeded, please try again later.'; + break; + case 403: + message = 'Invalid code, please try again.'; + break; + case 417: + // TODO: This shouldn't be a thing?, but its in the API doc? + message = 'Number already registered.'; + break; + case 401: + message = + 'Invalid authentication, most likely someone re-registered and invalidated our registration.'; + break; + case 404: + message = 'Number is not registered.'; + break; + default: + message = + 'The server rejected our query, please file a bug report.'; + } + e.message = `${message} (original: ${e.message})`; + throw e; + }); + } + + function getSenderCertificate() { + return _ajax({ + call: 'deliveryCert', + httpType: 'GET', + responseType: 'json', + schema: { certificate: 'string' }, + }); + } + + function registerSupportForUnauthenticatedDelivery() { + return _ajax({ + call: 'supportUnauthenticatedDelivery', + httpType: 'PUT', + responseType: 'json', + }); + } + + function getProfile(number) { + return _ajax({ + call: 'profile', + httpType: 'GET', + urlParameters: `/${number}`, + responseType: 'json', + }); + } + function getProfileUnauth(number, { accessKey } = {}) { + return _ajax({ + call: 'profile', + httpType: 'GET', + urlParameters: `/${number}`, + responseType: 'json', + unauthenticated: true, + accessKey, + }); + } + + function getAvatar(path) { + // Using _outerAJAX, since it's not hardcoded to the Signal Server. Unlike our + // attachment CDN, it uses our self-signed certificate, so we pass it in. + return _outerAjax(`${cdnUrl}/${path}`, { + certificateAuthority, + contentType: 'application/octet-stream', + proxyUrl, + responseType: 'arraybuffer', + timeout: 0, + type: 'GET', + }); + } + + function requestVerificationSMS(number) { + return _ajax({ + call: 'accounts', + httpType: 'GET', + urlParameters: `/sms/code/${number}`, + }); + } + + function requestVerificationVoice(number) { + return _ajax({ + call: 'accounts', + httpType: 'GET', + urlParameters: `/voice/code/${number}`, + }); + } + + async function confirmCode( + number, + code, + newPassword, + registrationId, + deviceName, + options = {} + ) { + const { accessKey } = options; + const jsonData = { + supportsSms: false, + fetchesMessages: true, + registrationId, + unidentifiedAccessKey: accessKey + ? _btoa(_getString(accessKey)) + : undefined, + unrestrictedUnidentifiedAccess: false, + }; + + let call; + let urlPrefix; + let schema; + let responseType; + + if (deviceName) { + jsonData.name = deviceName; + call = 'devices'; + urlPrefix = '/'; + schema = { deviceId: 'number' }; + responseType = 'json'; + } else { + call = 'accounts'; + urlPrefix = '/code/'; + } + + // We update our saved username and password, since we're creating a new account + username = number; + password = newPassword; + + const response = await _ajax({ + call, + httpType: 'PUT', + urlParameters: urlPrefix + code, + jsonData, + responseType, + validateResponse: schema, + }); + + // From here on out, our username will be our phone number combined with device + username = `${number}.${response.deviceId || 1}`; + + return response; + } + + function updateDeviceName(deviceName) { + return _ajax({ + call: 'updateDeviceName', + httpType: 'PUT', + jsonData: { + deviceName, + }, + }); + } + + function removeSignalingKey() { + return _ajax({ + call: 'removeSignalingKey', + httpType: 'DELETE', + }); + } + + function getDevices() { + return _ajax({ + call: 'devices', + httpType: 'GET', + }); + } + + function registerKeys(genKeys) { + const keys = {}; + keys.identityKey = _btoa(_getString(genKeys.identityKey)); + keys.signedPreKey = { + keyId: genKeys.signedPreKey.keyId, + publicKey: _btoa(_getString(genKeys.signedPreKey.publicKey)), + signature: _btoa(_getString(genKeys.signedPreKey.signature)), + }; + + keys.preKeys = []; + let j = 0; + // eslint-disable-next-line guard-for-in, no-restricted-syntax + for (const i in genKeys.preKeys) { + keys.preKeys[j] = { + keyId: genKeys.preKeys[i].keyId, + publicKey: _btoa(_getString(genKeys.preKeys[i].publicKey)), + }; + j += 1; + } + + // This is just to make the server happy + // (v2 clients should choke on publicKey) + keys.lastResortKey = { keyId: 0x7fffffff, publicKey: _btoa('42') }; + + return _ajax({ + call: 'keys', + httpType: 'PUT', + jsonData: keys, + }); + } + + function setSignedPreKey(signedPreKey) { + return _ajax({ + call: 'signed', + httpType: 'PUT', + jsonData: { + keyId: signedPreKey.keyId, + publicKey: _btoa(_getString(signedPreKey.publicKey)), + signature: _btoa(_getString(signedPreKey.signature)), + }, + }); + } + + function getMyKeys() { + return _ajax({ + call: 'keys', + httpType: 'GET', + responseType: 'json', + validateResponse: { count: 'number' }, + }).then(res => res.count); + } + + function handleKeys(res) { + if (!Array.isArray(res.devices)) { + throw new Error('Invalid response'); + } + res.identityKey = _base64ToBytes(res.identityKey); + res.devices.forEach(device => { + if ( + !_validateResponse(device, { signedPreKey: 'object' }) || + !_validateResponse(device.signedPreKey, { + publicKey: 'string', + signature: 'string', + }) + ) { + throw new Error('Invalid signedPreKey'); + } + if (device.preKey) { + if ( + !_validateResponse(device, { preKey: 'object' }) || + !_validateResponse(device.preKey, { publicKey: 'string' }) + ) { + throw new Error('Invalid preKey'); + } + // eslint-disable-next-line no-param-reassign + device.preKey.publicKey = _base64ToBytes(device.preKey.publicKey); + } + // eslint-disable-next-line no-param-reassign + device.signedPreKey.publicKey = _base64ToBytes( + device.signedPreKey.publicKey + ); + // eslint-disable-next-line no-param-reassign + device.signedPreKey.signature = _base64ToBytes( + device.signedPreKey.signature + ); + }); + return res; + } + + function getKeysForNumber(number, deviceId = '*') { + return _ajax({ + call: 'keys', + httpType: 'GET', + urlParameters: `/${number}/${deviceId}`, + responseType: 'json', + validateResponse: { identityKey: 'string', devices: 'object' }, + }).then(handleKeys); + } + + function getKeysForNumberUnauth( + number, + deviceId = '*', + { accessKey } = {} + ) { + return _ajax({ + call: 'keys', + httpType: 'GET', + urlParameters: `/${number}/${deviceId}`, + responseType: 'json', + validateResponse: { identityKey: 'string', devices: 'object' }, + unauthenticated: true, + accessKey, + }).then(handleKeys); + } + + function sendMessagesUnauth( + destination, + messageArray, + timestamp, + silent, + online, + { accessKey } = {} + ) { + const jsonData = { messages: messageArray, timestamp }; + + if (silent) { + jsonData.silent = true; + } + if (online) { + jsonData.online = true; + } + + return _ajax({ + call: 'messages', + httpType: 'PUT', + urlParameters: `/${destination}`, + jsonData, + responseType: 'json', + unauthenticated: true, + accessKey, + }); + } + + function sendMessages( + destination, + messageArray, + timestamp, + silent, + online + ) { + const jsonData = { messages: messageArray, timestamp }; + + if (silent) { + jsonData.silent = true; + } + if (online) { + jsonData.online = true; + } + + return _ajax({ + call: 'messages', + httpType: 'PUT', + urlParameters: `/${destination}`, + jsonData, + responseType: 'json', + }); + } + function getAttachment(fileUrl) { return _outerAjax(fileUrl, { contentType: 'application/octet-stream', + proxyUrl, responseType: 'arraybuffer', timeout: 0, type: 'GET', @@ -344,7 +884,7 @@ function initialize() { const result = await _outerAjax(url, { processData: false, responseType: 'arraybufferwithdetails', - proxyUrl: '', + proxyUrl: contentProxyUrl, type: 'HEAD', disableLogs: true, }); @@ -372,12 +912,38 @@ function initialize() { return _outerAjax(url, { processData: false, responseType: returnArrayBuffer ? 'arraybufferwithdetails' : null, - proxyUrl: '', + proxyUrl: contentProxyUrl, type: 'GET', redirect: 'follow', disableLogs: true, headers, }); } + + function getMessageSocket() { + log.info('opening message socket', url); + const fixedScheme = url + .replace('https://', 'wss://') + .replace('http://', 'ws://'); + const login = encodeURIComponent(username); + const pass = encodeURIComponent(password); + + return _createSocket( + `${fixedScheme}/v1/websocket/?login=${login}&password=${pass}&agent=OWD`, + { certificateAuthority, proxyUrl } + ); + } + + function getProvisioningSocket() { + log.info('opening provisioning socket', url); + const fixedScheme = url + .replace('https://', 'wss://') + .replace('http://', 'ws://'); + + return _createSocket( + `${fixedScheme}/v1/websocket/provisioning/?agent=OWD`, + { certificateAuthority, proxyUrl } + ); + } } } diff --git a/js/spell_check.js b/js/spell_check.js new file mode 100644 index 000000000..96ea34e00 --- /dev/null +++ b/js/spell_check.js @@ -0,0 +1,180 @@ +/* global require, process, _ */ + +/* eslint-disable strict */ + +const electron = require('electron'); + +const osLocale = require('os-locale'); +const os = require('os'); +const semver = require('semver'); +const spellchecker = require('spellchecker'); + +const { remote, webFrame } = electron; + +// `remote.require` since `Menu` is a main-process module. +const buildEditorContextMenu = remote.require('electron-editor-context-menu'); + +const EN_VARIANT = /^en/; + +// Prevent the spellchecker from showing contractions as errors. +const ENGLISH_SKIP_WORDS = [ + 'ain', + 'couldn', + 'didn', + 'doesn', + 'hadn', + 'hasn', + 'mightn', + 'mustn', + 'needn', + 'oughtn', + 'shan', + 'shouldn', + 'wasn', + 'weren', + 'wouldn', +]; + +function setupLinux(locale) { + if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { + // apt-get install hunspell- can be run for easy access + // to other dictionaries + const location = process.env.HUNSPELL_DICTIONARIES || '/usr/share/hunspell'; + + window.log.info( + 'Detected Linux. Setting up spell check with locale', + locale, + 'and dictionary location', + location + ); + spellchecker.setDictionary(locale, location); + } else { + window.log.info( + 'Detected Linux. Using default en_US spell check dictionary' + ); + } +} + +function setupWin7AndEarlier(locale) { + if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { + const location = process.env.HUNSPELL_DICTIONARIES; + + window.log.info( + 'Detected Windows 7 or below. Setting up spell-check with locale', + locale, + 'and dictionary location', + location + ); + spellchecker.setDictionary(locale, location); + } else { + window.log.info( + 'Detected Windows 7 or below. Using default en_US spell check dictionary' + ); + } +} + +// We load locale this way and not via app.getLocale() because this call returns +// 'es_ES' and not just 'es.' And hunspell requires the fully-qualified locale. +const locale = osLocale.sync().replace('-', '_'); + +// The LANG environment variable is how node spellchecker finds its default language: +// https://github.com/atom/node-spellchecker/blob/59d2d5eee5785c4b34e9669cd5d987181d17c098/lib/spellchecker.js#L29 +if (!process.env.LANG) { + process.env.LANG = locale; +} + +if (process.platform === 'linux') { + setupLinux(locale); +} else if (process.platform === 'windows' && semver.lt(os.release(), '8.0.0')) { + setupWin7AndEarlier(locale); +} else { + // OSX and Windows 8+ have OS-level spellcheck APIs + window.log.info( + 'Using OS-level spell check API with locale', + process.env.LANG + ); +} + +const simpleChecker = { + spellCheck(text) { + return !this.isMisspelled(text); + }, + isMisspelled(text) { + const misspelled = spellchecker.isMisspelled(text); + + // The idea is to make this as fast as possible. For the many, many calls which + // don't result in the red squiggly, we minimize the number of checks. + if (!misspelled) { + return false; + } + + // Only if we think we've found an error do we check the locale and skip list. + if (locale.match(EN_VARIANT) && _.contains(ENGLISH_SKIP_WORDS, text)) { + return false; + } + + return true; + }, + getSuggestions(text) { + return spellchecker.getCorrectionsForMisspelling(text); + }, + add(text) { + spellchecker.add(text); + }, +}; + +const dummyChecker = { + spellCheck() { + return true; + }, + isMisspelled() { + return false; + }, + getSuggestions() { + return []; + }, + add() { + // nothing + }, +}; + +window.spellChecker = simpleChecker; +window.disableSpellCheck = () => { + window.removeEventListener('contextmenu', spellCheckHandler); + webFrame.setSpellCheckProvider('en-US', false, dummyChecker); +}; + +window.enableSpellCheck = () => { + webFrame.setSpellCheckProvider( + 'en-US', + // Not sure what this parameter (`autoCorrectWord`) does: https://github.com/atom/electron/issues/4371 + // The documentation for `webFrame.setSpellCheckProvider` passes `true` so we do too. + true, + simpleChecker + ); + window.addEventListener('contextmenu', spellCheckHandler); +}; + +const spellCheckHandler = e => { + // Only show the context menu in text editors. + if (!e.target.closest('textarea, input, [contenteditable="true"]')) { + return; + } + + const selectedText = window.getSelection().toString(); + const isMisspelled = selectedText && simpleChecker.isMisspelled(selectedText); + const spellingSuggestions = + isMisspelled && simpleChecker.getSuggestions(selectedText).slice(0, 5); + const menu = buildEditorContextMenu({ + isMisspelled, + spellingSuggestions, + }); + + // The 'contextmenu' event is emitted after 'selectionchange' has fired + // but possibly before the visible selection has changed. Try to wait + // to show the menu until after that, otherwise the visible selection + // will update after the menu dismisses and look weird. + setTimeout(() => { + menu.popup(remote.getCurrentWindow()); + }, 30); +}; diff --git a/js/views/create_group_dialog_view.js b/js/views/create_group_dialog_view.js index a2bc8d78e..8a5cb9e30 100644 --- a/js/views/create_group_dialog_view.js +++ b/js/views/create_group_dialog_view.js @@ -165,9 +165,9 @@ props: { titleText: this.titleText, groupName: this.groupName, - okText: i18n('ok'), - cancelText: i18n('cancel'), + okText: this.okText, isPublic: this.isPublic, + cancelText: this.cancelText, existingMembers: this.existingMembers, friendList: this.friendsAndMembers, isAdmin: this.isAdmin, diff --git a/js/views/phone-input-view.js b/js/views/phone-input-view.js new file mode 100644 index 000000000..e9537f901 --- /dev/null +++ b/js/views/phone-input-view.js @@ -0,0 +1,39 @@ +/* global libphonenumber, Whisper */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.PhoneInputView = Whisper.View.extend({ + tagName: 'div', + className: 'phone-input', + templateName: 'phone-number', + initialize() { + this.$('input.number').intlTelInput(); + }, + events: { + change: 'validateNumber', + keyup: 'validateNumber', + }, + validateNumber() { + const input = this.$('input.number'); + const regionCode = this.$('li.active') + .attr('data-country-code') + .toUpperCase(); + const number = input.val(); + + const parsedNumber = libphonenumber.util.parseNumber(number, regionCode); + if (parsedNumber.isValidNumber) { + this.$('.number-container').removeClass('invalid'); + this.$('.number-container').addClass('valid'); + } else { + this.$('.number-container').removeClass('valid'); + } + input.trigger('validation'); + + return parsedNumber.e164; + }, + }); +})(); diff --git a/js/views/standalone_registration_view.js b/js/views/standalone_registration_view.js new file mode 100644 index 000000000..937738dfb --- /dev/null +++ b/js/views/standalone_registration_view.js @@ -0,0 +1,490 @@ +/* global + Whisper, + $, + getAccountManager, + textsecure, + i18n, + passwordUtil, + _, + setTimeout, + displayNameRegex +*/ + +/* eslint-disable more/no-then */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + const REGISTER_INDEX = 0; + const PROFILE_INDEX = 1; + let currentPageIndex = REGISTER_INDEX; + + Whisper.StandaloneRegistrationView = Whisper.View.extend({ + templateName: 'standalone', + className: 'full-screen-flow standalone-fullscreen', + initialize() { + this.accountManager = getAccountManager(); + // Clean status in case the app closed unexpectedly + textsecure.storage.remove('secondaryDeviceStatus'); + + this.render(); + + const number = textsecure.storage.user.getNumber(); + if (number) { + this.$('input.number').val(number); + } + this.phoneView = new Whisper.PhoneInputView({ + el: this.$('#phone-number-input'), + }); + this.$('#error').hide(); + + this.$('.standalone-mnemonic').hide(); + this.$('.standalone-secondary-device').hide(); + + this.onGenerateMnemonic(); + + const options = window.mnemonic.get_languages().map(language => { + const text = language + // Split by whitespace or underscore + .split(/[\s_]+/) + // Capitalise each word + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + return ``; + }); + this.$('#mnemonic-language').append(options); + this.$('#mnemonic-language').val('english'); + this.$('#mnemonic-display-language').append(options); + this.$('#mnemonic-display-language').val('english'); + + this.$passwordInput = this.$('#password'); + this.$passwordConfirmationInput = this.$('#password-confirmation'); + this.$passwordInputError = this.$('.password-inputs .error'); + + this.registrationParams = {}; + this.$pages = this.$('.page'); + this.pairingInterval = null; + this.showRegisterPage(); + + this.onValidatePassword(); + + this.onSecondaryDeviceRegistered = this.onSecondaryDeviceRegistered.bind( + this + ); + + this.$('#display-name').get(0).oninput = () => { + this.sanitiseNameInput(); + }; + + this.$('#display-name').get(0).onpaste = () => { + // Sanitise data immediately after paste because it's easier + setTimeout(() => { + this.sanitiseNameInput(); + }); + }; + this.sanitiseNameInput(); + }, + events: { + keyup: 'onKeyup', + 'validation input.number': 'onValidation', + 'click #request-voice': 'requestVoice', + 'click #request-sms': 'requestSMSVerification', + 'change #code': 'onChangeCode', + 'click #register': 'registerWithoutMnemonic', + 'click #register-mnemonic': 'registerWithMnemonic', + 'click #register-secondary-device': 'registerSecondaryDevice', + 'click #cancel-secondary-device': 'cancelSecondaryDevice', + 'click #back-button': 'onBack', + 'click #save-button': 'onSaveProfile', + 'change #mnemonic': 'onChangeMnemonic', + 'click #generate-mnemonic': 'onGenerateMnemonic', + 'change #mnemonic-display-language': 'onGenerateMnemonic', + 'click #copy-mnemonic': 'onCopyMnemonic', + 'click .section-toggle': 'toggleSection', + 'keyup #password': 'onValidatePassword', + 'keyup #password-confirmation': 'onValidatePassword', + }, + sanitiseNameInput() { + const oldVal = this.$('#display-name').val(); + const newVal = oldVal.replace(displayNameRegex, ''); + this.$('#display-name').val(newVal); + if (_.isEmpty(newVal)) { + this.$('#save-button').attr('disabled', 'disabled'); + return false; + } + this.$('#save-button').removeAttr('disabled'); + return true; + }, + async showPage(pageIndex) { + // eslint-disable-next-line func-names + this.$pages.each(function(index) { + if (index !== pageIndex) { + $(this).hide(); + } else { + $(this).show(); + currentPageIndex = pageIndex; + } + }); + }, + async showRegisterPage() { + this.registrationParams = {}; + this.showPage(REGISTER_INDEX); + }, + async showProfilePage(mnemonic, language) { + this.registrationParams = { + mnemonic, + language, + }; + this.$passwordInput.val(''); + this.$passwordConfirmationInput.val(''); + this.onValidatePassword(); + this.showPage(PROFILE_INDEX); + this.$('#display-name').focus(); + }, + onKeyup(event) { + if ( + currentPageIndex !== PROFILE_INDEX && + currentPageIndex !== REGISTER_INDEX + ) { + // Only want enter/escape keys to work on profile page + return; + } + + const validName = this.sanitiseNameInput(); + switch (event.key) { + case 'Enter': + if (event.target.id === 'mnemonic') { + this.registerWithMnemonic(); + } else if (event.target.id === 'primary-pubkey') { + this.registerSecondaryDevice(); + } else if (validName) { + this.onSaveProfile(); + } + break; + case 'Escape': + case 'Esc': + this.onBack(); + break; + default: + } + }, + async register(mnemonic, language) { + // Make sure the password is valid + if (this.validatePassword()) { + window.pushToast({ + title: i18n('invalidPassword'), + type: 'info', + }); + return; + } + + const input = this.trim(this.$passwordInput.val()); + + // Ensure we clear the secondary device registration status + textsecure.storage.remove('secondaryDeviceStatus'); + + try { + await this.resetRegistration(); + + await window.setPassword(input); + await this.accountManager.registerSingleDevice( + mnemonic, + language, + this.trim(this.$('#display-name').val()) + ); + this.$el.trigger('openInbox'); + } catch (e) { + if (typeof e === 'string') { + window.pushToast({ + title: e, + type: 'info', + }); + } + this.log(e); + } + }, + registerWithoutMnemonic() { + const mnemonic = this.$('#mnemonic-display').text(); + const language = this.$('#mnemonic-display-language').val(); + this.showProfilePage(mnemonic, language); + }, + async onSecondaryDeviceRegistered() { + clearInterval(this.pairingInterval); + // Ensure the left menu is updated + Whisper.events.trigger('userChanged', { isSecondaryDevice: true }); + // will re-run the background initialisation + Whisper.events.trigger('registration_done'); + this.$el.trigger('openInbox'); + }, + async resetRegistration() { + await window.Signal.Data.removeAllIdentityKeys(); + await window.Signal.Data.removeAllPrivateConversations(); + Whisper.Registration.remove(); + // Do not remove all items since they are only set + // at startup. + textsecure.storage.remove('identityKey'); + textsecure.storage.remove('secondaryDeviceStatus'); + window.ConversationController.reset(); + await window.ConversationController.load(); + Whisper.RotateSignedPreKeyListener.stop(Whisper.events); + }, + async cancelSecondaryDevice() { + Whisper.events.off( + 'secondaryDeviceRegistration', + this.onSecondaryDeviceRegistered + ); + this.$('#register-secondary-device') + .removeAttr('disabled') + .text('Link'); + this.$('#cancel-secondary-device').hide(); + this.$('.standalone-secondary-device #pubkey').text(''); + await this.resetRegistration(); + }, + async registerSecondaryDevice() { + if (textsecure.storage.get('secondaryDeviceStatus') === 'ongoing') { + return; + } + await this.resetRegistration(); + textsecure.storage.put('secondaryDeviceStatus', 'ongoing'); + this.$('#register-secondary-device') + .attr('disabled', 'disabled') + .text('Sending...'); + this.$('#cancel-secondary-device').show(); + const mnemonic = this.$('#mnemonic-display').text(); + const language = this.$('#mnemonic-display-language').val(); + const primaryPubKey = this.$('#primary-pubkey').val(); + this.$('.standalone-secondary-device #error').hide(); + // Ensure only one listener + Whisper.events.off( + 'secondaryDeviceRegistration', + this.onSecondaryDeviceRegistered + ); + Whisper.events.once( + 'secondaryDeviceRegistration', + this.onSecondaryDeviceRegistered + ); + const onError = async error => { + this.$('.standalone-secondary-device #error') + .text(error) + .show(); + await this.resetRegistration(); + this.$('#register-secondary-device') + .removeAttr('disabled') + .text('Link'); + this.$('#cancel-secondary-device').hide(); + }; + const c = new Whisper.Conversation({ + id: primaryPubKey, + type: 'private', + }); + const validationError = c.validateNumber(); + if (validationError) { + onError('Invalid public key'); + return; + } + try { + await this.accountManager.registerSingleDevice( + mnemonic, + language, + null + ); + await this.accountManager.requestPairing(primaryPubKey); + const pubkey = textsecure.storage.user.getNumber(); + const words = window.mnemonic.pubkey_to_secret_words(pubkey); + + this.$('.standalone-secondary-device #pubkey').text( + `Here is your secret:\n${words}` + ); + } catch (e) { + onError(e); + } + }, + registerWithMnemonic() { + const mnemonic = this.$('#mnemonic').val(); + const language = this.$('#mnemonic-language').val(); + try { + window.mnemonic.mn_decode(mnemonic, language); + } catch (error) { + this.$('#mnemonic').addClass('error-input'); + this.$('#error').text(error); + this.$('#error').show(); + return; + } + this.$('#error').hide(); + this.$('#mnemonic').removeClass('error-input'); + if (!mnemonic) { + this.log('Please provide a mnemonic word list'); + } else { + this.showProfilePage(mnemonic, language); + } + }, + onSaveProfile() { + if (_.isEmpty(this.registrationParams)) { + this.onBack(); + return; + } + + const { mnemonic, language } = this.registrationParams; + this.register(mnemonic, language); + }, + onBack() { + this.showRegisterPage(); + }, + onChangeMnemonic() { + this.$('#status').html(''); + }, + async onGenerateMnemonic() { + const language = this.$('#mnemonic-display-language').val(); + const mnemonic = await this.accountManager.generateMnemonic(language); + this.$('#mnemonic-display').text(mnemonic); + }, + onCopyMnemonic() { + window.clipboard.writeText(this.$('#mnemonic-display').text()); + window.pushToast({ + title: i18n('copiedMnemonic'), + type: 'info', + }); + }, + log(s) { + window.log.info(s); + this.$('#status').text(s); + }, + displayError(error) { + this.$('#error') + .hide() + .text(error) + .addClass('in') + .fadeIn(); + }, + onValidation() { + if (this.$('#number-container').hasClass('valid')) { + this.$('#request-sms, #request-voice').removeAttr('disabled'); + } else { + this.$('#request-sms, #request-voice').prop('disabled', 'disabled'); + } + }, + onChangeCode() { + if (!this.validateCode()) { + this.$('#code').addClass('invalid'); + } else { + this.$('#code').removeClass('invalid'); + } + }, + requestVoice() { + window.removeSetupMenuItems(); + this.$('#error').hide(); + const number = this.phoneView.validateNumber(); + if (number) { + this.accountManager + .requestVoiceVerification(number) + .catch(this.displayError.bind(this)); + this.$('#step2') + .addClass('in') + .fadeIn(); + } else { + this.$('#number-container').addClass('invalid'); + } + }, + requestSMSVerification() { + window.removeSetupMenuItems(); + $('#error').hide(); + const number = this.phoneView.validateNumber(); + if (number) { + this.accountManager + .requestSMSVerification(number) + .catch(this.displayError.bind(this)); + this.$('#step2') + .addClass('in') + .fadeIn(); + } else { + this.$('#number-container').addClass('invalid'); + } + }, + + toggleSection(e) { + function focusInput() { + const inputs = $(this).find('input'); + if ($(this).is(':visible')) { + if (inputs[0]) { + inputs[0].focus(); + } + } + } + // Expand or collapse this panel + const $target = this.$(e.currentTarget); + const $next = $target.next(); + + // Toggle section visibility + $next.slideToggle('fast', focusInput); + $target.toggleClass('section-toggle-visible'); + + // Hide the other sections + this.$('.section-toggle') + .not($target) + .removeClass('section-toggle-visible'); + this.$('.section-content') + .not($next) + .slideUp('fast'); + }, + validatePassword() { + const input = this.trim(this.$passwordInput.val()); + const confirmationInput = this.trim( + this.$passwordConfirmationInput.val() + ); + + // If user hasn't set a value then skip + if (!input && !confirmationInput) { + return null; + } + + const error = passwordUtil.validatePassword(input, i18n); + if (error) { + return error; + } + + if (input !== confirmationInput) { + return "Password don't match"; + } + + return null; + }, + onValidatePassword() { + const passwordValidation = this.validatePassword(); + if (passwordValidation) { + this.$passwordInput.addClass('error-input'); + this.$passwordConfirmationInput.addClass('error-input'); + + this.$passwordInput.removeClass('match-input'); + this.$passwordConfirmationInput.removeClass('match-input'); + + this.$passwordInputError.text(passwordValidation); + this.$passwordInputError.show(); + } else { + this.$passwordInput.removeClass('error-input'); + this.$passwordConfirmationInput.removeClass('error-input'); + + this.$passwordInputError.text(''); + this.$passwordInputError.hide(); + + // Show green box around inputs that match + const input = this.trim(this.$passwordInput.val()); + const confirmationInput = this.trim( + this.$passwordConfirmationInput.val() + ); + if (input && input === confirmationInput) { + this.$passwordInput.addClass('match-input'); + this.$passwordConfirmationInput.addClass('match-input'); + } else { + this.$passwordInput.removeClass('match-input'); + this.$passwordConfirmationInput.removeClass('match-input'); + } + } + }, + trim(value) { + return value ? value.trim() : value; + }, + }); +})(); diff --git a/libloki/crypto.js b/libloki/crypto.js index 23a7f68c7..99f2e1a4d 100644 --- a/libloki/crypto.js +++ b/libloki/crypto.js @@ -17,7 +17,6 @@ class FallBackDecryptionError extends Error {} const IV_LENGTH = 16; - const NONCE_LENGTH = 12; async function DHEncrypt(symmetricKey, plainText) { const iv = libsignal.crypto.getRandomBytes(IV_LENGTH); @@ -34,52 +33,6 @@ return ivAndCiphertext; } - async function EncryptGCM(symmetricKey, plaintext) { - const nonce = crypto.getRandomValues(new Uint8Array(NONCE_LENGTH)); - - const key = await crypto.subtle.importKey( - 'raw', - symmetricKey, - { name: 'AES-GCM' }, - false, - ['encrypt'] - ); - - const ciphertext = await crypto.subtle.encrypt( - { name: 'AES-GCM', iv: nonce, tagLength: 128 }, - key, - plaintext - ); - - const ivAndCiphertext = new Uint8Array( - NONCE_LENGTH + ciphertext.byteLength - ); - - ivAndCiphertext.set(nonce); - ivAndCiphertext.set(new Uint8Array(ciphertext), nonce.byteLength); - - return ivAndCiphertext; - } - - async function DecryptGCM(symmetricKey, ivAndCiphertext) { - const nonce = ivAndCiphertext.slice(0, NONCE_LENGTH); - const ciphertext = ivAndCiphertext.slice(NONCE_LENGTH); - - const key = await crypto.subtle.importKey( - 'raw', - symmetricKey, - { name: 'AES-GCM' }, - false, - ['decrypt'] - ); - - return crypto.subtle.decrypt( - { name: 'AES-GCM', iv: nonce }, - key, - ciphertext - ); - } - async function DHDecrypt(symmetricKey, ivAndCiphertext) { const iv = ivAndCiphertext.slice(0, IV_LENGTH); const ciphertext = ivAndCiphertext.slice(IV_LENGTH); @@ -136,6 +89,15 @@ const base32zIndex = Multibase.names.indexOf('base32z'); const base32zCode = Multibase.codes[base32zIndex]; + function bufferToArrayBuffer(buf) { + const ab = new ArrayBuffer(buf.length); + const view = new Uint8Array(ab); + for (let i = 0; i < buf.length; i += 1) { + view[i] = buf[i]; + } + return ab; + } + function decodeSnodeAddressToPubKey(snodeAddress) { const snodeAddressClean = snodeAddress .replace('.snode', '') @@ -144,11 +106,64 @@ return Multibase.decode(`${base32zCode}${snodeAddressClean}`); } - function generateEphemeralKeyPair() { - const keys = libsignal.Curve.generateKeyPair(); - // Signal protocol prepends with "0x05" - keys.pubKey = keys.pubKey.slice(1); - return keys; + class LokiSnodeChannel { + constructor() { + this._ephemeralKeyPair = libsignal.Curve.generateKeyPair(); + // Signal protocol prepends with "0x05" + this._ephemeralKeyPair.pubKey = this._ephemeralKeyPair.pubKey.slice(1); + this._ephemeralPubKeyHex = StringView.arrayBufferToHex( + this._ephemeralKeyPair.pubKey + ); + this._cache = {}; + } + + async _getSymmetricKey(snodeAddress) { + if (snodeAddress in this._cache) { + return this._cache[snodeAddress]; + } + const ed25519PubKey = decodeSnodeAddressToPubKey(snodeAddress); + const sodium = await window.getSodium(); + const curve25519PubKey = sodium.crypto_sign_ed25519_pk_to_curve25519( + ed25519PubKey + ); + const snodePubKeyArrayBuffer = bufferToArrayBuffer(curve25519PubKey); + const symmetricKey = libsignal.Curve.calculateAgreement( + snodePubKeyArrayBuffer, + this._ephemeralKeyPair.privKey + ); + this._cache[snodeAddress] = symmetricKey; + return symmetricKey; + } + + getChannelPublicKeyHex() { + return this._ephemeralPubKeyHex; + } + + async decrypt(snodeAddress, ivAndCiphertextBase64) { + const ivAndCiphertext = dcodeIO.ByteBuffer.wrap( + ivAndCiphertextBase64, + 'base64' + ).toArrayBuffer(); + const symmetricKey = await this._getSymmetricKey(snodeAddress); + try { + const decrypted = await DHDecrypt(symmetricKey, ivAndCiphertext); + const decoder = new TextDecoder(); + return decoder.decode(decrypted); + } catch (e) { + return ivAndCiphertext; + } + } + + async encrypt(snodeAddress, plainText) { + if (typeof plainText === 'string') { + const textEncoder = new TextEncoder(); + // eslint-disable-next-line no-param-reassign + plainText = textEncoder.encode(plainText); + } + const symmetricKey = await this._getSymmetricKey(snodeAddress); + const ciphertext = await DHEncrypt(symmetricKey, plainText); + return dcodeIO.ByteBuffer.wrap(ciphertext).toString('base64'); + } } async function generateSignatureForPairing(secondaryPubKey, type) { @@ -300,6 +315,7 @@ const tokenString = dcodeIO.ByteBuffer.wrap(token).toString('utf8'); return tokenString; } + const snodeCipher = new LokiSnodeChannel(); const sha512 = data => crypto.subtle.digest('SHA-512', data); @@ -458,11 +474,10 @@ window.libloki.crypto = { DHEncrypt, - EncryptGCM, // AES-GCM DHDecrypt, - DecryptGCM, // AES-GCM FallBackSessionCipher, FallBackDecryptionError, + snodeCipher, decryptToken, generateSignatureForPairing, verifyPairingSignature, @@ -470,7 +485,8 @@ validateAuthorisation, PairingType, LokiSessionCipher, - generateEphemeralKeyPair, + // for testing + _LokiSnodeChannel: LokiSnodeChannel, _decodeSnodeAddressToPubKey: decodeSnodeAddressToPubKey, sha512, }; diff --git a/libloki/storage.js b/libloki/storage.js index 9f057d17e..ff52e52c5 100644 --- a/libloki/storage.js +++ b/libloki/storage.js @@ -240,14 +240,6 @@ return window.Signal.Data.getSecondaryDevicesFor(primaryDevicePubKey); } - function getGuardNodes() { - return window.Signal.Data.getGuardNodes(); - } - - function updateGuardNodes(nodes) { - return window.Signal.Data.updateGuardNodes(nodes); - } - async function getAllDevicePubKeysForPrimaryPubKey(primaryDevicePubKey) { await saveAllPairingAuthorisationsFor(primaryDevicePubKey); const secondaryPubKeys = @@ -273,8 +265,6 @@ getAllDevicePubKeysForPrimaryPubKey, getSecondaryDevicesFor, getPrimaryDeviceMapping, - getGuardNodes, - updateGuardNodes, }; // Libloki protocol store diff --git a/libloki/test/index.html b/libloki/test/index.html index f9eb9c2e4..4d1c2a5e7 100644 --- a/libloki/test/index.html +++ b/libloki/test/index.html @@ -33,6 +33,7 @@ + diff --git a/libloki/test/snode_channel_test.js b/libloki/test/snode_channel_test.js new file mode 100644 index 000000000..cb535c73e --- /dev/null +++ b/libloki/test/snode_channel_test.js @@ -0,0 +1,141 @@ +/* global libloki, Multibase, libsignal, StringView, dcodeIO */ + +'use strict'; + +async function generateSnodeKeysAndAddress() { + // snode identitys is a ed25519 keypair + const sodium = await window.getSodium(); + const ed25519KeyPair = sodium.crypto_sign_keypair(); + const keyPair = { + pubKey: ed25519KeyPair.publicKey, + privKey: ed25519KeyPair.privateKey, + }; + // snode address is the pubkey in base32z + let address = Multibase.encode( + 'base32z', + Multibase.Buffer.from(keyPair.pubKey) + ).toString(); + // remove first letter, which is the encoding code + address = address.substring(1); + return { keyPair, address }; +} + +describe('Snode Channel', () => { + describe('snodeCipher singleton', () => { + it('should be defined at libloki.crypto', () => { + assert.isDefined(libloki.crypto.snodeCipher); + assert.isTrue( + libloki.crypto.snodeCipher instanceof libloki.crypto._LokiSnodeChannel + ); + }); + }); + + describe('#decodeSnodeAddressToPubKey', () => { + it('should decode a base32z encoded .snode address', async () => { + const { keyPair, address } = await generateSnodeKeysAndAddress(); + + const buffer = libloki.crypto._decodeSnodeAddressToPubKey( + `http://${address}.snode` + ); + + const expected = new Uint8Array(keyPair.pubKey); + assert.strictEqual(expected.length, 32); + assert.strictEqual(buffer.length, 32); + for (let i = 0; i < buffer.length; i += 1) { + assert.strictEqual(buffer[i], expected[i]); + } + }); + }); + + describe('#LokiSnodeChannel', () => { + it('should generate an ephemeral key pair', () => { + const channel = new libloki.crypto._LokiSnodeChannel(); + + assert.isDefined(channel._ephemeralKeyPair); + assert.isTrue(channel._ephemeralKeyPair.privKey instanceof ArrayBuffer); + assert.isTrue(channel._ephemeralKeyPair.pubKey instanceof ArrayBuffer); + const pubKeyHex = StringView.arrayBufferToHex( + channel._ephemeralKeyPair.pubKey + ); + assert.strictEqual(channel.getChannelPublicKeyHex(), pubKeyHex); + }); + + it('should cache something by snode address', async () => { + const { address } = await generateSnodeKeysAndAddress(); + + const channel = new libloki.crypto._LokiSnodeChannel(); + // cache should be empty + assert.strictEqual(Object.keys(channel._cache).length, 0); + + // push to cache + await channel._getSymmetricKey(address); + + assert.strictEqual(Object.keys(channel._cache).length, 1); + assert.strictEqual(Object.keys(channel._cache)[0], address); + }); + + it('should encrypt data correctly', async () => { + // message sent by Session + const snode = await generateSnodeKeysAndAddress(); + const messageSent = 'I am Groot'; + const textEncoder = new TextEncoder(); + const data = textEncoder.encode(messageSent); + + const channel = new libloki.crypto._LokiSnodeChannel(); + const encrypted = await channel.encrypt(snode.address, data); + + assert.strictEqual(typeof encrypted, 'string'); + + // message received by storage server + const senderPubKey = StringView.hexToArrayBuffer( + channel.getChannelPublicKeyHex() + ); + const sodium = await window.getSodium(); + const snodePrivKey = sodium.crypto_sign_ed25519_sk_to_curve25519( + snode.keyPair.privKey + ).buffer; + const symmetricKey = libsignal.Curve.calculateAgreement( + senderPubKey, + snodePrivKey + ); + const encryptedArrayBuffer = dcodeIO.ByteBuffer.wrap( + encrypted, + 'base64' + ).toArrayBuffer(); + const decrypted = await libloki.crypto.DHDecrypt( + symmetricKey, + encryptedArrayBuffer + ); + const textDecoder = new TextDecoder(); + const messageReceived = textDecoder.decode(decrypted); + assert.strictEqual(messageSent, messageReceived); + }); + + it('should decrypt data correctly', async () => { + const channel = new libloki.crypto._LokiSnodeChannel(); + // message sent by storage server + const snode = await generateSnodeKeysAndAddress(); + const messageSent = 'You are Groot'; + const textEncoder = new TextEncoder(); + const data = textEncoder.encode(messageSent); + const senderPubKey = StringView.hexToArrayBuffer( + channel.getChannelPublicKeyHex() + ); + const sodium = await window.getSodium(); + const snodePrivKey = sodium.crypto_sign_ed25519_sk_to_curve25519( + snode.keyPair.privKey + ).buffer; + const symmetricKey = libsignal.Curve.calculateAgreement( + senderPubKey, + snodePrivKey + ); + const encrypted = await libloki.crypto.DHEncrypt(symmetricKey, data); + const encryptedBase64 = dcodeIO.ByteBuffer.wrap(encrypted).toString( + 'base64' + ); + // message received by Session + const decrypted = await channel.decrypt(snode.address, encryptedBase64); + assert.strictEqual(messageSent, decrypted); + }); + }); +}); diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index e037305c5..1e1764ac2 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -28,6 +28,7 @@ const ARCHIVE_AGE = 7 * 24 * 60 * 60 * 1000; function AccountManager(username, password) { + // this.server = window.WebAPI.connect({ username, password }); this.pending = Promise.resolve(); } @@ -47,8 +48,12 @@ AccountManager.prototype = new textsecure.EventTarget(); AccountManager.prototype.extend({ constructor: AccountManager, - requestVoiceVerification(number) {}, - requestSMSVerification(number) {}, + requestVoiceVerification(number) { + // return this.server.requestVerificationVoice(number); + }, + requestSMSVerification(number) { + // return this.server.requestVerificationSMS(number); + }, async encryptDeviceName(name, providedIdentityKey) { if (!name) { return null; @@ -93,13 +98,23 @@ return name; }, async maybeUpdateDeviceName() { - throw new Error('Signal method called: maybeUpdateDeviceName'); + const isNameEncrypted = textsecure.storage.user.getDeviceNameEncrypted(); + if (isNameEncrypted) { + return; + } + const deviceName = await textsecure.storage.user.getDeviceName(); + const base64 = await this.encryptDeviceName(deviceName); + + await this.server.updateDeviceName(base64); }, async deviceNameIsEncrypted() { await textsecure.storage.user.setDeviceNameEncrypted(); }, async maybeDeleteSignalingKey() { - throw new Error('Signal method called: maybeDeleteSignalingKey'); + const key = await textsecure.storage.user.getSignalingKey(); + if (key) { + await this.server.removeSignalingKey(); + } }, registerSingleDevice(mnemonic, mnemonicLanguage, profileName) { const createAccount = this.createAccount.bind(this); @@ -197,7 +212,19 @@ 'account_manager: registerSecondDevice has not been implemented!' ); }, - refreshPreKeys() {}, + refreshPreKeys() { + // const generateKeys = this.generateKeys.bind(this, 0); + // const registerKeys = this.server.registerKeys.bind(this.server); + // return this.queueTask(() => + // this.server.getMyKeys().then(preKeyCount => { + // window.log.info(`prekey count ${preKeyCount}`); + // if (preKeyCount < 10) { + // return generateKeys().then(registerKeys); + // } + // return null; + // }) + // ); + }, rotateSignedPreKey() { return this.queueTask(() => { const signedKeyId = textsecure.storage.get('signedKeyId', 1); @@ -485,6 +512,7 @@ keyId: res.keyId, publicKey: res.keyPair.pubKey, signature: res.signature, + // server.registerKeys doesn't use keyPair, confirmKeys does keyPair: res.keyPair, }; }) @@ -590,27 +618,11 @@ }; // Update authorisation in database with the new grant signature await libloki.storage.savePairingAuthorisation(authorisation); - - // Try to upload to the file server and then send a message - try { - await lokiFileServerAPI.updateOurDeviceMapping(); - await libloki.api.sendPairingAuthorisation( - authorisation, - secondaryDevicePubKey - ); - } catch (e) { - log.error( - 'Failed to authorise secondary device: ', - e && e.stack ? e.stack : e - ); - // File server upload failed or message sending failed, we should rollback changes - await libloki.storage.removePairingAuthorisationForSecondaryPubKey( - secondaryDevicePubKey - ); - await lokiFileServerAPI.updateOurDeviceMapping(); - throw e; - } - + await lokiFileServerAPI.updateOurDeviceMapping(); + await libloki.api.sendPairingAuthorisation( + authorisation, + secondaryDevicePubKey + ); // Always be friends with secondary devices await secondaryConversation.setFriendRequestStatus( window.friends.friendRequestStatusEnum.friends, diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 94b14fc7b..b6b24f355 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -11,6 +11,7 @@ /* global HttpResource: false */ /* global ContactBuffer: false */ /* global GroupBuffer: false */ +/* global WebSocketResource: false */ /* global lokiPublicChatAPI: false */ /* global lokiMessageAPI: false */ /* global feeds: false */ @@ -31,7 +32,14 @@ function MessageReceiver(username, password, signalingKey, options = {}) { this.signalingKey = signalingKey; this.username = username; this.password = password; - this.server = WebAPI.connect(); + this.server = WebAPI.connect({ username, password }); + + if (!options.serverTrustRoot) { + throw new Error('Server trust root is required!'); + } + this.serverTrustRoot = window.Signal.Crypto.base64ToArrayBuffer( + options.serverTrustRoot + ); const address = libsignal.SignalProtocolAddress.fromString(username); this.number = address.getName(); @@ -102,6 +110,31 @@ MessageReceiver.prototype.extend({ feed.on('rssMessage', this.handleUnencryptedMessage.bind(this)); }); + // TODO: Rework this socket stuff to work with online messaging + const useWebSocket = false; + if (useWebSocket) { + if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { + this.socket.close(); + this.wsr.close(); + } + // initialize the socket and start listening for messages + this.socket = this.server.getMessageSocket(); + this.socket.onclose = this.onclose.bind(this); + this.socket.onerror = this.onerror.bind(this); + this.socket.onopen = this.onopen.bind(this); + this.wsr = new WebSocketResource(this.socket, { + handleRequest: this.handleRequest.bind(this), + keepalive: { + path: '/v1/keepalive', + disconnect: true, + }, + }); + + // Because sometimes the socket doesn't properly emit its close event + this._onClose = this.onclose.bind(this); + this.wsr.addEventListener('close', this._onClose); + } + // Ensures that an immediate 'empty' event from the websocket will fire only after // all cached envelopes are processed. this.incoming = [this.pending]; @@ -130,11 +163,29 @@ MessageReceiver.prototype.extend({ this.stoppingProcessing = true; return this.close(); }, - shutdown() {}, + shutdown() { + if (this.socket) { + this.socket.onclose = null; + this.socket.onerror = null; + this.socket.onopen = null; + this.socket = null; + } + + if (this.wsr) { + this.wsr.removeEventListener('close', this._onClose); + this.wsr = null; + } + }, async close() { window.log.info('MessageReceiver.close()'); this.calledClose = true; + // Our WebSocketResource instance will close the socket and emit a 'close' event + // if the socket doesn't emit one quickly enough. + if (this.wsr) { + this.wsr.close(3000, 'called close'); + } + // stop polling all open group rooms if (lokiPublicChatAPI) { await lokiPublicChatAPI.close(); @@ -169,6 +220,27 @@ MessageReceiver.prototype.extend({ 'calledClose:', this.calledClose ); + // TODO: handle properly + // this.shutdown(); + + // if (this.calledClose) { + // return Promise.resolve(); + // } + // if (ev.code === 3000) { + // return Promise.resolve(); + // } + // if (ev.code === 3001) { + // this.onEmpty(); + // } + // // possible 403 or network issue. Make an request to confirm + // return this.server + // .getDevices(this.number) + // .then(this.connect.bind(this)) // No HTTP error? Reconnect + // .catch(e => { + // const event = new Event('error'); + // event.error = e; + // return this.dispatchAndWait(event); + // }); }, handleRequest(request, options) { const { onSuccess, onFailure } = options; @@ -565,7 +637,9 @@ MessageReceiver.prototype.extend({ ? WebSocket.OPEN : WebSocket.CLOSED; } - if (this.hasConnected) { + if (this.socket) { + return this.socket.readyState; + } else if (this.hasConnected) { return WebSocket.CLOSED; } return -1; @@ -903,11 +977,6 @@ MessageReceiver.prototype.extend({ 'devicePairingRequestReceived', pairingRequest.secondaryDevicePubKey ); - } else { - Whisper.events.trigger( - 'devicePairingRequestReceivedNoListener', - pairingRequest.secondaryDevicePubKey - ); } // Ignore requests if the dialog is closed } diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 5feb6d648..369f8ecee 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -178,6 +178,10 @@ OutgoingMessage.prototype = { return false; }) ); + // TODO: check if still applicable + // if (updateDevices === undefined) { + // return this.server.getKeysForNumber(number, '*').then(handleResult); + // } let promise = Promise.resolve(true); updateDevices.forEach(device => { promise = promise.then(() => diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index a0bfe4267..8775c9987 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -184,8 +184,8 @@ Message.prototype = { }, }; -function MessageSender() { - this.server = WebAPI.connect(); +function MessageSender(username, password) { + this.server = WebAPI.connect({ username, password }); this.pendingMessages = {}; } @@ -595,8 +595,16 @@ MessageSender.prototype = { ); }, - async getProfile() { - throw new Error('Signal code called. This needs to be removed'); + async getProfile(number, { accessKey } = {}) { + if (accessKey) { + return this.server.getProfileUnauth(number, { accessKey }); + } + + return this.server.getProfile(number); + }, + + getAvatar(path) { + return this.server.getAvatar(path); }, uploadAvatar(attachment) { @@ -1330,6 +1338,7 @@ textsecure.MessageSender = function MessageSenderWrapper(username, password) { this.leaveGroup = sender.leaveGroup.bind(sender); this.sendSyncMessage = sender.sendSyncMessage.bind(sender); this.getProfile = sender.getProfile.bind(sender); + this.getAvatar = sender.getAvatar.bind(sender); this.uploadAvatar = sender.uploadAvatar.bind(sender); this.syncReadMessages = sender.syncReadMessages.bind(sender); this.syncVerification = sender.syncVerification.bind(sender); diff --git a/libtextsecure/stringview.js b/libtextsecure/stringview.js index 6be7d28b3..75b00aefd 100644 --- a/libtextsecure/stringview.js +++ b/libtextsecure/stringview.js @@ -26,7 +26,6 @@ : 0; }, - // This is not a "standard" base64, do not use! base64ToBytes(sBase64, nBlocksSize) { const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ''); const nInLen = sB64Enc.length; diff --git a/libtextsecure/test/fake_web_api.js b/libtextsecure/test/fake_web_api.js index 3c38a806d..29e2a932f 100644 --- a/libtextsecure/test/fake_web_api.js +++ b/libtextsecure/test/fake_web_api.js @@ -1,11 +1,56 @@ window.setImmediate = window.nodeSetImmediate; +const getKeysForNumberMap = {}; +const messagesSentMap = {}; + const fakeCall = () => Promise.resolve(); const fakeAPI = { + confirmCode: fakeCall, getAttachment: fakeCall, + getAvatar: fakeCall, + getDevices: fakeCall, + // getKeysForNumber: fakeCall, + getMessageSocket: fakeCall, + getMyKeys: fakeCall, + getProfile: fakeCall, + getProvisioningSocket: fakeCall, putAttachment: fakeCall, - putAvatar: fakeCall, + registerKeys: fakeCall, + requestVerificationSMS: fakeCall, + requestVerificationVoice: fakeCall, + // sendMessages: fakeCall, + setSignedPreKey: fakeCall, + + getKeysForNumber(number) { + const res = getKeysForNumberMap[number]; + if (res !== undefined) { + delete getKeysForNumberMap[number]; + return Promise.resolve(res); + } + throw new Error('getKeysForNumber of unknown/used number'); + }, + + sendMessages(destination, messageArray) { + for (let i = 0, max = messageArray.length; i < max; i += 1) { + const msg = messageArray[i]; + if ( + (msg.type !== 1 && msg.type !== 3) || + msg.destinationDeviceId === undefined || + msg.destinationRegistrationId === undefined || + msg.body === undefined || + msg.timestamp === undefined || + msg.relay !== undefined || + msg.destination !== undefined + ) { + throw new Error('Invalid message'); + } + + messagesSentMap[ + `${destination}.${messageArray[i].destinationDeviceId}` + ] = msg; + } + }, }; window.WebAPI = { diff --git a/main.js b/main.js index 8be3fe80e..e0a54cf45 100644 --- a/main.js +++ b/main.js @@ -9,7 +9,7 @@ const crypto = require('crypto'); const _ = require('lodash'); const pify = require('pify'); const electron = require('electron'); -const { setup: setupSpellChecker } = require('./app/spell_check'); + const packageJson = require('./package.json'); const GlobalErrors = require('./app/global_errors'); @@ -82,25 +82,6 @@ const { } = require('./app/protocol_filter'); const { installPermissionsHandler } = require('./app/permissions'); -const _sodium = require('libsodium-wrappers'); - -async function getSodium() { - await _sodium.ready; - return _sodium; -} - -let appStartInitialSpellcheckSetting = true; - -async function getSpellCheckSetting() { - const json = await sql.getItemById('spell-check'); - // Default to `true` if setting doesn't exist yet - if (!json) { - return true; - } - - return json.value; -} - function showWindow() { if (!mainWindow) { return; @@ -174,6 +155,7 @@ function prepareURL(pathSegments, moreKeys) { serverUrl: config.get('serverUrl'), localUrl: config.get('localUrl'), cdnUrl: config.get('cdnUrl'), + localServerPort: config.get('localServerPort'), defaultPoWDifficulty: config.get('defaultPoWDifficulty'), seedNodeList: JSON.stringify(config.get('seedNodeList')), certificateAuthority: config.get('certificateAuthority'), @@ -185,7 +167,6 @@ function prepareURL(pathSegments, moreKeys) { contentProxyUrl: config.contentProxyUrl, importMode: importMode ? true : undefined, // for stringify() serverTrustRoot: config.get('serverTrustRoot'), - appStartInitialSpellcheckSetting, defaultFileServer: config.get('defaultFileServer'), ...moreKeys, }, @@ -205,12 +186,10 @@ function captureClicks(window) { window.webContents.on('new-window', handleUrl); } -const DEFAULT_WIDTH = 880; -// add contact button needs to be visible (on HiDpi screens?) -// otherwise integration test fail -const DEFAULT_HEIGHT = 820; +const DEFAULT_WIDTH = 800; +const DEFAULT_HEIGHT = 720; const MIN_WIDTH = 880; -const MIN_HEIGHT = 820; +const MIN_HEIGHT = 580; const BOUNDS_BUFFER = 100; function isVisible(window, bounds) { @@ -238,7 +217,7 @@ function isVisible(window, bounds) { ); } -async function createWindow() { +function createWindow() { const { screen } = electron; const windowOptions = Object.assign( { @@ -255,7 +234,6 @@ async function createWindow() { contextIsolation: false, preload: path.join(__dirname, 'preload.js'), nativeWindowOpen: true, - spellcheck: await getSpellCheckSetting(), }, icon: path.join(__dirname, 'images', 'session', 'icon_64.png'), }, @@ -306,8 +284,6 @@ async function createWindow() { // Create the browser window. mainWindow = new BrowserWindow(windowOptions); - setupSpellChecker(mainWindow, locale.messages); - // Disable system main menu mainWindow.setMenu(null); @@ -355,10 +331,6 @@ async function createWindow() { mainWindow.on('focus', () => { mainWindow.flashFrame(false); - if (passwordWindow) { - passwordWindow.close(); - passwordWindow = null; - } }); if (config.environment === 'test') { @@ -371,8 +343,6 @@ async function createWindow() { mainWindow.loadURL( prepareURL([__dirname, 'libloki', 'test', 'index.html']) ); - } else if (config.environment.includes('test-integration')) { - mainWindow.loadURL(prepareURL([__dirname, 'background_test.html'])); } else { mainWindow.loadURL(prepareURL([__dirname, 'background.html'])); } @@ -397,7 +367,6 @@ async function createWindow() { config.environment === 'test' || config.environment === 'test-lib' || config.environment === 'test-loki' || - config.environment.includes('test-integration') || (mainWindow.readyForShutdown && windowState.shouldQuit()) ) { return; @@ -630,7 +599,7 @@ async function showDebugLogWindow() { return; } - const theme = await getThemeFromMainWindow(); + const theme = await pify(getDataFromMainWindow)('theme-setting'); const size = mainWindow.getSize(); const options = { width: Math.max(size[0] - 100, MIN_WIDTH), @@ -678,7 +647,7 @@ async function showPermissionsPopupWindow() { return; } - const theme = await getThemeFromMainWindow(); + const theme = await pify(getDataFromMainWindow)('theme-setting'); const size = mainWindow.getSize(); const options = { width: Math.min(400, size[0]), @@ -729,8 +698,7 @@ app.on('ready', async () => { if ( process.env.NODE_ENV !== 'test' && process.env.NODE_ENV !== 'test-lib' && - process.env.NODE_ENV !== 'test-loki' && - !process.env.NODE_ENV.includes('test-integration') + process.env.NODE_ENV !== 'test-loki' ) { installFileHandler({ protocol: electronProtocol, @@ -803,7 +771,6 @@ async function showMainWindow(sqlKey, passwordAttempt = false) { messages: locale.messages, passwordAttempt, }); - appStartInitialSpellcheckSetting = await getSpellCheckSetting(); await sqlChannels.initialize(); try { @@ -927,8 +894,7 @@ app.on('window-all-closed', () => { process.platform !== 'darwin' || config.environment === 'test' || config.environment === 'test-lib' || - config.environment === 'test-loki' || - config.environment.includes('test-integration') + config.environment === 'test-loki' ) { app.quit(); } @@ -979,10 +945,11 @@ ipc.on('add-setup-menu-items', () => { }); ipc.on('draw-attention', () => { - if (!mainWindow) { - return; - } - if (process.platform === 'win32' || process.platform === 'linux') { + if (process.platform === 'darwin') { + app.dock.bounce(); + } else if (process.platform === 'win32') { + mainWindow.flashFrame(true); + } else if (process.platform === 'linux') { mainWindow.flashFrame(true); } }); @@ -1031,6 +998,11 @@ ipc.on('password-window-login', async (event, passPhrase) => { const passwordAttempt = true; await showMainWindow(passPhrase, passwordAttempt); sendResponse(); + + if (passwordWindow) { + passwordWindow.close(); + passwordWindow = null; + } } catch (e) { const localisedError = locale.messages.invalidPassword.message; sendResponse(localisedError || 'Invalid password'); @@ -1127,52 +1099,6 @@ ipc.on('get-auto-update-setting', event => { event.returnValue = typeof configValue !== 'boolean' ? true : configValue; }); -async function decryptLns(event, lnsName, ciphertext) { - const sodium = await getSodium(); - - const salt = new Uint8Array(sodium.crypto_pwhash_SALTBYTES); - - try { - const key = sodium.crypto_pwhash( - sodium.crypto_secretbox_KEYBYTES, - lnsName, - salt, - sodium.crypto_pwhash_OPSLIMIT_MODERATE, - sodium.crypto_pwhash_MEMLIMIT_MODERATE, - sodium.crypto_pwhash_ALG_ARGON2ID13 - ); - - const nonce = new Uint8Array(sodium.crypto_secretbox_NONCEBYTES); - - const res = sodium.crypto_secretbox_open_easy(ciphertext, nonce, key); - - // null as first parameter to indivate no error - event.reply('decrypt-lns-response', null, res); - } catch (err) { - event.reply('decrypt-lns-response', err); - } -} - -async function blake2bDigest(event, input) { - const sodium = await getSodium(); - - try { - const res = sodium.crypto_generichash(32, input); - - event.reply('blake2b-digest-response', null, res); - } catch (err) { - event.reply('blake2b-digest-response', err); - } -} - -ipc.on('blake2b-digest', (event, input) => { - blake2bDigest(event, input); -}); - -ipc.on('decrypt-lns-entry', (event, lnsName, ciphertext) => { - decryptLns(event, lnsName, ciphertext); -}); - ipc.on('set-auto-update-setting', (event, enabled) => { userConfig.set('autoUpdate', !!enabled); @@ -1184,9 +1110,9 @@ ipc.on('set-auto-update-setting', (event, enabled) => { } }); -function getThemeFromMainWindow() { - return new Promise(resolve => { - ipc.once('get-success-theme-setting', (_event, value) => resolve(value)); - mainWindow.webContents.send('get-theme-setting'); - }); +function getDataFromMainWindow(name, callback) { + ipc.once(`get-success-${name}`, (_event, error, value) => + callback(error, value) + ); + mainWindow.webContents.send(`get-${name}`); } diff --git a/package.json b/package.json index 8eb720e00..edd8b54c8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "session-messenger-desktop", "productName": "Session", "description": "Private messaging from your desktop", - "version": "1.0.6", + "version": "1.0.5", "license": "GPL-3.0", "author": { "name": "Loki Project", @@ -18,46 +18,55 @@ "start": "electron .", "start-multi": "cross-env NODE_APP_INSTANCE=1 electron .", "start-multi2": "cross-env NODE_APP_INSTANCE=2 electron .", - "start-prod": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod electron .", - "start-prod-multi": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod1 electron .", - "start-swarm-test": "cross-env NODE_ENV=swarm-testing NODE_APP_INSTANCE=1 electron .", - "start-swarm-test-2": "cross-env NODE_ENV=swarm-testing NODE_APP_INSTANCE=2 electron .", + "start-prod": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod LOKI_DEV=1 electron .", + "start-prod-multi": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod1 LOKI_DEV=1 electron .", + "start-swarm-test": "cross-env NODE_ENV=swarm-testing NODE_APP_INSTANCE=test1 LOKI_DEV=1 electron .", + "start-swarm-test-2": "cross-env NODE_ENV=swarm-testing2 NODE_APP_INSTANCE=test2 LOKI_DEV=1 electron .", "grunt": "grunt", "icon-gen": "electron-icon-maker --input=images/icon_1024.png --output=./build", "generate": "yarn icon-gen && yarn grunt", + "build": "electron-builder --config.extraMetadata.environment=$SIGNAL_ENV", "build-release": "cross-env SIGNAL_ENV=production npm run build -- --config.directories.output=release", + "make:linux:x64:appimage": "electron-builder build --linux appimage --x64", "build-module-protobuf": "pbjs --target static-module --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js", "clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js", "build-protobuf": "yarn build-module-protobuf", "clean-protobuf": "yarn clean-module-protobuf", + "prepare-beta-build": "node prepare_beta_build.js", + "prepare-import-build": "node prepare_import_build.js", + "publish-to-apt": "NAME=$npm_package_name VERSION=$npm_package_version ./aptly.sh", "test": "yarn test-node && yarn test-electron", "test-view": "NODE_ENV=test yarn run start", "test-lib-view": "NODE_ENV=test-lib yarn run start", "test-loki-view": "NODE_ENV=test-loki yarn run start", "test-electron": "yarn grunt test", - "test-integration": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js", - "test-integration-parts": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'registration' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'openGroup' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'addFriends' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'linkDevice' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'closedGroup'", "test-node": "mocha --recursive --exit test/app test/modules ts/test libloki/test/node", + "test-node-coverage": "nyc --reporter=lcov --reporter=text mocha --recursive test/app test/modules ts/test libloki/test/node", + "test-node-coverage-html": "nyc --reporter=lcov --reporter=html mocha --recursive test/a/* */pp test/modules ts/test libloki/test/node", "eslint": "eslint --cache .", "eslint-fix": "eslint --fix .", "eslint-full": "eslint .", - "lint": "yarn format && yarn lint-files", - "lint-full": "yarn format-full && yarn lint-files-full", - "dev-lint": "yarn format && yarn lint-files", - "lint-files": "yarn eslint && yarn tslint", - "lint-files-full": "yarn eslint-full && yarn tslint", + "lint": "yarn format --list-different && yarn lint-windows", + "lint-full": "yarn format-full --list-different; yarn lint-windows-full", + "dev-lint": "yarn format --list-different; yarn lint-windows", + "lint-windows": "yarn eslint && yarn tslint", + "lint-windows-full": "yarn eslint-full && yarn tslint", "lint-deps": "node ts/util/lint/linter.js", "tslint": "tslint --format stylish --project .", - "format": "prettier --list-different --write `git ls-files --modified *.{css,js,json,scss,ts,tsx}` `git ls-files --modified ./**/*.{css,js,json,scss,ts,tsx}`", - "format-full": "prettier --list-different --write \"*.{css,js,json,scss,ts,tsx}\" \"./**/*.{css,js,json,scss,ts,tsx}\"", + "format": "prettier --write `git ls-files --modified *.{css,js,json,md,scss,ts,tsx}` `git ls-files --modified ./**/*.{css,js,json,md,scss,ts,tsx}`", + "format-full": "prettier --write \"*.{css,js,json,md,scss,ts,tsx}\" \"./**/*.{css,js,json,md,scss,ts,tsx}\"", "transpile": "tsc", "clean-transpile": "rimraf ts/**/*.js && rimraf ts/*.js", + "open-coverage": "open coverage/lcov-report/index.html", + "styleguide": "styleguidist server", "pow-metrics": "node metrics_app.js localhost 9000", "ready": "yarn clean-transpile && yarn grunt && yarn lint-full && yarn test-node && yarn test-electron && yarn lint-deps" }, "dependencies": { "@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#b10f232fac62ba7f8775c9e086bb5558fe7d948b", "@sindresorhus/is": "0.8.0", + "@types/dompurify": "^2.0.0", + "@types/rc-slider": "^8.6.5", "backbone": "1.3.3", "blob-util": "1.3.0", "blueimp-canvas-to-blob": "3.14.0", @@ -69,6 +78,8 @@ "config": "1.28.1", "cross-env": "^6.0.3", "dompurify": "^2.0.7", + "electron-context-menu": "^0.15.0", + "electron-editor-context-menu": "1.1.1", "electron-is-dev": "^1.1.0", "electron-localshortcut": "^3.2.1", "electron-updater": "^4.2.2", @@ -79,22 +90,29 @@ "filesize": "3.6.1", "firstline": "1.2.1", "form-data": "^3.0.0", - "fs-extra": "9.0.0", + "fs-extra": "5.0.0", "glob": "7.1.2", + "google-libphonenumber": "3.2.2", + "got": "8.2.0", "he": "1.2.0", + "intl-tel-input": "12.1.15", "jquery": "3.3.1", + "js-sha512": "0.8.0", + "js-yaml": "3.13.0", "jsbn": "1.1.0", - "libsodium-wrappers": "^0.7.6", + "libsodium-wrappers": "^0.7.4", "linkify-it": "2.0.3", "lodash": "4.17.11", "mkdirp": "0.5.1", "moment": "2.21.0", "mustache": "2.3.0", "node-fetch": "2.3.0", + "node-gyp": "3.8.0", "node-sass": "4.9.3", "os-locale": "2.1.0", "pify": "3.0.0", "protobufjs": "6.8.6", + "proxy-agent": "3.0.3", "rc-slider": "^8.7.1", "react": "16.8.3", "react-contextmenu": "2.11.0", @@ -110,29 +128,33 @@ "reselect": "4.0.0", "rimraf": "2.6.2", "semver": "5.4.1", + "spellchecker": "3.7.0", "tar": "4.4.8", + "testcheck": "1.0.0-rc.2", "tmp": "0.0.33", "to-arraybuffer": "1.0.1", "underscore": "1.9.0", - "uuid": "3.3.2" + "uuid": "3.3.2", + "websocket": "1.0.28" }, "devDependencies": { "@types/chai": "4.1.2", "@types/classnames": "2.2.3", "@types/color": "^3.0.0", "@types/config": "0.0.34", - "@types/dompurify": "^2.0.0", "@types/electron-is-dev": "^1.1.1", "@types/filesize": "3.6.0", "@types/fs-extra": "5.0.5", + "@types/google-libphonenumber": "7.4.14", + "@types/got": "9.4.1", "@types/jquery": "3.3.29", + "@types/js-yaml": "3.12.0", "@types/linkify-it": "2.0.3", "@types/lodash": "4.14.106", "@types/mkdirp": "0.5.2", "@types/mocha": "5.0.0", "@types/pify": "3.0.2", "@types/qs": "6.5.1", - "@types/rc-slider": "^8.6.5", "@types/react": "16.8.5", "@types/react-dom": "16.8.2", "@types/react-portal": "^4.0.2", @@ -147,10 +169,9 @@ "asar": "0.14.0", "bower": "1.8.2", "chai": "4.1.2", - "chai-as-promised": "^7.1.1", "dashdash": "1.14.1", - "electron": "8.2.0", - "electron-builder": "22.3.6", + "electron": "4.1.2", + "electron-builder": "22.3.2", "electron-icon-maker": "0.0.3", "electron-notarize": "^0.2.0", "eslint": "4.14.0", @@ -170,7 +191,6 @@ "grunt-sass": "3.0.1", "mocha": "4.1.0", "mocha-testcheck": "1.0.0-rc.0", - "node-gyp": "3.8.0", "node-sass-import-once": "1.2.0", "nyc": "11.4.1", "prettier": "1.12.0", @@ -178,7 +198,7 @@ "react-docgen-typescript": "1.2.6", "react-styleguidist": "7.0.1", "sinon": "4.4.2", - "spectron": "^10.0.0", + "spectron": "5.0.0", "ts-loader": "4.1.0", "tslint": "5.13.0", "tslint-microsoft-contrib": "6.0.0", @@ -252,6 +272,7 @@ "config/local-${env.SIGNAL_ENV}.json", "background.html", "about.html", + "settings.html", "password.html", "permissions_popup.html", "debug_log.html", diff --git a/password_preload.js b/password_preload.js index a90393ecb..1144878ea 100644 --- a/password_preload.js +++ b/password_preload.js @@ -34,7 +34,7 @@ window.Signal.Logs = require('./js/modules/logs'); window.CONSTANTS = { MAX_LOGIN_TRIES: 3, - MAX_PASSWORD_LENGTH: 64, + MAX_PASSWORD_LENGTH: 32, MAX_USERNAME_LENGTH: 20, }; diff --git a/preload.js b/preload.js index 4d513718c..94fa47338 100644 --- a/preload.js +++ b/preload.js @@ -1,4 +1,3 @@ -/* eslint-disable global-require */ /* global Whisper: false */ /* global window: false */ const path = require('path'); @@ -51,13 +50,6 @@ window.getStoragePubKey = key => window.getDefaultFileServer = () => config.defaultFileServer; window.initialisedAPI = false; -if ( - typeof process.env.NODE_ENV === 'string' && - process.env.NODE_ENV.includes('test-integration') -) { - window.electronRequire = require; -} - window.isBeforeVersion = (toCheck, baseVersion) => { try { return semver.lt(toCheck, baseVersion); @@ -72,7 +64,7 @@ window.isBeforeVersion = (toCheck, baseVersion) => { window.CONSTANTS = { MAX_LOGIN_TRIES: 3, - MAX_PASSWORD_LENGTH: 64, + MAX_PASSWORD_LENGTH: 32, MAX_USERNAME_LENGTH: 20, MAX_GROUP_NAME_LENGTH: 64, DEFAULT_PUBLIC_CHAT_URL: appConfig.get('defaultPublicChatServer'), @@ -95,26 +87,6 @@ window.wrapDeferred = deferredToPromise; const ipc = electron.ipcRenderer; const localeMessages = ipc.sendSync('locale-data'); -window.blake2b = input => - new Promise((resolve, reject) => { - ipc.once('blake2b-digest-response', (event, error, res) => { - // eslint-disable-next-line no-unused-expressions - error ? reject(error) : resolve(res); - }); - - ipc.send('blake2b-digest', input); - }); - -window.decryptLnsEntry = (key, value) => - new Promise((resolve, reject) => { - ipc.once('decrypt-lns-response', (event, error, res) => { - // eslint-disable-next-line no-unused-expressions - error ? reject(error) : resolve(res); - }); - - ipc.send('decrypt-lns-entry', key, value); - }); - window.updateZoomFactor = () => { const zoomFactor = window.getSettingValue('zoom-factor-setting') || 100; window.setZoomFactor(zoomFactor / 100); @@ -217,11 +189,6 @@ ipc.on('set-up-as-standalone', () => { Whisper.events.trigger('setupAsStandalone'); }); -ipc.on('get-theme-setting', () => { - const theme = window.Events.getThemeSetting(); - ipc.send('get-success-theme-setting', theme); -}); - // Settings-related events window.showPermissionsPopup = () => ipc.send('show-permissions-popup'); @@ -312,7 +279,13 @@ window.nodeSetImmediate = setImmediate; const { initialize: initializeWebAPI } = require('./js/modules/web_api'); -window.WebAPI = initializeWebAPI(); +window.WebAPI = initializeWebAPI({ + url: config.serverUrl, + cdnUrl: config.cdnUrl, + certificateAuthority: config.certificateAuthority, + contentProxyUrl: config.contentProxyUrl, + proxyUrl: config.proxyUrl, +}); window.seedNodeList = JSON.parse(config.seedNodeList); const LokiSnodeAPI = require('./js/modules/loki_snode_api'); @@ -324,10 +297,6 @@ window.lokiSnodeAPI = new LokiSnodeAPI({ window.LokiMessageAPI = require('./js/modules/loki_message_api'); -if (process.env.USE_STUBBED_NETWORK) { - window.StubMessageAPI = require('./integration_test/stubs/stub_message_api'); - window.StubAppDotNetApi = require('./integration_test/stubs/stub_app_dot_net_api'); -} window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api'); window.LokiAppDotNetServerAPI = require('./js/modules/loki_app_dot_net_api'); @@ -336,6 +305,8 @@ window.LokiFileServerAPI = require('./js/modules/loki_file_server_api'); window.LokiRssAPI = require('./js/modules/loki_rss_api'); +window.localServerPort = config.localServerPort; + window.mnemonic = require('./libloki/modules/mnemonic'); const WorkerInterface = require('./js/modules/util_worker_interface'); @@ -356,6 +327,8 @@ window.dataURLToBlobSync = require('blueimp-canvas-to-blob'); window.emojiData = require('emoji-datasource'); window.EmojiPanel = require('emoji-panel'); window.filesize = require('filesize'); +window.libphonenumber = require('google-libphonenumber').PhoneNumberUtil.getInstance(); +window.libphonenumber.PhoneNumberFormat = require('google-libphonenumber').PhoneNumberFormat; window.loadImage = require('blueimp-load-image'); window.getGuid = require('uuid/v4'); window.profileImages = require('./app/profile_images'); @@ -364,6 +337,13 @@ window.React = require('react'); window.ReactDOM = require('react-dom'); window.moment = require('moment'); +const _sodium = require('libsodium-wrappers'); + +window.getSodium = async () => { + await _sodium.ready; + return _sodium; +}; + window.clipboard = clipboard; const Signal = require('./js/modules/signal'); @@ -393,17 +373,34 @@ window.Signal.Backup = require('./js/modules/backup'); window.Signal.Debug = require('./js/modules/debug'); window.Signal.Logs = require('./js/modules/logs'); -window.addEventListener('contextmenu', e => { - const editable = e.target.closest( - 'textarea, input, [contenteditable="true"]' - ); - const link = e.target.closest('a'); - const selection = Boolean(window.getSelection().toString()); - if (!editable && !selection && !link) { - e.preventDefault(); - } +// Add right-click listener for selected text and urls +const contextMenu = require('electron-context-menu'); + +const isQR = params => + params.mediaType === 'image' && params.titleText === 'Scan me!'; + +// QR saving doesn't work so we just disable it +contextMenu({ + showInspectElement: false, + shouldShowMenu: (event, params) => { + const isRegular = + params.mediaType === 'none' && (params.linkURL || params.selectionText); + return Boolean(!params.isEditable && (isQR(params) || isRegular)); + }, + menu: (actions, params) => { + // If it's not a QR then show the default options + if (!isQR(params)) { + return actions; + } + + return [actions.copyImage()]; + }, }); +// We pull this in last, because the native module involved appears to be sensitive to +// /tmp mounted as noexec on Linux. +require('./js/spell_check'); + window.shortenPubkey = pubkey => `(...${pubkey.substring(pubkey.length - 6)})`; window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g; @@ -412,9 +409,8 @@ window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g; window.lokiFeatureFlags = { multiDeviceUnpairing: true, privateGroupChats: true, - useSnodeProxy: !process.env.USE_STUBBED_NETWORK, + useSnodeProxy: true, useSealedSender: true, - useOnionRequests: false, }; // eslint-disable-next-line no-extend-native,func-names @@ -423,10 +419,7 @@ Promise.prototype.ignore = function() { this.then(() => {}); }; -if ( - config.environment.includes('test') && - !config.environment.includes('swarm-testing') -) { +if (config.environment.includes('test')) { const isWindows = process.platform === 'win32'; /* eslint-disable global-require, import/no-extraneous-dependencies */ window.test = { @@ -446,13 +439,5 @@ if ( updateSwarmNodes: () => {}, updateLastHash: () => {}, getSwarmNodesForPubKey: () => [], - buildNewOnionPaths: () => [], - }; -} -if (config.environment.includes('test-integration')) { - window.lokiFeatureFlags = { - multiDeviceUnpairing: true, - privateGroupChats: true, - useSnodeProxy: !process.env.USE_STUBBED_NETWORK, }; } diff --git a/prepare_beta_build.js b/prepare_beta_build.js new file mode 100644 index 000000000..13e297297 --- /dev/null +++ b/prepare_beta_build.js @@ -0,0 +1,66 @@ +/* eslint-disable no-console */ + +const fs = require('fs'); +const _ = require('lodash'); + +const packageJson = require('./package.json'); + +const { version } = packageJson; +const beta = /beta/; + +// You might be wondering why this file is necessary. It comes down to our desire to allow +// side-by-side installation of production and beta builds. Electron-Builder uses +// top-level data from package.json for many things, like the executable name, the +// debian package name, the install directory under /opt on linux, etc. We tried +// adding the ${channel} macro to these values, but Electron-Builder didn't like that. + +if (!beta.test(version)) { + process.exit(); +} + +console.log('prepare_beta_build: updating package.json'); + +// ------- + +const NAME_PATH = 'name'; +const PRODUCTION_NAME = 'loki-messenger-desktop'; +const BETA_NAME = 'loki-messenger-desktop-beta'; + +const PRODUCT_NAME_PATH = 'productName'; +const PRODUCTION_PRODUCT_NAME = 'Session'; +const BETA_PRODUCT_NAME = 'Session Beta'; + +const APP_ID_PATH = 'build.appId'; +const PRODUCTION_APP_ID = 'com.loki-project.messenger-desktop'; +const BETA_APP_ID = 'com.loki-project.messenger-desktop-beta'; + +const STARTUP_WM_CLASS_PATH = 'build.linux.desktop.StartupWMClass'; +const PRODUCTION_STARTUP_WM_CLASS = 'Session'; +const BETA_STARTUP_WM_CLASS = 'Session Beta'; + +// ------- + +function checkValue(object, objectPath, expected) { + const actual = _.get(object, objectPath); + if (actual !== expected) { + throw new Error(`${objectPath} was ${actual}; expected ${expected}`); + } +} + +// ------ + +checkValue(packageJson, NAME_PATH, PRODUCTION_NAME); +checkValue(packageJson, PRODUCT_NAME_PATH, PRODUCTION_PRODUCT_NAME); +checkValue(packageJson, APP_ID_PATH, PRODUCTION_APP_ID); +checkValue(packageJson, STARTUP_WM_CLASS_PATH, PRODUCTION_STARTUP_WM_CLASS); + +// ------- + +_.set(packageJson, NAME_PATH, BETA_NAME); +_.set(packageJson, PRODUCT_NAME_PATH, BETA_PRODUCT_NAME); +_.set(packageJson, APP_ID_PATH, BETA_APP_ID); +_.set(packageJson, STARTUP_WM_CLASS_PATH, BETA_STARTUP_WM_CLASS); + +// ------- + +fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, ' ')); diff --git a/prepare_import_build.js b/prepare_import_build.js new file mode 100644 index 000000000..e74a168ed --- /dev/null +++ b/prepare_import_build.js @@ -0,0 +1,69 @@ +/* eslint-disable no-console */ + +const fs = require('fs'); +const _ = require('lodash'); + +const packageJson = require('./package.json'); +const defaultConfig = require('./config/default.json'); + +function checkValue(object, objectPath, expected) { + const actual = _.get(object, objectPath); + if (actual !== expected) { + throw new Error(`${objectPath} was ${actual}; expected ${expected}`); + } +} + +// You might be wondering why this file is necessary. We have some very specific +// requirements around our import-flavor builds. They need to look exactly the same as +// normal builds, but they must immediately open into import mode. So they need a +// slight config tweak, and then a change to the .app/.exe name (note: we do NOT want to +// change where data is stored or anything, since that would make these builds +// incompatible with the mainline builds) So we just change the artifact name. +// +// Another key thing to know about these builds is that we should not upload the +// latest.yml (windows) and latest-mac.yml (mac) that go along with the executables. +// This would interrupt the normal install flow for users installing from +// signal.org/download. So any release script will need to upload these files manually +// instead of relying on electron-builder, which will upload everything. + +// ------- + +console.log('prepare_import_build: updating config/default.json'); + +const IMPORT_PATH = 'import'; +const IMPORT_START_VALUE = false; +const IMPORT_END_VALUE = true; + +checkValue(defaultConfig, IMPORT_PATH, IMPORT_START_VALUE); + +_.set(defaultConfig, IMPORT_PATH, IMPORT_END_VALUE); + +// ------- + +console.log('prepare_import_build: updating package.json'); + +const MAC_ASSET_PATH = 'build.mac.artifactName'; +// eslint-disable-next-line no-template-curly-in-string +const MAC_ASSET_START_VALUE = '${name}-mac-${version}.${ext}'; +// eslint-disable-next-line no-template-curly-in-string +const MAC_ASSET_END_VALUE = '${name}-mac-${version}-import.${ext}'; + +const WIN_ASSET_PATH = 'build.win.artifactName'; +// eslint-disable-next-line no-template-curly-in-string +const WIN_ASSET_START_VALUE = '${name}-win-${version}.${ext}'; +// eslint-disable-next-line no-template-curly-in-string +const WIN_ASSET_END_VALUE = '${name}-win-${version}-import.${ext}'; + +checkValue(packageJson, MAC_ASSET_PATH, MAC_ASSET_START_VALUE); +checkValue(packageJson, WIN_ASSET_PATH, WIN_ASSET_START_VALUE); + +_.set(packageJson, MAC_ASSET_PATH, MAC_ASSET_END_VALUE); +_.set(packageJson, WIN_ASSET_PATH, WIN_ASSET_END_VALUE); + +// --- + +fs.writeFileSync( + './config/default.json', + JSON.stringify(defaultConfig, null, ' ') +); +fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, ' ')); diff --git a/settings.html b/settings.html new file mode 100644 index 000000000..bd10df867 --- /dev/null +++ b/settings.html @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/stylesheets/_mentions.scss b/stylesheets/_mentions.scss index eded63c8b..104164dea 100644 --- a/stylesheets/_mentions.scss +++ b/stylesheets/_mentions.scss @@ -82,7 +82,7 @@ .error-faded { opacity: 0; - margin-top: -5px; + margin-top: -20px; transition: all 100ms linear; } @@ -96,6 +96,8 @@ max-height: 240px; overflow-y: auto; margin: 4px; + border-top: 1px solid #2f2f2f; + border-bottom: 1px solid #2f2f2f; .check-mark { float: right; diff --git a/stylesheets/_options.scss b/stylesheets/_options.scss index 51b78dccd..a28bbafcb 100644 --- a/stylesheets/_options.scss +++ b/stylesheets/_options.scss @@ -1,2 +1,15 @@ @import 'variables'; +@import '../node_modules/intl-tel-input/build/css/intlTelInput.css'; @import 'progress'; +.iti-flag { + // override intlTelInput's flags image location + background: url('../node_modules/intl-tel-input/build/img/flags.png'); +} + +.intl-tel-input .country-list { + text-align: left; +} + +.intl-tel-input .country-list .country .country-name { + color: #000; +} diff --git a/stylesheets/_password.scss b/stylesheets/_password.scss new file mode 100644 index 000000000..46a9f0dd0 --- /dev/null +++ b/stylesheets/_password.scss @@ -0,0 +1,50 @@ +.password { + .content-wrapper { + display: flex; + align-items: center; + justify-content: center; + color: $color-dark-05; + width: 100%; + height: 100%; + } + + .content { + margin: 3em; + } + + .inputs { + display: flex; + flex-direction: column; + } + + input { + width: 30em; + } + + .error { + font-weight: bold; + font-size: 16px; + margin-top: 1em; + } + + .reset { + font-size: 15px; + margin-top: 1em; + cursor: pointer; + user-select: none; + + a { + color: #78be20; + font-weight: bold; + } + } + + .overlay { + color: $color-dark-05; + background: $color-dark-85; + + .step { + padding: 0; + } + } +} diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index a70be1e63..0b3aa883e 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -140,11 +140,11 @@ div.spacer-lg { transition: filter 0.1s; } -.subtle { +.text-subtle { opacity: 0.6; } -.soft { +.text-soft { opacity: 0.4; } @@ -460,43 +460,22 @@ $session_message-container-border-radius: 5px; .notification-count { position: absolute; - top: $session-margin-lg; - right: $session-margin-lg; + font-size: $session-font-xs; + font-family: $session-font-family; + top: 20px; + right: 20px; + width: 20px; + height: 20px; padding: 3px; + border-radius: 50%; + font-weight: 700; + background: red; + color: $session-color-white; + text-align: center; opacity: 1; } } -.notification-count { - display: flex; - align-items: center; - justify-content: center; - font-family: $session-font-family; - border-radius: 50%; - font-weight: 700; - background: $session-color-danger; - color: $session-color-white; - text-align: center; - - span { - position: relative; - - sup { - font-size: 130%; - position: absolute; - } - } - - &.hover { - transition: $session-transition-duration; - cursor: pointer; - - &:hover { - filter: brightness(80%); - } - } -} - .session-icon { fill: $session-color-white; } @@ -823,24 +802,6 @@ label { } } -.device-pairing-dialog { - &__desc { - font-weight: 300; - font-size: $session-font-sm; - margin-bottom: $session-margin-lg; - } - - &__secret-words { - display: flex; - flex-direction: column; - align-items: center; - background-color: $session-shade-6; - padding: $session-margin-sm $session-margin-lg; - border-radius: 3px; - margin-bottom: $session-margin-md; - } -} - .create-group-dialog .session-modal__body { display: flex; flex-direction: column; @@ -848,12 +809,6 @@ label { .friend-selection-list { width: unset; } - - .create-group-dialog__member-count { - text-align: center; - margin-top: -25px; - opacity: 0.6; - } } .session-confirm { @@ -969,7 +924,7 @@ label { list-style: none; padding: 0px; margin: 0px; - max-height: 40vh; + max-height: 450px; overflow-y: auto; } @@ -1642,6 +1597,92 @@ input { } } +.clear-data, +.password-prompt { + &-wrapper { + display: flex; + justify-content: center; + align-items: center; + + background-color: $session-color-black; + + width: 100%; + height: 100%; + + padding: 3 * $session-margin-lg; + } + + &-error-section { + width: 100%; + color: $session-color-white; + margin: -$session-margin-sm 0px 2 * $session-margin-lg 0px; + + .session-label { + &.primary { + background-color: rgba($session-color-primary, 0.3); + } + padding: $session-margin-xs $session-margin-sm; + font-size: $session-font-xs; + text-align: center; + } + } + + &-container { + font-family: 'SF Pro Text'; + color: $session-color-white; + display: inline-flex; + flex-direction: column; + align-items: center; + justify-content: center; + + width: 600px; + min-width: 420px; + padding: 3 * $session-margin-lg 2 * $session-margin-lg; + box-sizing: border-box; + background-color: $session-shade-4; + border: 1px solid $session-shade-8; + border-radius: 2px; + + .warning-info-area, + .password-info-area { + display: inline-flex; + justify-content: center; + align-items: center; + + h1 { + color: $session-color-white; + } + svg { + margin-right: $session-margin-lg; + } + } + + p, + input { + margin: $session-margin-lg 0px; + } + + .button-group { + display: inline-flex; + } + + #password-prompt-input { + width: 100%; + color: #fff; + background-color: #2e2e2e; + margin-top: 2 * $session-margin-lg; + padding: $session-margin-xs $session-margin-lg; + outline: none; + border: none; + border-radius: 2px; + text-align: center; + font-size: 24px; + letter-spacing: 5px; + font-family: 'SF Pro Text'; + } + } +} + .onboarding-message-section { display: flex; flex-grow: 1; @@ -1765,13 +1806,6 @@ input { justify-content: space-between; transition: $session-transition-duration; - &:first-child { - border-top: 1px solid rgba($session-shade-8, 0.6); - } - &:last-child { - border-bottom: 1px solid rgba($session-shade-8, 0.6); - } - &.selected { background-color: $session-shade-4; } @@ -1799,10 +1833,6 @@ input { margin-left: 5px; opacity: 0.8; } - - &__avatar > div { - margin-bottom: 0px !important; - } } .invite-friends-container { diff --git a/stylesheets/_session_left_pane.scss b/stylesheets/_session_left_pane.scss index 851a4f5d6..7184ec6f1 100644 --- a/stylesheets/_session_left_pane.scss +++ b/stylesheets/_session_left_pane.scss @@ -149,7 +149,6 @@ $session-compose-margin: 20px; &-session { display: flex; - height: 100vh; } &__sections-container { @@ -466,6 +465,27 @@ $session-compose-margin: 20px; } } +.contact-notification-count-bubble { + display: flex; + align-items: center; + justify-content: center; + background: $session-color-danger; + width: 22px; + height: 22px; + font-size: $session-font-xs; + margin-left: auto; + text-align: center; + border-radius: 50%; + font-weight: bold; + cursor: pointer; + transition: $session-transition-duration; + + color: $session-color-white; + &:hover { + filter: brightness(80%); + } +} + .left-pane-contact { &-section, &-content { diff --git a/stylesheets/_session_password.scss b/stylesheets/_session_password.scss deleted file mode 100644 index 98d548277..000000000 --- a/stylesheets/_session_password.scss +++ /dev/null @@ -1,89 +0,0 @@ -.password { - height: 100vh; - - .clear-data, - .password-prompt { - &-wrapper { - display: flex; - justify-content: center; - align-items: center; - - background-color: $session-color-black; - - width: 100%; - height: 100%; - - padding: 3 * $session-margin-lg; - } - - &-error-section { - width: 100%; - color: $session-color-white; - margin: -$session-margin-sm 0px 2 * $session-margin-lg 0px; - - .session-label { - &.primary { - background-color: rgba($session-color-primary, 0.3); - } - padding: $session-margin-xs $session-margin-sm; - font-size: $session-font-xs; - text-align: center; - } - } - - &-container { - font-family: 'SF Pro Text'; - color: $session-color-white; - display: inline-flex; - flex-direction: column; - align-items: center; - justify-content: center; - - width: 600px; - min-width: 420px; - padding: 3 * $session-margin-lg 2 * $session-margin-lg; - box-sizing: border-box; - background-color: $session-shade-4; - border: 1px solid $session-shade-8; - border-radius: 2px; - - .warning-info-area, - .password-info-area { - display: inline-flex; - justify-content: center; - align-items: center; - - h1 { - color: $session-color-white; - } - svg { - margin-right: $session-margin-lg; - } - } - - p, - input { - margin: $session-margin-lg 0px; - } - - .button-group { - display: inline-flex; - } - - #password-prompt-input { - width: 100%; - color: #fff; - background-color: #2e2e2e; - margin-top: 2 * $session-margin-lg; - padding: $session-margin-xs $session-margin-lg; - outline: none; - border: none; - border-radius: 2px; - text-align: center; - font-size: 24px; - letter-spacing: 5px; - font-family: 'SF Pro Text'; - } - } - } -} diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index 6ff2f4e76..39de2faad 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -1670,6 +1670,10 @@ body.dark-theme { // _options + .intl-tel-input .country-list .country .country-name { + color: #000; + } + // _progress // Not sure we need to change anything there - it's blue diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index 4961f65a3..d4657b295 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -12,6 +12,7 @@ @import 'emoji'; @import 'mentions'; @import 'settings'; +@import 'password'; // Build the main view @import 'index'; @@ -21,16 +22,10 @@ @import 'ios'; @import 'theme_dark'; -// /////////////////// // -// ///// Session ///// // -// /////////////////// // +// Session @import 'modules'; @import 'session'; - -// Separate screens @import 'session_signin'; -@import 'session_password'; - @import 'session_theme'; @import 'session_left_pane'; @import 'session_group_panel'; diff --git a/test/index.html b/test/index.html index a779fba6d..54d1dd46f 100644 --- a/test/index.html +++ b/test/index.html @@ -500,6 +500,7 @@ + @@ -547,6 +548,7 @@ + @@ -574,6 +576,7 @@ + @@ -583,6 +586,7 @@ + diff --git a/test/libphonenumber_util_test.js b/test/libphonenumber_util_test.js new file mode 100644 index 000000000..0e478e4eb --- /dev/null +++ b/test/libphonenumber_util_test.js @@ -0,0 +1,26 @@ +/* global libphonenumber */ + +'use strict'; + +describe('libphonenumber util', () => { + describe('parseNumber', () => { + it('numbers with + are valid without providing regionCode', () => { + const result = libphonenumber.util.parseNumber('+14155555555'); + assert.isTrue(result.isValidNumber); + assert.strictEqual(result.nationalNumber, '4155555555'); + assert.strictEqual(result.e164, '+14155555555'); + assert.strictEqual(result.regionCode, 'US'); + assert.strictEqual(result.countryCode, '1'); + }); + it('variant numbers with the right regionCode are valid', () => { + ['4155555555', '14155555555', '+14155555555'].forEach(number => { + const result = libphonenumber.util.parseNumber(number, 'US'); + assert.isTrue(result.isValidNumber); + assert.strictEqual(result.nationalNumber, '4155555555'); + assert.strictEqual(result.e164, '+14155555555'); + assert.strictEqual(result.regionCode, 'US'); + assert.strictEqual(result.countryCode, '1'); + }); + }); + }); +}); diff --git a/test/modules/privacy_test.js b/test/modules/privacy_test.js index e51959f6a..df3490bd3 100644 --- a/test/modules/privacy_test.js +++ b/test/modules/privacy_test.js @@ -7,27 +7,18 @@ const Privacy = require('../../js/modules/privacy'); const APP_ROOT_PATH = path.join(__dirname, '..', '..', '..'); describe('Privacy', () => { - describe('redactSessionID', () => { - it('should redact all session IDs', () => { + describe('redactPhoneNumbers', () => { + it('should redact all phone numbers', () => { const text = - 'This is a log line with a session ID 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827\n' + - 'and another one 05766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab62'; + 'This is a log line with a phone number +12223334455\n' + + 'and another one +13334445566'; - const actual = Privacy.redactSessionID(text); + const actual = Privacy.redactPhoneNumbers(text); const expected = - 'This is a log line with a session ID [REDACTED]\n' + - 'and another one [REDACTED]'; + 'This is a log line with a phone number +[REDACTED]455\n' + + 'and another one +[REDACTED]566'; assert.equal(actual, expected); }); - - it('should not redact non session IDS', () => { - const text = - 'This is a log line with a non-session ID sadsad0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827888\n' + - 'and another one 766049a70e725ad02f7fe61b10e461380a4d7433f98096b3cacbf0362d5cab6234'; - - const actual = Privacy.redactSessionID(text); - assert.equal(actual, text); - }); }); describe('redactGroupIds', () => { @@ -62,20 +53,20 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${APP_ROOT_PATH}/main.js\n` + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + `path2 file:///${encodedAppRootPath}/js/background.js.` + - 'phone2 0531033dc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 lorem\n' + + 'phone2 +13334445566 lorem\n' + 'group2 group(abcdefghij) doloret\n'; const actual = Privacy.redactAll(text); const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 [REDACTED] ipsum\n' + + 'phone1 +[REDACTED]455 ipsum\n' + 'group1 group([REDACTED]789) doloret\n' + 'path2 file:///[REDACTED]/js/background.js.' + - 'phone2 [REDACTED] lorem\n' + + 'phone2 +[REDACTED]566 lorem\n' + 'group2 group([REDACTED]hij) doloret\n'; assert.equal(actual, expected); }); @@ -87,13 +78,13 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${testPath}/main.js\n` + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n'; + 'phone1 +12223334455 ipsum\n'; const actual = Privacy._redactPath(testPath)(text); const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n'; + 'phone1 +12223334455 ipsum\n'; assert.equal(actual, expected); }); @@ -103,7 +94,7 @@ describe('Privacy', () => { const text = 'This is a log line with sensitive information:\n' + `path1 ${testPath}/main.js\n` + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + `path2 file:///${encodedTestPath}/js/background.js.`; @@ -111,7 +102,7 @@ describe('Privacy', () => { const expected = 'This is a log line with sensitive information:\n' + 'path1 [REDACTED]/main.js\n' + - 'phone1 0531032fc7415b7cc1b7516480ad121d391eddce3cfb2cee27dd5b215609c32827 ipsum\n' + + 'phone1 +12223334455 ipsum\n' + 'group1 group(123456789) doloret\n' + 'path2 file:///[REDACTED]/js/background.js.'; assert.equal(actual, expected); diff --git a/test/spellcheck_test.js b/test/spellcheck_test.js new file mode 100644 index 000000000..6937d9d9e --- /dev/null +++ b/test/spellcheck_test.js @@ -0,0 +1,12 @@ +describe('spellChecker', () => { + it('should work', () => { + assert( + window.spellChecker.spellCheck('correct'), + 'Spellchecker returned false on a correct word.' + ); + assert( + !window.spellChecker.spellCheck('fhqwgads'), + 'Spellchecker returned true on a incorrect word.' + ); + }); +}); diff --git a/ts/components/DevicePairingDialog.tsx b/ts/components/DevicePairingDialog.tsx index b87d43b6e..4fc0b3ed3 100644 --- a/ts/components/DevicePairingDialog.tsx +++ b/ts/components/DevicePairingDialog.tsx @@ -85,7 +85,6 @@ export class DevicePairingDialog extends React.Component { onClose={this.closeDialog} >
-
{this.renderErrors()} { return ( null} onClose={this.closeDialog} >
-

- {window.i18n('allowPairingWithDevice')} -

{this.renderErrors()} - -
- -
{secretWords}
-
- + +
{secretWords}
{
{this.renderErrors()}

{window.i18n('waitingForDeviceToRegister')}

- {window.i18n('pairNewDevicePrompt')} + + {window.i18n('pairNewDevicePrompt')} +
@@ -197,7 +191,7 @@ export class DevicePairingDialog extends React.Component {

{window.i18n('confirmUnpairingTitle')}
- {description} + {description}

@@ -288,7 +282,6 @@ export class DevicePairingDialog extends React.Component { this.closeDialog(); window.pushToast({ title: window.i18n('devicePairedSuccessfully'), - type: 'success', }); const conv = window.ConversationController.get(this.state.currentPubKey); if (conv) { diff --git a/ts/components/MainViewController.tsx b/ts/components/MainViewController.tsx index e344920c2..2641081fa 100644 --- a/ts/components/MainViewController.tsx +++ b/ts/components/MainViewController.tsx @@ -14,16 +14,9 @@ export const MainViewController = { }, renderSettingsView: (category: SessionSettingCategory) => { - // tslint:disable-next-line: no-backbone-get-set-outside-model - const isSecondaryDevice = !!window.textsecure.storage.get( - 'isSecondaryDevice' - ); if (document.getElementById('main-view')) { ReactDOM.render( - , + , document.getElementById('main-view') ); } diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index 198d06fbd..f0d4e021b 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -419,7 +419,6 @@ export class ConversationHeader extends React.Component { $('.session-search-input input').focus(); } - // tslint:disable-next-line: cyclomatic-complexity private renderPublicMenuItems() { const { i18n, @@ -439,10 +438,9 @@ export class ConversationHeader extends React.Component { timerOptions, onBlockUser, onUnblockUser, - // hasNickname, - // onClearNickname, - // onChangeNickname, - isFriend, + hasNickname, + onClearNickname, + onChangeNickname, } = this.props; if (isPublic || isRss) { @@ -454,7 +452,7 @@ export class ConversationHeader extends React.Component { const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser'); const blockHandler = isBlocked ? onUnblockUser : onBlockUser; - const disappearingMessagesMenuItem = isFriend && ( + const disappearingMessagesMenuItem = ( {(timerOptions || []).map(item => ( { {i18n('showSafetyNumber')} ); - const resetSessionMenuItem = isFriend && - !isGroup && ( - {i18n('resetSession')} - ); + const resetSessionMenuItem = !isGroup && ( + {i18n('resetSession')} + ); const blockHandlerMenuItem = !isMe && !isGroup && !isRss && {blockTitle}; - // const changeNicknameMenuItem = !isMe && - // !isGroup && ( - // {i18n('changeNickname')} - // ); - // const clearNicknameMenuItem = !isMe && - // !isGroup && - // hasNickname && ( - // {i18n('clearNickname')} - // ); + const changeNicknameMenuItem = !isMe && + !isGroup && ( + {i18n('changeNickname')} + ); + const clearNicknameMenuItem = !isMe && + !isGroup && + hasNickname && ( + {i18n('clearNickname')} + ); const archiveConversationMenuItem = isArchived ? ( {i18n('moveConversationToInbox')} @@ -509,8 +506,8 @@ export class ConversationHeader extends React.Component { {showSafetyNumberMenuItem} {resetSessionMenuItem} {blockHandlerMenuItem} - {/* {changeNicknameMenuItem} - {clearNicknameMenuItem} */} + {changeNicknameMenuItem} + {clearNicknameMenuItem} {archiveConversationMenuItem} ); diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 99f1b3d97..c8dd2a98d 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -1116,19 +1116,11 @@ export class Message extends React.PureComponent { expiring ? 'module-message--expired' : null )} role="button" - onClick={event => { + onClick={() => { const selection = window.getSelection(); - // Text is being selected if (selection && selection.type === 'Range') { return; } - - // User clicked on message body - const target = event.target as HTMLDivElement; - if (target.className === 'text-selectable') { - return; - } - this.props.onSelectMessage(); }} > diff --git a/ts/components/conversation/UpdateGroupMembersDialog.tsx b/ts/components/conversation/UpdateGroupMembersDialog.tsx index 00b51ddea..215c52ca7 100644 --- a/ts/components/conversation/UpdateGroupMembersDialog.tsx +++ b/ts/components/conversation/UpdateGroupMembersDialog.tsx @@ -93,7 +93,7 @@ export class UpdateGroupMembersDialog extends React.Component { noFriendsClasses = classNames('no-friends', 'hidden'); } else { // private group - titleText = this.props.titleText; + titleText = `${this.props.titleText} (Members: ${checkMarkedCount})`; noFriendsClasses = this.state.friendList.length === 0 ? 'no-friends' @@ -114,16 +114,6 @@ export class UpdateGroupMembersDialog extends React.Component { onOk={() => null} >
- - {!this.props.isPublic && ( - <> - - {`${checkMarkedCount} members`} - -
- - )} -

{errorMsg}

@@ -134,8 +124,6 @@ export class UpdateGroupMembersDialog extends React.Component { 'noMembersInThisGroup' )})`}

-
-
diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx index 2f8f9c10d..add26cefd 100644 --- a/ts/components/session/ActionsPanel.tsx +++ b/ts/components/session/ActionsPanel.tsx @@ -106,16 +106,26 @@ export class ActionsPanel extends React.Component { default: iconType = SessionIconType.Moon; } - - return ( - - ); + if (!isSelected) { + return ( + + ); + } else { + return ( + + ); + } }; public editProfileHandle() { diff --git a/ts/components/session/LeftPaneSectionHeader.tsx b/ts/components/session/LeftPaneSectionHeader.tsx index 6a8d60bd6..f5f33cddf 100644 --- a/ts/components/session/LeftPaneSectionHeader.tsx +++ b/ts/components/session/LeftPaneSectionHeader.tsx @@ -1,10 +1,6 @@ import React from 'react'; import classNames from 'classnames'; import { SessionButton } from './SessionButton'; -import { - NotificationCountSize, - SessionNotificationCount, -} from './SessionNotificationCount'; const Tab = ({ isSelected, @@ -93,6 +89,8 @@ export class LeftPaneSectionHeader extends React.Component { /> ); } else if (buttonLabel && notificationCount && notificationCount > 0) { + const shortenedNotificationCount = + notificationCount > 9 ? 9 : notificationCount; children.push(
{ key="compose" disabled={false} /> - + role="button" + > + {shortenedNotificationCount} +
); } else if (notificationCount && notificationCount > 0) { + const shortenedNotificationCount = + notificationCount > 9 ? 9 : notificationCount; children.push( - + role="button" + > + {shortenedNotificationCount} +
); } diff --git a/ts/components/session/RegistrationTabs.tsx b/ts/components/session/RegistrationTabs.tsx index a7fb044db..0b5f34fd0 100644 --- a/ts/components/session/RegistrationTabs.tsx +++ b/ts/components/session/RegistrationTabs.tsx @@ -39,7 +39,6 @@ interface State { passwordErrorString: string; passwordFieldsMatch: boolean; mnemonicSeed: string; - generatedMnemonicSeed: string; hexGeneratedPubKey: string; primaryDevicePubKey: string; mnemonicError: string | undefined; @@ -114,7 +113,6 @@ export class RegistrationTabs extends React.Component<{}, State> { passwordErrorString: '', passwordFieldsMatch: false, mnemonicSeed: '', - generatedMnemonicSeed: '', hexGeneratedPubKey: '', primaryDevicePubKey: '', mnemonicError: undefined, @@ -127,11 +125,39 @@ export class RegistrationTabs extends React.Component<{}, State> { window.textsecure.storage.remove('secondaryDeviceStatus'); } - public componentDidMount() { + public render() { this.generateMnemonicAndKeyPair().ignore(); + + return this.renderTabs(); } - public render() { + private async generateMnemonicAndKeyPair() { + if (this.state.mnemonicSeed === '') { + const language = 'english'; + const mnemonic = await this.accountManager.generateMnemonic(language); + + let seedHex = window.mnemonic.mn_decode(mnemonic, language); + // handle shorter than 32 bytes seeds + const privKeyHexLength = 32 * 2; + if (seedHex.length !== privKeyHexLength) { + seedHex = seedHex.concat(seedHex); + seedHex = seedHex.substring(0, privKeyHexLength); + } + const seed = window.dcodeIO.ByteBuffer.wrap( + seedHex, + 'hex' + ).toArrayBuffer(); + const keyPair = await window.libsignal.Curve.async.createKeyPair(seed); + const hexGeneratedPubKey = Buffer.from(keyPair.pubKey).toString('hex'); + + this.setState({ + mnemonicSeed: mnemonic, + hexGeneratedPubKey, // our 'frontend' sessionID + }); + } + } + + private renderTabs() { const { selectedTab } = this.state; const createAccount = window.i18n('createAccount'); @@ -160,32 +186,6 @@ export class RegistrationTabs extends React.Component<{}, State> { ); } - private async generateMnemonicAndKeyPair() { - if (this.state.generatedMnemonicSeed === '') { - const language = 'english'; - const mnemonic = await this.accountManager.generateMnemonic(language); - - let seedHex = window.mnemonic.mn_decode(mnemonic, language); - // handle shorter than 32 bytes seeds - const privKeyHexLength = 32 * 2; - if (seedHex.length !== privKeyHexLength) { - seedHex = seedHex.concat(seedHex); - seedHex = seedHex.substring(0, privKeyHexLength); - } - const seed = window.dcodeIO.ByteBuffer.wrap( - seedHex, - 'hex' - ).toArrayBuffer(); - const keyPair = await window.libsignal.Curve.async.createKeyPair(seed); - const hexGeneratedPubKey = Buffer.from(keyPair.pubKey).toString('hex'); - - this.setState({ - generatedMnemonicSeed: mnemonic, - hexGeneratedPubKey, // our 'frontend' sessionID - }); - } - } - private readonly handleTabSelect = (tabType: TabType): void => { if (tabType !== TabType.SignIn) { this.cancelSecondaryDevice().ignore(); @@ -200,6 +200,7 @@ export class RegistrationTabs extends React.Component<{}, State> { passwordErrorString: '', passwordFieldsMatch: false, mnemonicSeed: '', + hexGeneratedPubKey: '', primaryDevicePubKey: '', mnemonicError: undefined, displayNameError: undefined, @@ -730,19 +731,15 @@ export class RegistrationTabs extends React.Component<{}, State> { const { password, mnemonicSeed, - generatedMnemonicSeed, - signInMode, displayName, passwordErrorString, passwordFieldsMatch, } = this.state; // Make sure the password is valid - window.log.info('starting registration'); const trimName = displayName.trim(); if (!trimName) { - window.log.warn('invalid trimmed name for registration'); window.pushToast({ title: window.i18n('displayNameEmpty'), type: 'error', @@ -753,7 +750,6 @@ export class RegistrationTabs extends React.Component<{}, State> { } if (passwordErrorString) { - window.log.warn('invalid password for registration'); window.pushToast({ title: window.i18n('invalidPassword'), type: 'error', @@ -764,8 +760,6 @@ export class RegistrationTabs extends React.Component<{}, State> { } if (!!password && !passwordFieldsMatch) { - window.log.warn('passwords does not match for registration'); - window.pushToast({ title: window.i18n('passwordsDoNotMatch'), type: 'error', @@ -775,48 +769,28 @@ export class RegistrationTabs extends React.Component<{}, State> { return; } - if (signInMode === SignInMode.UsingSeed && !mnemonicSeed) { - window.log.warn('empty mnemonic seed passed in seed restoration mode'); - - return; - } else if (!generatedMnemonicSeed) { - window.log.warn('empty generated seed'); - + if (!mnemonicSeed) { return; } // Ensure we clear the secondary device registration status window.textsecure.storage.remove('secondaryDeviceStatus'); - const seedToUse = - signInMode === SignInMode.UsingSeed - ? mnemonicSeed - : generatedMnemonicSeed; - try { await this.resetRegistration(); await window.setPassword(password); await this.accountManager.registerSingleDevice( - seedToUse, + mnemonicSeed, language, trimName ); trigger('openInbox'); } catch (e) { - window.pushToast({ - title: `Error: ${e.message || 'Something went wrong'}`, - type: 'error', - id: 'registrationError', - }); - let exmsg = ''; - if (e.message) { - exmsg += e.message; - } - if (e.stack) { - exmsg += ` | stack: + ${e.stack}`; + if (typeof e === 'string') { + //this.showToast(e); } - window.log.warn('exception during registration:', exmsg); + //this.log(e); } } @@ -830,12 +804,8 @@ export class RegistrationTabs extends React.Component<{}, State> { } private async registerSecondaryDevice() { - window.log.warn('starting registerSecondaryDevice'); - // tslint:disable-next-line: no-backbone-get-set-outside-model if (window.textsecure.storage.get('secondaryDeviceStatus') === 'ongoing') { - window.log.warn('registering secondary device already ongoing'); - return; } this.setState({ diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index e86ed1ef0..de9119337 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -176,8 +176,9 @@ export class SessionClosableOverlay extends React.Component { value={this.state.groupName} maxLength={window.CONSTANTS.MAX_GROUPNAME_LENGTH} onChange={this.onGroupNameChanged} - onPressEnter={() => onButtonClick(this.state.groupName)} /> + + {/* */}
) : ( { const messageSubText = messageSub ? 'session-confirm-main-message' - : 'subtle'; + : 'text-subtle'; return ( {
{message} {messageSub && ( - + {messageSub} )} diff --git a/ts/components/session/SessionGroupSettings.tsx b/ts/components/session/SessionGroupSettings.tsx index 919cf0876..123f17d23 100644 --- a/ts/components/session/SessionGroupSettings.tsx +++ b/ts/components/session/SessionGroupSettings.tsx @@ -240,7 +240,7 @@ export class SessionGroupSettings extends React.Component { {showMemberCount && ( <>
-
+
{window.i18n('members', memberCount)}
diff --git a/ts/components/session/SessionIdEditable.tsx b/ts/components/session/SessionIdEditable.tsx index 6fca9f63e..cf2f8da9d 100644 --- a/ts/components/session/SessionIdEditable.tsx +++ b/ts/components/session/SessionIdEditable.tsx @@ -45,7 +45,6 @@ export class SessionIdEditable extends React.PureComponent { spellCheck={false} onKeyDown={this.handleKeyDown} onChange={this.handleChange} - onBlur={this.handleChange} value={value || text} maxLength={maxLength} /> @@ -62,7 +61,7 @@ export class SessionIdEditable extends React.PureComponent { private handleKeyDown(e: any) { const { editable, onPressEnter } = this.props; - if (editable && e.key === 'Enter') { + if (editable && e.keyCode === 13) { e.preventDefault(); // tslint:disable-next-line: no-unused-expression onPressEnter && onPressEnter(); diff --git a/ts/components/session/SessionInput.tsx b/ts/components/session/SessionInput.tsx index edd8c64ed..11432e80a 100644 --- a/ts/components/session/SessionInput.tsx +++ b/ts/components/session/SessionInput.tsx @@ -61,10 +61,6 @@ export class SessionInput extends React.PureComponent { className={classNames( enableShowHide ? 'session-input-floating-label-show-hide' : '' )} - // just incase onChange isn't triggered - onBlur={e => { - this.updateInputValue(e); - }} onKeyPress={event => { event.persist(); if (event.key === 'Enter' && this.props.onEnterPressed) { diff --git a/ts/components/session/SessionNotificationCount.tsx b/ts/components/session/SessionNotificationCount.tsx deleted file mode 100644 index 97f565189..000000000 --- a/ts/components/session/SessionNotificationCount.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; - -export enum NotificationCountSize { - // Size in px - ON_ICON = 20, - ON_HEADER = 24, -} - -interface Props { - count?: number; - size: number; - onClick?: any; -} - -export class SessionNotificationCount extends React.Component { - public static defaultProps = { - size: NotificationCountSize.ON_ICON, - }; - - constructor(props: any) { - super(props); - } - - public render() { - const { count, size, onClick } = this.props; - - const hasHover = !!onClick; - - const MAX_SINGLE_DIGIT = 9; - const overflow = typeof count === 'number' && count > MAX_SINGLE_DIGIT; - - const fontSizeVal = overflow ? size / 2 : size / 2 + 2; - const fontSize = `${fontSizeVal}px`; - - const bubbleStyle = { - width: `${size}px`, - height: `${size}px`, - }; - - const countStyle = { - fontSize, - marginTop: overflow ? `${size / 8}px` : '0px', - marginLeft: overflow ? `-${size / 4}px` : '0px', - }; - - const supStyle = { - top: `-${size * (3 / 8)}px`, - }; - - const countElement: JSX.Element = overflow ? ( - <> - {MAX_SINGLE_DIGIT} - + - - ) : ( - <>{count} - ); - - const shouldRender = typeof count === 'number' && count > 0; - - return ( - <> - {shouldRender && ( -
- {countElement} -
- )} - - ); - } -} diff --git a/ts/components/session/SessionPasswordModal.tsx b/ts/components/session/SessionPasswordModal.tsx index 4d7091057..8808b3f16 100644 --- a/ts/components/session/SessionPasswordModal.tsx +++ b/ts/components/session/SessionPasswordModal.tsx @@ -20,9 +20,6 @@ interface State { } export class SessionPasswordModal extends React.Component { - private readonly passwordInput: React.RefObject; - private readonly passwordInputConfirm: React.RefObject; - constructor(props: any) { super(props); @@ -36,20 +33,10 @@ export class SessionPasswordModal extends React.Component { this.closeDialog = this.closeDialog.bind(this); this.onKeyUp = this.onKeyUp.bind(this); - this.onPaste = this.onPaste.bind(this); - - this.passwordInput = React.createRef(); - this.passwordInputConfirm = React.createRef(); } public componentDidMount() { - setTimeout(() => { - if (!this.passwordInput.current) { - return; - } - - this.passwordInput.current.focus(); - }, 100); + setTimeout(() => $('#password-modal-input').focus(), 100); } public render() { @@ -76,21 +63,17 @@ export class SessionPasswordModal extends React.Component { {action !== PasswordAction.Remove && ( )}
@@ -140,21 +123,16 @@ export class SessionPasswordModal extends React.Component { } private async setPassword(onSuccess: any) { - if (!this.passwordInput.current || !this.passwordInputConfirm.current) { - return; - } - - // Trim leading / trailing whitespace for UX - const enteredPassword = String(this.passwordInput.current.value).trim(); + const enteredPassword = String($('#password-modal-input').val()); const enteredPasswordConfirm = String( - this.passwordInputConfirm.current.value - ).trim(); + $('#password-modal-input-confirm').val() + ); if (enteredPassword.length === 0 || enteredPasswordConfirm.length === 0) { return; } - // Check passwords entered + // Check passwords enntered if ( enteredPassword.length === 0 || (this.props.action === PasswordAction.Change && @@ -213,27 +191,6 @@ export class SessionPasswordModal extends React.Component { } } - private onPaste(event: any) { - const clipboard = event.clipboardData.getData('text'); - - if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) { - const title = String( - window.i18n( - 'pasteLongPasswordToastTitle', - window.CONSTANTS.MAX_PASSWORD_LENGTH - ) - ); - - window.pushToast({ - title, - type: 'warning', - }); - } - - // Prevent pating into input - return false; - } - private async onKeyUp(event: any) { const { onOk } = this.props; diff --git a/ts/components/session/SessionPasswordPrompt.tsx b/ts/components/session/SessionPasswordPrompt.tsx index 01bb0f9e1..cd30d3e53 100644 --- a/ts/components/session/SessionPasswordPrompt.tsx +++ b/ts/components/session/SessionPasswordPrompt.tsx @@ -15,8 +15,6 @@ interface State { } export class SessionPasswordPrompt extends React.PureComponent<{}, State> { - private readonly inputRef: React.RefObject; - constructor(props: any) { super(props); @@ -27,16 +25,12 @@ export class SessionPasswordPrompt extends React.PureComponent<{}, State> { }; this.onKeyUp = this.onKeyUp.bind(this); - this.onPaste = this.onPaste.bind(this); - this.initLogin = this.initLogin.bind(this); this.initClearDataView = this.initClearDataView.bind(this); - - this.inputRef = React.createRef(); } public componentDidMount() { - (this.inputRef.current as HTMLInputElement).focus(); + setTimeout(() => $('#password-prompt-input').focus(), 100); } public render() { @@ -68,8 +62,6 @@ export class SessionPasswordPrompt extends React.PureComponent<{}, State> { placeholder={' '} onKeyUp={this.onKeyUp} maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH} - onPaste={this.onPaste} - ref={this.inputRef} /> ); const infoIcon = this.state.clearDataView ? ( @@ -128,43 +120,23 @@ export class SessionPasswordPrompt extends React.PureComponent<{}, State> { event.preventDefault(); } - public onPaste(event: any) { - const clipboard = event.clipboardData.getData('text'); - - if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) { - this.setState({ - error: String( - window.i18n( - 'pasteLongPasswordToastTitle', - window.CONSTANTS.MAX_PASSWORD_LENGTH - ) - ), - }); - } - - // Prevent pasting into input - return false; - } - public async onLogin(passPhrase: string) { - const passPhraseTrimmed = passPhrase.trim(); + const trimmed = passPhrase ? passPhrase.trim() : passPhrase; try { - await window.onLogin(passPhraseTrimmed); - } catch (error) { + await window.onLogin(trimmed); + } catch (e) { // Increment the error counter and show the button if necessary this.setState({ errorCount: this.state.errorCount + 1, }); - this.setState({ error }); + this.setState({ error: e }); } } private async initLogin() { - const passPhrase = String( - (this.inputRef.current as HTMLInputElement).value - ); + const passPhrase = String($('#password-prompt-input').val()); await this.onLogin(passPhrase); } diff --git a/ts/components/session/SessionQRModal.tsx b/ts/components/session/SessionQRModal.tsx index dc71ee4cd..97f613c8d 100644 --- a/ts/components/session/SessionQRModal.tsx +++ b/ts/components/session/SessionQRModal.tsx @@ -29,7 +29,7 @@ export class SessionQRModal extends React.Component { >
-
+
diff --git a/ts/components/session/SessionSeedModal.tsx b/ts/components/session/SessionSeedModal.tsx index ed5fdb242..e9d461bd9 100644 --- a/ts/components/session/SessionSeedModal.tsx +++ b/ts/components/session/SessionSeedModal.tsx @@ -121,7 +121,7 @@ export class SessionSeedModal extends React.Component {

{i18n('seedSavePromptMain')}
- {i18n('seedSavePromptAlt')} + {i18n('seedSavePromptAlt')}

diff --git a/ts/components/session/icon/SessionIconButton.tsx b/ts/components/session/icon/SessionIconButton.tsx index 60dfb083d..a0b0f5cd4 100644 --- a/ts/components/session/icon/SessionIconButton.tsx +++ b/ts/components/session/icon/SessionIconButton.tsx @@ -1,12 +1,11 @@ import React from 'react'; import classNames from 'classnames'; -import { Props, SessionIcon } from '../icon'; -import { SessionNotificationCount } from '../SessionNotificationCount'; +import { Props, SessionIcon } from '../icon'; interface SProps extends Props { onClick: any; - notificationCount?: number; + notificationCount: number | undefined; isSelected: boolean; } @@ -36,7 +35,13 @@ export class SessionIconButton extends React.PureComponent { isSelected, } = this.props; - const { notificationCount } = this.props; + let { notificationCount } = this.props; + + if (notificationCount === 0) { + notificationCount = undefined; + } else if (notificationCount !== undefined && notificationCount > 9) { + notificationCount = 9; + } return (
{ iconColor={iconColor} iconRotation={iconRotation} /> - + {notificationCount !== undefined && ( + {notificationCount} + )}
); } diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index 9660fc0a4..01ab4dd16 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -27,7 +27,6 @@ export enum SessionSettingType { export interface SettingsViewProps { category: SessionSettingCategory; - isSecondaryDevice: boolean; } interface State { @@ -116,14 +115,9 @@ export class SettingsView extends React.Component { const description = setting.description || ''; const comparisonValue = setting.comparisonValue || null; - const storedSetting = window.getSettingValue( - setting.id, - comparisonValue - ); const value = - storedSetting !== undefined - ? storedSetting - : setting.content && setting.content.defaultValue; + window.getSettingValue(setting.id, comparisonValue) || + (setting.content && setting.content.defaultValue); const sliderFn = setting.type === SessionSettingType.Slider @@ -227,7 +221,7 @@ export class SettingsView extends React.Component { } public render() { - const { category, isSecondaryDevice } = this.props; + const { category } = this.props; const shouldRenderPasswordLock = this.state.shouldLockSettings && this.state.hasPassword; @@ -236,7 +230,6 @@ export class SettingsView extends React.Component {
@@ -361,7 +354,7 @@ export class SettingsView extends React.Component { type: SessionSettingType.Toggle, category: SessionSettingCategory.Appearance, setFn: window.toggleSpellCheck, - content: { defaultValue: true }, + content: undefined, comparisonValue: undefined, onClick: undefined, confirmationDialogParams: undefined, @@ -581,10 +574,6 @@ export class SettingsView extends React.Component { private getLinkedDeviceSettings(): Array { const { linkedPubKeys } = this.state; - const { isSecondaryDevice } = this.props; - const noPairedDeviceText = isSecondaryDevice - ? window.i18n('deviceIsSecondaryNoPairing') - : window.i18n('noPairedDevices'); if (linkedPubKeys && linkedPubKeys.length > 0) { return linkedPubKeys.map((pubkey: any) => { @@ -632,7 +621,7 @@ export class SettingsView extends React.Component { return [ { id: 'no-linked-device', - title: noPairedDeviceText, + title: window.i18n('noPairedDevices'), type: undefined, description: '', category: SessionSettingCategory.Devices, diff --git a/ts/components/session/settings/SessionSettingsHeader.tsx b/ts/components/session/settings/SessionSettingsHeader.tsx index 2c14e0dbf..b5dc93df0 100644 --- a/ts/components/session/settings/SessionSettingsHeader.tsx +++ b/ts/components/session/settings/SessionSettingsHeader.tsx @@ -5,23 +5,20 @@ import { SessionSettingCategory, SettingsViewProps } from './SessionSettings'; import { SessionButton } from '../SessionButton'; interface Props extends SettingsViewProps { - // showLinkDeviceButton is used to completely hide the button while the settings password lock is displayed showLinkDeviceButton: boolean | null; - // isSecondaryDevice is used to just disable the linkDeviceButton when we are already a secondary device - isSecondaryDevice: boolean; + disableLinkDeviceButton: boolean | null; } export class SettingsHeader extends React.Component { public static defaultProps = { showLinkDeviceButton: false, + disableLinkDeviceButton: true, }; public constructor(props: any) { super(props); - // mark the linkDeviceButton as disabled by default. - // it will be enabled if needed during componentDidMount(). this.state = { - disableLinkDeviceButton: true, + disableLinkDeviceButton: this.props.disableLinkDeviceButton, }; this.showAddLinkedDeviceModal = this.showAddLinkedDeviceModal.bind(this); } @@ -35,12 +32,10 @@ export class SettingsHeader extends React.Component { } public componentDidMount() { - if (!this.props.isSecondaryDevice) { - window.Whisper.events.on('refreshLinkedDeviceList', async () => { - this.refreshLinkedDevice(); - }); + window.Whisper.events.on('refreshLinkedDeviceList', async () => { this.refreshLinkedDevice(); - } + }); + this.refreshLinkedDevice(); } public refreshLinkedDevice() { @@ -56,9 +51,7 @@ export class SettingsHeader extends React.Component { } public componentWillUnmount() { - if (!this.props.isSecondaryDevice) { - window.Whisper.events.off('refreshLinkedDeviceList'); - } + window.Whisper.events.off('refreshLinkedDeviceList'); } public render() { diff --git a/ts/updater/common.ts b/ts/updater/common.ts index e6bf4df13..e51727af1 100644 --- a/ts/updater/common.ts +++ b/ts/updater/common.ts @@ -37,9 +37,11 @@ export async function showDownloadUpdateDialog( cancelId: DOWNLOAD_BUTTON, }; - const ret = await dialog.showMessageBox(mainWindow, options); - - return ret.response === DOWNLOAD_BUTTON; + return new Promise(resolve => { + dialog.showMessageBox(mainWindow, options, response => { + resolve(response === DOWNLOAD_BUTTON); + }); + }); } export async function showUpdateDialog( @@ -60,22 +62,33 @@ export async function showUpdateDialog( defaultId: LATER_BUTTON, cancelId: RESTART_BUTTON, }; - const ret = await dialog.showMessageBox(mainWindow, options); - return ret.response === RESTART_BUTTON; + return new Promise(resolve => { + dialog.showMessageBox(mainWindow, options, response => { + // It's key to delay any install calls here because they don't seem to work inside this + // callback - but only if the message box has a parent window. + // Fixes this: https://github.com/signalapp/Signal-Desktop/issues/1864 + resolve(response === RESTART_BUTTON); + }); + }); } export async function showCannotUpdateDialog( mainWindow: BrowserWindow, messages: MessagesType -) { +): Promise { const options = { type: 'error', buttons: [messages.ok.message], title: messages.cannotUpdate.message, message: messages.cannotUpdateDetail.message, }; - await dialog.showMessageBox(mainWindow, options); + + return new Promise(resolve => { + dialog.showMessageBox(mainWindow, options, () => { + resolve(); + }); + }); } export function getPrintableError(error: Error) { diff --git a/ts/updater/updater.ts b/ts/updater/updater.ts index f31ddfa12..efd12b1bf 100644 --- a/ts/updater/updater.ts +++ b/ts/updater/updater.ts @@ -55,8 +55,8 @@ export function stop() { if (interval) { clearInterval(interval); interval = undefined; + stopped = true; } - stopped = true; } async function checkForUpdates( diff --git a/ts/util/libphonenumberInstance.ts b/ts/util/libphonenumberInstance.ts new file mode 100644 index 000000000..40855ac23 --- /dev/null +++ b/ts/util/libphonenumberInstance.ts @@ -0,0 +1,6 @@ +import libphonenumber from 'google-libphonenumber'; + +const instance = libphonenumber.PhoneNumberUtil.getInstance(); +const PhoneNumberFormat = libphonenumber.PhoneNumberFormat; + +export { instance, PhoneNumberFormat }; diff --git a/ts/util/lint/linter.ts b/ts/util/lint/linter.ts index a0773fbc6..1ada85d60 100644 --- a/ts/util/lint/linter.ts +++ b/ts/util/lint/linter.ts @@ -122,6 +122,7 @@ const excludedFiles = [ '^node_modules/hpack\\.js/*', '^node_modules/http-proxy-middlewar/*', '^node_modules/icss-utils/*', + '^node_modules/intl-tel-input/examples/*', '^node_modules/istanbul*', '^node_modules/jimp/*', '^node_modules/jquery/*', @@ -160,6 +161,7 @@ const excludedFiles = [ '^node_modules/spectron/*', '^node_modules/style-loader/*', '^node_modules/svgo/*', + '^node_modules/testcheck/*', '^node_modules/text-encoding/*', '^node_modules/tinycolor2/*', '^node_modules/to-ast/*', diff --git a/yarn.lock b/yarn.lock index f49865784..9cb509508 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,16 +8,16 @@ integrity sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA== "@babel/parser@^7.1.3": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4" - integrity sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA== + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" + integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" - integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1" + integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.2" "@develar/schema-utils@~2.1.0": version "2.1.0" @@ -27,22 +27,6 @@ ajv "^6.1.0" ajv-keywords "^3.1.0" -"@electron/get@^1.0.1": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.9.0.tgz#7fa6e61d7ff50fb82a8a41f437af7de3b97aa9a5" - integrity sha512-OBIKtF6ttIJotDXe4KJMUyTBO4xMii+mFjlA8R4CORuD4HvCUaCK3lPjhdTRCvuEv6gzWNbAvd9DNBv0v780lw== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - sanitize-filename "^1.6.2" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^2.0.2" - global-tunnel-ng "^2.7.1" - "@journeyapps/sqlcipher@https://github.com/scottnonnenberg-signal/node-sqlcipher.git#b10f232fac62ba7f8775c9e086bb5558fe7d948b": version "4.0.0" resolved "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#b10f232fac62ba7f8775c9e086bb5558fe7d948b" @@ -113,10 +97,15 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" + integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" - integrity sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ== + version "1.7.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.0.tgz#f90ffc52a2e519f018b13b6c4da03cbff36ebed6" + integrity sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg== dependencies: type-detect "4.0.8" @@ -226,10 +215,10 @@ dependencies: "@types/node" "*" -"@types/fs-extra@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.0.tgz#1114834b53c3914806cd03b3304b37b3bd221a4d" - integrity sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg== +"@types/fs-extra@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.0.1.tgz#a2378d6e7e8afea1564e44aafa2e207dadf77686" + integrity sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw== dependencies: "@types/node" "*" @@ -242,6 +231,19 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/google-libphonenumber@7.4.14": + version "7.4.14" + resolved "https://registry.yarnpkg.com/@types/google-libphonenumber/-/google-libphonenumber-7.4.14.tgz#3625d7aed0c16df920588428c86f0538bd0612ec" + integrity sha512-HGQobTn8CGn0/j7lezIHlGTE1czxQU6NeT7TYtC3OWcbyyUUSfdt4tXSTEGbXmNZlE+xczTDbyR8P6aYGPzG1A== + +"@types/got@9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@types/got/-/got-9.4.1.tgz#7479a3a321599b5e17647f3bd9d402b8c55bfee1" + integrity sha512-m7Uc07bG/bZ+Dis7yI3mGssYDcAdUvP4irF3ZmBzf0ig7zEd1FyADfnELVGcf+p1Ol/iPCXbZYwcSNOJA2a+Qg== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + "@types/jquery@3.3.29": version "3.3.29" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd" @@ -249,6 +251,11 @@ dependencies: "@types/sizzle" "*" +"@types/js-yaml@3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.0.tgz#3494ce97358e2675e24e97a747ec23478eeaf8b6" + integrity sha512-UGEe/6RsNAxgWdknhzFZbCxuYc5I7b/YEKlfKbo+76SM8CJzGs7XKCj7zyugXViRbKYpXhSXhCYVQZL5tmDbpQ== + "@types/linkify-it@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.0.3.tgz#5352a2d7a35d7c77b527483cd6e68da9148bd780" @@ -282,14 +289,14 @@ integrity sha512-ZS0vBV7Jn5Z/Q4T3VXauEKMDCV8nWOtJJg90OsDylkYJiQwcWtKuLzohWzrthBkerUF7DLMmJcwOPEP0i/AOXw== "@types/node@*": - version "13.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.1.tgz#96f606f8cd67fb018847d9b61e93997dabdefc72" - integrity sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ== + version "13.1.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.6.tgz#076028d0b0400be8105b89a0a55550c86684ffec" + integrity sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg== -"@types/node@^12.0.12": - version "12.12.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.31.tgz#d6b4f9645fee17f11319b508fb1001797425da51" - integrity sha512-T+wnJno8uh27G9c+1T+a1/WYCHzLeDqtsGJkoEdSp2X8RTh3oOCZQcUnjAx90CS8cmmADX51O0FI/tu9s0yssg== +"@types/node@^10.12.18": + version "10.17.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" + integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== "@types/node@^8.9.4": version "8.10.59" @@ -357,9 +364,9 @@ "@types/react" "*" "@types/react@*": - version "16.9.23" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c" - integrity sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw== + version "16.9.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.17.tgz#58f0cc0e9ec2425d1441dd7b623421a867aa253e" + integrity sha512-UP27In4fp4sWF5JgyV6pwVPAQM83Fj76JOcg02X5BZcpSu5Wx+fP9RMqc2v0ssBoQIFvD5JdKY41gjJJKmw6Bg== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -409,6 +416,11 @@ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== +"@types/tough-cookie@*": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.6.tgz#c880579e087d7a0db13777ff8af689f4ffc7b0d5" + integrity sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ== + "@types/trusted-types@*": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.4.tgz#922d092c84a776a59acb0bd6785fd82b59b9bad5" @@ -421,25 +433,6 @@ dependencies: "@types/node" "*" -"@types/webdriverio@^4.8.0": - version "4.13.3" - resolved "https://registry.yarnpkg.com/@types/webdriverio/-/webdriverio-4.13.3.tgz#c1571c4e62724135c0b11e7d7e36b07af5168856" - integrity sha512-AfSQM1xTO9Ax+u9uSQPDuw69DQ0qA2RMoKHn86jCgWNcwKVUjGMSP4sfSl3JOfcZN8X/gWvn7znVPp2/g9zcJA== - dependencies: - "@types/node" "*" - -"@types/yargs-parser@*": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" - integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== - -"@types/yargs@^15.0.4": - version "15.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" - integrity sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg== - dependencies: - "@types/yargs-parser" "*" - "@vxna/mini-html-webpack-template@^0.1.6": version "0.1.7" resolved "https://registry.yarnpkg.com/@vxna/mini-html-webpack-template/-/mini-html-webpack-template-0.1.7.tgz#2a8270e513ee14f395cc17c2ce22ced383c45d22" @@ -480,9 +473,9 @@ acorn-jsx@^3.0.0: acorn "^3.0.4" acorn-jsx@^5.0.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== acorn@^3.0.4: version "3.3.0" @@ -490,14 +483,14 @@ acorn@^3.0.4: integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= acorn@^5.0.0, acorn@^5.5.0, acorn@^5.5.3: - version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" - integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== acorn@^6.1.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" - integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + version "6.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" + integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== add-dom-event-listener@^1.1.0: version "1.1.0" @@ -516,6 +509,20 @@ address@^1.0.1: resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== +agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" @@ -532,11 +539,11 @@ ajv@^5.1.0, ajv@^5.3.0: json-schema-traverse "^0.3.0" ajv@^6.0.1, ajv@^6.1.0, ajv@^6.5.5: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== dependencies: - fast-deep-equal "^3.1.1" + fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" @@ -608,7 +615,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" -any-promise@^1.0.0: +any-promise@^1.0.0, any-promise@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= @@ -621,38 +628,66 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -app-builder-bin@3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.4.tgz#2caa9a95358e1f02b7fed5ac47613ab878328de2" - integrity sha512-Bta3I+OU64ePwvSdhpYyCj11XcR8VGdlPSINM87LPg0apCbprLu03zvnmnHRw/RvZB2RGAQ93Z8NO3JT3DbXdA== +app-builder-bin@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.2.tgz#fba56e6e9ef76fcd37816738c5f9a0b3992d7183" + integrity sha512-tYeNHp8js5c5MT+mzGEMsZhDBXRVAkCgrvz80rzNLlCojG6YrNLRSzfCWZiKJCv5W2faHjxTkKQoRAMY3RWaow== -app-builder-lib@22.3.6, app-builder-lib@~22.3.6: - version "22.3.6" - resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-22.3.6.tgz#982c5be22664123a7535bc29624180371f8d5333" - integrity sha512-9TANMAFL7cFNuRB3cMc1BS5dBQzqY5RVEUD4VMZ0MYhFY/DM9+v2BvfY3Ef+7XSOzirA6p4TljAVz5ydIEdZFw== +app-builder-lib@22.3.2: + version "22.3.2" + resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-22.3.2.tgz#d43e0bdff91d484c0bd07d7248043dbb2665b8ac" + integrity sha512-QHaDelJUP3R+HUmnQJzHvjlCCJGG6t0bcFSZTOtUx+44B42VxzKpxtoG55xRFbyrU5l2++n7dQqEZAGk8iL6Qg== dependencies: "7zip-bin" "~5.0.3" "@develar/schema-utils" "~2.1.0" async-exit-hook "^2.0.1" bluebird-lst "^1.0.9" - builder-util "22.3.6" - builder-util-runtime "8.6.1" + builder-util "22.3.2" + builder-util-runtime "8.6.0" chromium-pickle-js "^0.2.0" debug "^4.1.1" ejs "^3.0.1" - electron-publish "22.3.6" + electron-publish "22.3.2" fs-extra "^8.1.0" - hosted-git-info "^3.0.4" + hosted-git-info "^3.0.2" is-ci "^2.0.0" isbinaryfile "^4.0.4" js-yaml "^3.13.1" lazy-val "^1.0.4" minimatch "^3.0.4" normalize-package-data "^2.5.0" - read-config-file "5.0.2" + read-config-file "5.0.1" sanitize-filename "^1.6.3" - semver "^7.1.3" - temp-file "^3.3.7" + semver "^7.1.1" + temp-file "^3.3.6" + +app-builder-lib@~22.3.2: + version "22.3.3" + resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-22.3.3.tgz#9a95a3c14f69fb6131834dd840fba561191c9998" + integrity sha512-zZJyuF3djIA5K6tbx8t3w40M0iVoBR6K2k4KMHOu96+ffmfvdlu+UrsvDqvP1N1cgwFoSSyvW/Hg9/SP12pnEQ== + dependencies: + "7zip-bin" "~5.0.3" + "@develar/schema-utils" "~2.1.0" + async-exit-hook "^2.0.1" + bluebird-lst "^1.0.9" + builder-util "22.3.3" + builder-util-runtime "8.6.0" + chromium-pickle-js "^0.2.0" + debug "^4.1.1" + ejs "^3.0.1" + electron-publish "22.3.3" + fs-extra "^8.1.0" + hosted-git-info "^3.0.2" + is-ci "^2.0.0" + isbinaryfile "^4.0.4" + js-yaml "^3.13.1" + lazy-val "^1.0.4" + minimatch "^3.0.4" + normalize-package-data "^2.5.0" + read-config-file "5.0.1" + sanitize-filename "^1.6.3" + semver "^7.1.1" + temp-file "^3.3.6" append-transform@^0.4.0: version "0.4.0" @@ -886,11 +921,21 @@ ast-types@0.9.11: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.11.tgz#371177bb59232ff5ceaa1d09ee5cad705b1a5aa9" integrity sha1-NxF3u1kjL/XOqh0J7lytcFsaWqk= +ast-types@0.x.x: + version "0.13.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== + ast-types@^0.7.2: version "0.7.8" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" integrity sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -928,11 +973,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -1047,9 +1087,9 @@ backbone@1.3.3: underscore ">=1.8.3" bail@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" - integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.4.tgz#7181b66d508aa3055d3f6c13f0a0c720641dde9b" + integrity sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww== balanced-match@^0.4.2: version "0.4.2" @@ -1225,11 +1265,6 @@ bonjour@^3.5.0: multicast-dns "^6.0.1" multicast-dns-service-types "^1.1.0" -boolean@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f" - integrity sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA== - bower@1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/bower/-/bower-1.8.2.tgz#adf53529c8d4af02ef24fb8d5341c1419d33e2f7" @@ -1386,7 +1421,7 @@ buffer-alloc@^1.2.0: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" -buffer-crc32@0.2.13, buffer-crc32@^0.2.1, buffer-crc32@~0.2.3: +buffer-crc32@0.2.13, buffer-crc32@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= @@ -1426,9 +1461,9 @@ buffer@^4.3.0: isarray "^1.0.0" buffer@^5.1.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce" - integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== + version "5.4.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" + integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -1438,33 +1473,44 @@ buffers@~0.1.1: resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= -builder-util-runtime@8.6.1: - version "8.6.1" - resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.1.tgz#cf9a268fa51704de24f3c085aa8d1d1b3767d9ea" - integrity sha512-gwIUtMaICmc+e2EC3u3byXcwCyfhtG40LJRNnGfs8AYqacKl4ZLP50ab+uDttn7QAXe0LfMAuKz9v8bCODV0yg== +builder-util-runtime@8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.0.tgz#b7007c30126da9a90e99932128d2922c8c178649" + integrity sha512-WTDhTUVrm7zkFyd6Qn7AXgmWifjpZ/fYnEdV3XCOIDMNNb/KPddBTbQ8bUlxxVeuOYlhGpcLUypG+4USdGL1ww== dependencies: debug "^4.1.1" sax "^1.2.4" -builder-util-runtime@8.6.2: - version "8.6.2" - resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.6.2.tgz#8270e15b012d8d3b110f3e327b0fd8b0e07b1686" - integrity sha512-9QnIBISfhgQ2BxtRLidVqf/v5HD73vSKZDllpUmGd2L6VORGQk7cZAPmPtw4HQM3gPBelyVJ5yIjMNZ8xjmd1A== +builder-util@22.3.2: + version "22.3.2" + resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.3.2.tgz#23c61aaf0f0006f994087b33a26e47cdaec7aa8d" + integrity sha512-jNeyA/AgyujE6NQLoEkERY/qrilFdCFAC6cBM1ylSnRuzlD1tQQCI3QJhuMJG/gLUCgyNsfFQhN0Mw6IqsDAqw== dependencies: + "7zip-bin" "~5.0.3" + "@types/debug" "^4.1.5" + app-builder-bin "3.5.2" + bluebird-lst "^1.0.9" + builder-util-runtime "8.6.0" + chalk "^3.0.0" debug "^4.1.1" - sax "^1.2.4" + fs-extra "^8.1.0" + is-ci "^2.0.0" + js-yaml "^3.13.1" + source-map-support "^0.5.16" + stat-mode "^1.0.0" + temp-file "^3.3.6" -builder-util@22.3.6, builder-util@~22.3.6: - version "22.3.6" - resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.3.6.tgz#a1941eec1606cf079d94c1e474109949a39818a9" - integrity sha512-1fMRRouGzggUcPcy/ZR2NV8KZejwXPYNRD4vr/Ws/KvGQFNMTHA9VFscT7tGfOseQCLaLsa5C2gU5MOAiFx7AA== +builder-util@22.3.3, builder-util@~22.3.2, builder-util@~22.3.3: + version "22.3.3" + resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.3.3.tgz#62f0527ceaa1a2e4a60596a9b38ad1ffe3e20ae6" + integrity sha512-VzQALenLDdeaz7hXaQgS9N0Xz3zlgkK64Dp2Vn61XTbhI0MgVneTeEKKDFwdBC/l7v0cHsOPeao/xeWmyznC2g== dependencies: "7zip-bin" "~5.0.3" "@types/debug" "^4.1.5" - "@types/fs-extra" "^8.1.0" - app-builder-bin "3.5.4" + "@types/fs-extra" "^8.0.1" + app-builder-bin "3.5.2" bluebird-lst "^1.0.9" - builder-util-runtime "8.6.1" + builder-util-runtime "8.6.0" chalk "^3.0.0" debug "^4.1.1" fs-extra "^8.1.0" @@ -1472,7 +1518,7 @@ builder-util@22.3.6, builder-util@~22.3.6: js-yaml "^3.13.1" source-map-support "^0.5.16" stat-mode "^1.0.0" - temp-file "^3.3.7" + temp-file "^3.3.6" builtin-modules@^1.1.1: version "1.1.1" @@ -1548,6 +1594,19 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" + integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -1621,9 +1680,9 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30001035" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001035.tgz#3a182cab9d556a4a02d945f1f739e81c18e73bfa" - integrity sha512-kLUON4XN3tq5Nwl7ZICDw+7/vMynSpRMVYDRkzLL31lgnpa6M2YXYdjst3h+xbzjMgdcveRTnRGE1h/1IcKK6A== + version "1.0.30001020" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001020.tgz#30c82517d283d6a64df76ea7734fade566e7e5e7" + integrity sha512-KMZ0k2Xg+UiQemGBh+cFKnnFi/oEYZcuGWSb1B0gofoP89ZI/fmjYJ8J9lQb9OnSupdG6cHZSxn+LtFli2Q07w== caseless@~0.12.0: version "0.12.0" @@ -1631,16 +1690,9 @@ caseless@~0.12.0: integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= ccount@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17" - integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw== - -chai-as-promised@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" - integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== - dependencies: - check-error "^1.0.2" + version "1.0.4" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.4.tgz#9cf2de494ca84060a2a8d2854edd6dfb0445f386" + integrity sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w== chai@4.1.2: version "4.1.2" @@ -1690,31 +1742,31 @@ chalk@^3.0.0: supports-color "^7.1.0" character-entities-html4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" - integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== + version "1.1.3" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.3.tgz#5ce6e01618e47048ac22f34f7f39db5c6fd679ef" + integrity sha512-SwnyZ7jQBCRHELk9zf2CN5AnGEc2nA+uKMZLHvcqhpPprjkYhiLn0DywMHgN5ttFZuITMATbh68M6VIVKwJbcg== character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + version "1.1.3" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.3.tgz#3c729991d9293da0ede6dddcaf1f2ce1009ee8b4" + integrity sha512-YAxUpPoPwxYFsslbdKkhrGnXAtXoHNgYjlBM3WMXkWGTl5RsY3QmOyhwAgL8Nxm9l5LBThXGawxKPn68y6/fww== character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + version "1.2.3" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.3.tgz#bbed4a52fe7ef98cc713c6d80d9faa26916d54e6" + integrity sha512-yB4oYSAa9yLcGyTbB4ItFwHw43QHdH129IJ5R+WvxOkWlyFnR5FAaBNnUq4mcxsTVZGh28bHoeTHMKXH1wZf3w== character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + version "1.1.3" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.3.tgz#1647f4f726638d3ea4a750cf5d1975c1c7919a85" + integrity sha512-VOq6PRzQBam/8Jm6XBGk2fNEnHXAdGd6go0rtd4weAGECBamHDwwCQSOT12TACIYUZegUXnV6xBXqUssijtxIg== chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= -check-error@^1.0.1, check-error@^1.0.2: +check-error@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= @@ -1739,9 +1791,9 @@ chokidar@^2.0.2, chokidar@^2.1.2: fsevents "^1.2.7" chownr@^1.0.1, chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== chrome-trace-event@^0.1.1: version "0.1.3" @@ -1822,6 +1874,14 @@ cli-spinners@^1.1.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== +cli-truncate@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -1859,7 +1919,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-response@^1.0.2: +clone-response@1.0.2, clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= @@ -1889,9 +1949,9 @@ code-point-at@^1.0.0: integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= codemirror@^5.36.0: - version "5.52.0" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.52.0.tgz#4dbd6aef7f0e63db826b9a23922f0c03ac75c0a7" - integrity sha512-K2UB6zjscrfME03HeRe/IuOmCeqNpw7PLKGHThYpLbZEuKf+ZoujJPhxZN4hHJS1O7QyzEsV7JJZGxuQWVaFCg== + version "5.50.2" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.50.2.tgz#32ddfe2b50193fcf573d8141c4a31d267c92b4a3" + integrity sha512-PPjUsC1oXSM86lunKrw609P1oM0Wu8z9rqzjbeyBYCcx44VL41aUpccdOf1PfAZtTONlmN3sT3p2etLNYa1OGg== coffee-script@~1.10.0: version "1.10.0" @@ -1899,9 +1959,9 @@ coffee-script@~1.10.0: integrity sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA= collapse-white-space@^1.0.2: - version "1.0.6" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" - integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.5.tgz#c2495b699ab1ed380d29a1091e01063e75dbbe3a" + integrity sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ== collection-visit@^1.0.0: version "1.0.0" @@ -1986,6 +2046,13 @@ colors@~1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= +combined-stream@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + integrity sha1-cj599ugBrFYTETp+RFqbactjKBg= + dependencies: + delayed-stream "~1.0.0" + combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.5, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2096,7 +2163,7 @@ concat-stream@1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.2: +concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -2106,14 +2173,6 @@ concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^ readable-stream "^2.2.2" typedarray "^0.0.6" -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - config@1.28.1: version "1.28.1" resolved "https://registry.yarnpkg.com/config/-/config-1.28.1.tgz#7625d2a1e4c90f131d8a73347982d93c3873282d" @@ -2122,12 +2181,12 @@ config@1.28.1: json5 "0.4.0" os-homedir "1.0.2" -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== +configstore@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.0.tgz#37de662c7a49b5fe8dbcf8f6f5818d2d81ed852b" + integrity sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ== dependencies: - dot-prop "^5.2.0" + dot-prop "^5.1.0" graceful-fs "^4.1.2" make-dir "^3.0.0" unique-string "^2.0.0" @@ -2224,11 +2283,6 @@ core-js@^2.4.0, core-js@^2.4.1: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== -core-js@^3.6.4: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" - integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2389,13 +2443,13 @@ css-parse@^2.0.0: css "^2.0.0" css-selector-tokenizer@^0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz#11e5e27c9a48d90284f22d45061c303d7a25ad87" - integrity sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw== + version "0.7.1" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" + integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA== dependencies: - cssesc "^3.0.0" - fastparse "^1.1.2" - regexpu-core "^4.6.0" + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" css-value@~0.0.1: version "0.0.1" @@ -2412,10 +2466,10 @@ css@^2.0.0: source-map-resolve "^0.5.2" urix "^0.1.0" -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= cssnano@^3.10.0: version "3.10.0" @@ -2464,9 +2518,9 @@ csso@~2.3.1: source-map "^0.5.3" csstype@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" - integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== + version "2.6.8" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431" + integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA== cuint@^0.2.1: version "0.2.2" @@ -2492,6 +2546,11 @@ dashdash@1.14.1, dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== + dateformat@~1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" @@ -2505,7 +2564,7 @@ debug-log@^1.0.1: resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" integrity sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8= -debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2624,9 +2683,9 @@ defaults@^1.0.3: clone "^1.0.2" defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + version "1.1.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.1.tgz#88ae694b93f67b81815a2c8c769aef6574ac8f2f" + integrity sha512-J7thop4u3mRTkYRQ+Vpfwy2G5Ehoy82I14+14W4YMDLKdWloI9gSzRbV30s/NckQGVJtPkWNcW4oMAUigTdqiQ== define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" @@ -2662,6 +2721,15 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= +degenerator@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + del@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" @@ -2758,14 +2826,14 @@ dir-glob@^2.0.0: dependencies: path-type "^3.0.0" -dmg-builder@22.3.6: - version "22.3.6" - resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.3.6.tgz#2affc0bfb29a49eb35c11d91e5bf7ba32c140fbc" - integrity sha512-pUrvvzkSZfaCIXofqNH+YJ0ajgi4DDe/ZjMiL/T1yKrRXK3v7sfmnbSzHY8V/WtimSgzsK1izFg/SBsfwFsZWw== +dmg-builder@22.3.2: + version "22.3.2" + resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.3.2.tgz#4c052f75d601e3358da1ff9d7d57738e1c01b157" + integrity sha512-szx+nmCNjpfp2yy3zVCMQLj2nRHL3LfZyzvQECDGHr0ZHK7//48+MoJckkbOCB22ofBvx5Y2M1YqCjK8b2slYQ== dependencies: - app-builder-lib "~22.3.6" + app-builder-lib "~22.3.2" bluebird-lst "^1.0.9" - builder-util "~22.3.6" + builder-util "~22.3.2" fs-extra "^8.1.0" iconv-lite "^0.5.1" js-yaml "^3.13.1" @@ -2807,9 +2875,9 @@ doctrine@^2.0.0, doctrine@^2.0.2, doctrine@^2.1.0: esutils "^2.0.2" dom-align@^1.7.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.11.0.tgz#209c6f50bf8140b89311574389e5cf9283a0c0c0" - integrity sha512-c5xlri+XyxfgIGjJfayVIXo6j8aYh1jMlNlONh1UPTeGMW8T2kRVDcJMm2SryyPQ9i6FtjWyEnpDxT9SENXZBQ== + version "1.10.4" + resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.10.4.tgz#862ae4de0d11d6495c1c8ee1b195427e7caa727d" + integrity sha512-wytDzaru67AmqFOY4B9GUb/hrwWagezoYYK97D/vpK+ezg+cnuZO0Q2gltUPa7KfNmIqfRIYVCF8UhRDEHAmgQ== "dom-helpers@^2.4.0 || ^3.0.0": version "3.4.0" @@ -2829,11 +2897,11 @@ domain-browser@^1.1.1: integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== dompurify@^2.0.7: - version "2.0.8" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.8.tgz#6ef89d2d227d041af139c7b01d9f67ed59c2eb3c" - integrity sha512-vIOSyOXkMx81ghEalh4MLBtDHMx1bhKlaqHDMqM2yeitJ996SLOk5mGdDpI9ifJAgokred8Rmu219fX4OltqXw== + version "2.0.7" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.7.tgz#f8266ad38fe1602fb5b3222f31eedbf5c16c4fd5" + integrity sha512-S3O0lk6rFJtO01ZTzMollCOGg+WAtCwS3U5E2WSDY/x/sy7q70RjEC4Dmrih5/UqzLLB9XoKJ8KqwBxaNvBu4A== -dot-prop@^5.2.0: +dot-prop@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== @@ -2900,35 +2968,52 @@ ejs@~2.5.6: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5" integrity sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ== -electron-builder@22.3.6: - version "22.3.6" - resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.3.6.tgz#690667b5f70e27edc54a84442e9b2440dac51c28" - integrity sha512-W1fMEtAT7WOqetwTR2uCesMIqmnr8DHR9ZKYlHMxGxQt0jGaBX/gQHPf6+R8d2SQq2Bup6e1eE0xoeX3ixCfTw== +electron-builder@22.3.2: + version "22.3.2" + resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.3.2.tgz#902d150fc0670cb90213262e5e0aa3c4f299ffa4" + integrity sha512-bDjHfKtA4DapI6qqy4FC18fzLsOJtlSVGBqjSjhrgv+gbcppp3tjR6ASsUX5K64/8L9MGjhRGdfQ7iP78OLx8g== dependencies: - "@types/yargs" "^15.0.4" - app-builder-lib "22.3.6" + app-builder-lib "22.3.2" bluebird-lst "^1.0.9" - builder-util "22.3.6" - builder-util-runtime "8.6.1" + builder-util "22.3.2" + builder-util-runtime "8.6.0" chalk "^3.0.0" - dmg-builder "22.3.6" + dmg-builder "22.3.2" fs-extra "^8.1.0" is-ci "^2.0.0" lazy-val "^1.0.4" - read-config-file "5.0.2" + read-config-file "5.0.1" sanitize-filename "^1.6.3" - update-notifier "^4.1.0" + update-notifier "^4.0.0" yargs "^15.1.0" -electron-chromedriver@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/electron-chromedriver/-/electron-chromedriver-8.0.0.tgz#16f6124d481e9312cc18abc16495ddc2d61f8264" - integrity sha512-d0210ExhkGOwYLXFZHQR6LISZ8UbMqXWLwjTe8Cdh44XlO4z4+6DWQfM0p7aB2Qak/An6tN732Yl98wN1ylZww== +electron-chromedriver@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/electron-chromedriver/-/electron-chromedriver-3.0.0.tgz#ec0a17badb6c3529813c660bf91a43fb91407674" + integrity sha512-xWivZRiPTtDFJt+qXv7Ax/Dmhxj0iqESOxoLZ2szu3fv6k1vYDUDJUMHfdfVAke9D2gBRIgChuGb5j3YEt6hxQ== + dependencies: + electron-download "^4.1.0" + extract-zip "^1.6.5" + +electron-context-menu@^0.15.0: + version "0.15.2" + resolved "https://registry.yarnpkg.com/electron-context-menu/-/electron-context-menu-0.15.2.tgz#66f544a06d2099dafdecade2e9214a66e26b5764" + integrity sha512-r4Zv/NWKe6smezz66yIZSt0geRPx3wdfVlMYXNSGsPcN7LiyZXGlxWvCX9gsqN8Rg8nWC3Q2EcXzt8d9NQFUyg== + dependencies: + cli-truncate "^2.0.0" + electron-dl "^1.2.0" + electron-is-dev "^1.0.1" + +electron-dl@^1.2.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/electron-dl/-/electron-dl-1.14.0.tgz#1466f1b945664ca3d784268307c2b935728177bf" + integrity sha512-4okyei42a1mLsvLK7hLrIfd20EQzB18nIlLTwBV992aMSmTGLUEFRTmO1MfSslGNrzD8nuPuy1l/VxO8so4lig== dependencies: - electron-download "^4.1.1" - extract-zip "^1.6.7" + ext-name "^5.0.0" + pupa "^1.0.0" + unused-filename "^1.0.0" -electron-download@^4.1.1: +electron-download@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8" integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg== @@ -2943,6 +3028,17 @@ electron-download@^4.1.1: semver "^5.4.1" sumchecker "^2.0.2" +electron-editor-context-menu@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/electron-editor-context-menu/-/electron-editor-context-menu-1.1.1.tgz#dc30098e0dfb37f62628e43303124c7f3379572d" + integrity sha1-3DAJjg37N/YmKOQzAxJMfzN5Vy0= + dependencies: + lodash.clonedeep "^4.3.0" + lodash.defaults "^4.0.1" + lodash.isarray "^4.0.0" + lodash.isempty "^4.1.2" + lodash.isfunction "^3.0.8" + electron-icon-maker@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/electron-icon-maker/-/electron-icon-maker-0.0.3.tgz#bcd2e91896d7200f84fcc6652aed924fdaaa8307" @@ -2957,7 +3053,7 @@ electron-is-accelerator@^0.1.0: resolved "https://registry.yarnpkg.com/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz#509e510c26a56b55e17f863a4b04e111846ab27b" integrity sha1-UJ5RDCala1Xhf4Y6SwThEYRqsns= -electron-is-dev@*, electron-is-dev@^1.1.0: +electron-is-dev@*, electron-is-dev@^1.0.1, electron-is-dev@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.1.0.tgz#b15a2a600bdc48a51a857d460e05f15b19a2522c" integrity sha512-Z1qA/1oHNowGtSBIcWk0pcLEqYT/j+13xUw/MYOrBUOL4X7VN0i0KCTf5SqyvMPmW5pSPKbo28wkxMxzZ20YnQ== @@ -2980,32 +3076,45 @@ electron-notarize@^0.2.0: debug "^4.1.1" fs-extra "^8.1.0" -electron-publish@22.3.6: - version "22.3.6" - resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.3.6.tgz#97500b832d5b281785882100d7c01dbf55dabd03" - integrity sha512-u4PW4ec4rPcdFUVMvL5OrqoYX8WLJHab8DtPkh/oZ2HPzGGX3eWnUvulIIJ4SUnesSYkbUcZd1RDYtfV52Q72w== +electron-publish@22.3.2: + version "22.3.2" + resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.3.2.tgz#d2e60caf7a9643fe57e501c20acaf32c737b1c50" + integrity sha512-nUGepzRtoGT8Tv83RHnnrR5szbw4+HpLLBdZU+aO5Kt3uoReTsp7NSm+agJfbI+H5rVpYCG8h6nPIejxGZ7LTg== dependencies: - "@types/fs-extra" "^8.1.0" bluebird-lst "^1.0.9" - builder-util "~22.3.6" - builder-util-runtime "8.6.1" + builder-util "~22.3.2" + builder-util-runtime "8.6.0" + chalk "^3.0.0" + fs-extra "^8.1.0" + lazy-val "^1.0.4" + mime "^2.4.4" + +electron-publish@22.3.3: + version "22.3.3" + resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.3.3.tgz#7d1e757a20ce0558fdc42900b6e3d773fdae9d9e" + integrity sha512-QfdS6gyqdjX+JBm3DhRT8nwO2TKQF9Z2dsZBXxCfE+FXYe2XmxMXWeXY2vPBHxSOpBYeAYVIkBiNL+gWcSfA+w== + dependencies: + "@types/fs-extra" "^8.0.1" + bluebird-lst "^1.0.9" + builder-util "~22.3.3" + builder-util-runtime "8.6.0" chalk "^3.0.0" fs-extra "^8.1.0" lazy-val "^1.0.4" mime "^2.4.4" electron-to-chromium@^1.2.7: - version "1.3.376" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.376.tgz#7cb7b5205564a06c8f8ecfbe832cbd47a1224bb1" - integrity sha512-cv/PYVz5szeMz192ngilmezyPNFkUjuynuL2vNdiqIrio440nfTDdc0JJU0TS2KHLSVCs9gBbt4CFqM+HcBnjw== + version "1.3.334" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.334.tgz#0588359f4ac5c4185ebacdf5fc7e1937e2c99872" + integrity sha512-RcjJhpsVaX0X6ntu/WSBlW9HE9pnCgXS9B8mTUObl1aDxaiOa0Lu+NMveIS5IDC+VELzhM32rFJDCC+AApVwcA== electron-updater@^4.2.2: - version "4.2.5" - resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.5.tgz#dbced8da6f8c6fc2dc662f2776131f5a49ce018d" - integrity sha512-ir8SI3capF5pN4LTQY79bP7oqiBKjgtdDW378xVId5VcGUZ+Toei2j+fgx1mq3y4Qg19z4HqLxEZ9FqMD0T0RA== + version "4.2.2" + resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.2.2.tgz#57e106bffad16f71b1ffa3968a52a1b71c8147e6" + integrity sha512-e/OZhr5tLW0GcgmpR5wD0ImxgKMa8pPoNWRcwRyMzTL9pGej7+ORp0t9DtI5ZBHUbObIoEbrk+6EDGUGtJf+aA== dependencies: "@types/semver" "^7.1.0" - builder-util-runtime "8.6.2" + builder-util-runtime "8.6.0" fs-extra "^8.1.0" js-yaml "^3.13.1" lazy-val "^1.0.4" @@ -3013,13 +3122,13 @@ electron-updater@^4.2.2: pako "^1.0.11" semver "^7.1.3" -electron@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-8.2.0.tgz#f3e3de23a6178b5ad7baa70f4814d6332a3212c2" - integrity sha512-mnV43gKCrCUMHLmGws/DU/l8LhaxrFD53A4ofwtthdCqOZWGIdk1+eMphiVumXR5a3lC64XVvmXQ2k28i7F/zw== +electron@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-4.1.2.tgz#dc8be0f219c73d60a97675d6d3c5b040c4f50513" + integrity sha512-QWz298CaH2jLyFVje01sj4fSEdXJj+oKVw2jNUovrhZE5jmFE4cr/KqZ7iHX00DG6AwmDF4W1Xrv0V+aQOkieQ== dependencies: - "@electron/get" "^1.0.1" - "@types/node" "^12.0.12" + "@types/node" "^10.12.18" + electron-download "^4.1.0" extract-zip "^1.0.3" elliptic@^6.0.0: @@ -3073,12 +3182,12 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= -encodeurl@^1.0.2, encodeurl@~1.0.2: +encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= @@ -3104,11 +3213,6 @@ env-paths@^1.0.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= -env-paths@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" - integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== - errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -3124,9 +3228,9 @@ error-ex@^1.2.0: is-arrayish "^0.2.1" es-abstract@^1.17.0, es-abstract@^1.17.0-next.1: - version "1.17.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" - integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== + version "1.17.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.1.tgz#1331afa4cba2628b63e988104b9846c2d631b380" + integrity sha512-WmWNHWmm/LDwK8jaeZic/g6sU1ZckM+vvOyCV1qFRhJJ6hzve6DRgthNQB7Lra1ocrw68HexLKYgtdxIPcb3Fg== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" @@ -3149,11 +3253,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - es6-object-assign@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" @@ -3169,10 +3268,12 @@ es6-promise@^4.0.3, es6-promise@^4.2.4: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" escape-html@~1.0.3: version "1.0.3" @@ -3184,17 +3285,12 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escodegen@^1.9.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" - integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== +escodegen@1.x.x, escodegen@^1.9.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.1.tgz#08770602a74ac34c7a90ca9229e7d51e379abc76" + integrity sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ== dependencies: - esprima "^4.0.1" + esprima "^3.1.3" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" @@ -3328,20 +3424,25 @@ espree@^3.5.2: acorn "^5.5.0" acorn-jsx "^3.0.0" +esprima@3.x.x, esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + esprima@^2.1.0, esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48" - integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q== + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== dependencies: estraverse "^4.0.0" @@ -3490,6 +3591,21 @@ express@^4.16.2: utils-merge "1.0.1" vary "~1.1.2" +ext-list@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" + integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== + dependencies: + mime-db "^1.28.0" + +ext-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" + integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== + dependencies: + ext-list "^2.0.0" + sort-keys-length "^1.0.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3560,16 +3676,6 @@ extract-zip@^1.0.3, extract-zip@^1.6.5: mkdirp "0.5.1" yauzl "2.4.1" -extract-zip@^1.6.7: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -3585,10 +3691,10 @@ fast-deep-equal@^1.0.0: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= -fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" - integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= fast-json-stable-stringify@^2.0.0: version "2.1.0" @@ -3600,7 +3706,7 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fastparse@^1.1.2: +fastparse@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== @@ -3626,13 +3732,6 @@ fd-slicer@~1.0.1: dependencies: pend "~1.2.0" -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -3658,7 +3757,7 @@ file-type@^3.1.0: resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= -file-uri-to-path@1.0.0: +file-uri-to-path@1, file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== @@ -3802,9 +3901,9 @@ flush-write-stream@^1.0.0: readable-stream "^2.3.6" follow-redirects@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" - integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" + integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== dependencies: debug "^3.0.0" @@ -3842,7 +3941,7 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.1, form-data@~2.3.2: +form-data@~2.3.1: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== @@ -3851,6 +3950,15 @@ form-data@~2.3.1, form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + integrity sha1-SXBJi+YEwgwAXU9cI67NIda0kJk= + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -3868,7 +3976,7 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -from2@^2.1.0: +from2@^2.1.0, from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= @@ -3892,15 +4000,14 @@ fs-extra@0.26.7, fs-extra@^0.26.5: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" - integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== +fs-extra@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^1.0.0" + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" fs-extra@^1.0.0: version "1.0.0" @@ -3979,6 +4086,14 @@ fstream@^1.0.0, fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4045,7 +4160,7 @@ get-stdin@^5.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= -get-stream@^3.0.0: +get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= @@ -4064,6 +4179,18 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-uri@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.4.tgz#d4937ab819e218d4cb5ae18e4f5962bef169cc6a" + integrity sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q== + dependencies: + data-uri-to-buffer "1" + debug "2" + extend "~3.0.2" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "2" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -4082,9 +4209,9 @@ getpass@^0.1.1: assert-plus "^1.0.0" github-slugger@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" - integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== + version "1.2.1" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.1.tgz#47e904e70bf2dccd0014748142d31126cfd49508" + integrity sha512-SsZUjg/P03KPzQBt7OxJPasGw6NRO5uOgiZ5RGXVud5iSIZ0eNZeNp5rTwCxtavrRUa/A77j8mePVc5lEvk0KQ== dependencies: emoji-regex ">=6.0.0 <=6.1.1" @@ -4169,19 +4296,6 @@ glob@~7.0.0: once "^1.3.0" path-is-absolute "^1.0.0" -global-agent@^2.0.2: - version "2.1.8" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.8.tgz#99d153662b2c04cbc1199ffbc081a3aa656ac50f" - integrity sha512-VpBe/rhY6Rw2VDOTszAMNambg+4Qv8j0yiTNDYEXXXxkUNGWLHp8A3ztK4YDBbFNcWF4rgsec6/5gPyryya/+A== - dependencies: - boolean "^3.0.0" - core-js "^3.6.4" - es6-error "^4.1.1" - matcher "^2.1.0" - roarr "^2.15.2" - semver "^7.1.2" - serialize-error "^5.0.0" - global-dirs@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" @@ -4209,16 +4323,6 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - global@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" @@ -4237,13 +4341,6 @@ globals@^9.18.0: resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== -globalthis@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.1.tgz#40116f5d9c071f9e8fb0037654df1ab3a83b7ef9" - integrity sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw== - dependencies: - define-properties "^1.1.3" - globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -4268,12 +4365,12 @@ globby@^7.1.1: slash "^1.0.0" globule@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.1.tgz#90a25338f22b7fbeb527cee63c629aea754d33b9" - integrity sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g== + version "1.3.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.0.tgz#41d0e9fb44afd4b80d93a23263714f90b3dec904" + integrity sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg== dependencies: glob "~7.1.1" - lodash "~4.17.12" + lodash "~4.17.10" minimatch "~3.0.2" glogg@^1.0.1: @@ -4283,6 +4380,34 @@ glogg@^1.0.1: dependencies: sparkles "^1.0.0" +google-libphonenumber@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.2.tgz#3d9d7ba727e99a50812f21b0ed313723b76c5c54" + integrity sha512-ubjGeosYPeusjYbUHy76lCniGTTI0k1rIFc+uKBX+jHQLDmWOSUtlFUxaeoLJ+Y+PAMM6dWp+C1HjHx5BI8kEw== + +got@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/got/-/got-8.2.0.tgz#0d11a071d05046348a2f5c0a5fa047fb687fdfc6" + integrity sha512-giadqJpXIwjY+ZsuWys8p2yjZGhOHiU4hiJHjS/oeCxw1u8vANQz3zPlrxW2Zw/siCXsSMI3hvzWGcnFyujyAg== + dependencies: + "@sindresorhus/is" "^0.7.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -4440,10 +4565,10 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== -handlebars@^4.0.3: - version "4.7.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.3.tgz#8ece2797826886cf8082d1726ff21d2a022550ee" - integrity sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg== +handlebars@^4.0.3, handlebars@^4.5.3: + version "4.7.2" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.2.tgz#01127b3840156a0927058779482031afe0e730d7" + integrity sha512-4PwqDL2laXtTWZghzzCtunQUTLbo31pcCJrd/B/9JP8XbhVzpS5ZXuKqlOzsd1rtcaLo4KqAn8nl8mkknS4MHw== dependencies: neo-async "^2.6.0" optimist "^0.6.1" @@ -4464,7 +4589,7 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" -har-validator@~5.1.3: +har-validator@~5.1.0: version "5.1.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== @@ -4499,11 +4624,23 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -4587,9 +4724,11 @@ he@1.2.0: integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== highlight.js@^9.12.0: - version "9.18.1" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c" - integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg== + version "9.17.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.17.1.tgz#14a4eded23fd314b05886758bb906e39dd627f9a" + integrity sha512-TA2/doAur5Ol8+iM3Ov7qy3jYcr/QiJ2eDTdRF4dfbjG7AaaB99J5G+zSl11ljbl6cIcahgPY6SKb3sC3EJ0fw== + dependencies: + handlebars "^4.5.3" hmac-drbg@^1.0.0: version "1.0.1" @@ -4601,9 +4740,9 @@ hmac-drbg@^1.0.0: minimalistic-crypto-utils "^1.0.1" hoist-non-react-statics@^3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + version "3.3.1" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#101685d3aff3b23ea213163f6e8e12f4f111e19f" + integrity sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw== dependencies: react-is "^16.7.0" @@ -4620,14 +4759,14 @@ hooker@~0.2.3: integrity sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk= hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== -hosted-git-info@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.4.tgz#be4973eb1fd2737b11c9c7c19380739bb249f60d" - integrity sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ== +hosted-git-info@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.2.tgz#8b7e3bd114b59b51786f8bade0f39ddc80275a97" + integrity sha512-ezZMWtHXm7Eb7Rq4Mwnx2vs79WUx2QmRg3+ZqeGroKzfDO+EprOcgRPYghsOP9JuYBfK18VojmRTGCg8Ma+ktw== dependencies: lru-cache "^5.1.1" @@ -4651,10 +4790,15 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.0.3" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" + integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== http-deceiver@^1.2.7: version "1.2.7" @@ -4672,6 +4816,17 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" @@ -4690,22 +4845,19 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - "http-parser-js@>=0.4.0 <0.4.11": version "0.4.10" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + http-proxy-middleware@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -4739,6 +4891,22 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +https-proxy-agent@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz#b8c286433e87602311b01c8ea34413d856a4af81" + integrity sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + hyphenate-style-name@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" @@ -4832,9 +5000,9 @@ imurmurhash@^0.1.4: integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= in-publish@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c" - integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ== + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + integrity sha1-4g/146KvwmkDILbcVSaCqcf631E= indent-string@^2.1.0: version "2.1.0" @@ -4856,7 +5024,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4901,7 +5069,20 @@ internal-ip@1.2.0: resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" integrity sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w= dependencies: - meow "^3.3.0" + meow "^3.3.0" + +intl-tel-input@12.1.15: + version "12.1.15" + resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-12.1.15.tgz#7393e6b77572731bbc65ca4585782e8ba3d74de4" + integrity sha512-9TN9x6aGdO1eL6iGFpobuLU4UymZqjSnS9UnsOSi//LU3A8nkLOcokSYBYjak18Uu8OM59HsGYDd1jKmwRsskw== + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" + integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" @@ -4920,15 +5101,15 @@ ip-regex@^1.0.1: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0= -ip@^1.1.0, ip@^1.1.5: +ip@1.1.5, ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== is-absolute-url@^2.0.0: version "2.1.0" @@ -4950,9 +5131,9 @@ is-accessor-descriptor@^1.0.0: kind-of "^6.0.0" is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.3.tgz#eb04cc47219a8895d8450ace4715abff2258a1f8" + integrity sha512-eEMa6MKpHFzw38eKm56iNNi6GJ7lf6aLLio7Kr23sJPAECscgRtZvOBYybejWDQ2bM949Y++61PY+udzj5QMLA== is-alphanumeric@^1.0.0: version "1.0.0" @@ -4960,9 +5141,9 @@ is-alphanumeric@^1.0.0: integrity sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ= is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.3.tgz#57ae21c374277b3defe0274c640a5704b8f6657c" + integrity sha512-A1IGAPO5AW9vSh7omxIlOGwIqEvpW/TA+DksVOPM5ODuxKlZS09+TEM1E3275lJqO2oJ38vDpeAL3DCIiHE6eA== dependencies: is-alphabetical "^1.0.0" is-decimal "^1.0.0" @@ -5026,9 +5207,9 @@ is-date-object@^1.0.1: integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.3.tgz#381068759b9dc807d8c0dc0bfbae2b68e1da48b7" + integrity sha512-bvLSwoDg2q6Gf+E2LEPiklHZxxiSi3XAh4Mav65mKqTfCO1HM3uBs24TjEH8iJX3bbDdLXKJXBTmGzuTUuAEjQ== is-descriptor@^0.1.0: version "0.1.6" @@ -5088,9 +5269,11 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" is-fullwidth-code-point@^1.0.0: version "1.0.0" @@ -5136,9 +5319,9 @@ is-glob@^4.0.0: is-extglob "^2.1.1" is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz#e8a426a69b6d31470d3a33a47bb825cda02506ee" + integrity sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA== is-in-browser@^1.1.3: version "1.1.3" @@ -5187,6 +5370,11 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -5255,6 +5443,11 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== +is-retry-allowed@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + is-root@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5" @@ -5295,9 +5488,9 @@ is-utf8@^0.2.0: integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-whitespace-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" - integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac" + integrity sha512-SNPgMLz9JzPccD3nPctcj8sZlX9DAMJSKH8bP7Z6bohCwuNgX8xbWr1eTAYXX9Vpi/aSn8Y1akL9WgM3t43YNQ== is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" @@ -5305,9 +5498,9 @@ is-windows@^1.0.1, is-windows@^1.0.2: integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-word-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" - integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.3.tgz#264d15541cbad0ba833d3992c34e6b40873b08aa" + integrity sha512-0wfcrFgOOOBdgRNT9H33xe6Zi6yhX/uoc4U8NBZGeQQB0ctU1dnlNTyL9JM2646bHDTpsDm1Brb3VPoCIMrd/A== is-wsl@^1.1.0: version "1.1.0" @@ -5409,6 +5602,14 @@ istanbul-reports@^1.1.3: dependencies: handlebars "^4.0.3" +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + javascript-stringify@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" @@ -5447,9 +5648,14 @@ jquery@3.3.1: integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg== js-base64@^2.1.8, js-base64@^2.1.9: - version "2.5.2" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" - integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== + version "2.5.1" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121" + integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw== + +js-sha512@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" + integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" @@ -5461,6 +5667,14 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= +js-yaml@3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e" + integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@^3.13.1, js-yaml@^3.2.7, js-yaml@^3.7.0, js-yaml@^3.9.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" @@ -5530,7 +5744,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -5553,11 +5767,11 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" - integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== dependencies: - minimist "^1.2.5" + minimist "^1.2.0" jsonfile@^2.1.0: version "2.4.0" @@ -5573,15 +5787,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonfile@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179" - integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg== - dependencies: - universalify "^1.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -5645,9 +5850,9 @@ jss@^9.8.1: warning "^3.0.0" just-extend@^4.0.2: - version "4.1.0" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" - integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" + integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw== kew@^0.7.0: version "0.7.0" @@ -5664,6 +5869,13 @@ keyboardevents-areequal@^0.2.1: resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194" integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw== +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" + integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== + dependencies: + json-buffer "3.0.0" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -5696,9 +5908,9 @@ kind-of@^5.0.0: integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== klaw@^1.0.0: version "1.3.1" @@ -5746,7 +5958,7 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libsodium-wrappers@^0.7.6: +libsodium-wrappers@^0.7.4: version "0.7.6" resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.6.tgz#baed4c16d4bf9610104875ad8a8e164d259d48fb" integrity sha512-OUO2CWW5bHdLr6hkKLHIKI4raEkZrf3QHkhXsJ1yCh6MZ3JDA7jFD3kCATNquuGSG6MjjPHQIQms0y0gBDzjQg== @@ -5823,12 +6035,12 @@ loader-runner@^2.3.0: integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== loader-utils@^1.0.2, loader-utils@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== dependencies: big.js "^5.2.2" - emojis-list "^3.0.0" + emojis-list "^2.0.0" json5 "^1.0.1" locate-path@^2.0.0: @@ -5866,7 +6078,7 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -lodash.clonedeep@^4.3.2: +lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= @@ -5876,16 +6088,36 @@ lodash.cond@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" integrity sha1-9HGh2khr5g9quVXRcRVSPdHSVdU= +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.isarray@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403" + integrity sha1-KspJayjEym1yZxUxNZDALm6jRAM= + +lodash.isempty@^4.1.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= + lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.isfunction@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -5911,7 +6143,7 @@ lodash@^3.10.1: resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.12, lodash@~4.17.5: +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10, lodash@~4.17.5: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -5934,9 +6166,9 @@ log-symbols@^2.2.0: chalk "^2.0.1" loglevel@^1.4.1: - version "1.6.7" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" - integrity sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== + version "1.6.6" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" + integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== lolex@^2.2.0: version "2.7.5" @@ -5956,9 +6188,9 @@ long@^4.0.0: integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== longest-streak@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" - integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.3.tgz#3de7a3f47ee18e9074ded8575b5c091f5d0a4105" + integrity sha512-9lz5IVdpwsKLMzQi0MQ+oD9EA0mIGcWYP7jXMTZVXP8D42PwuAk+M/HBFYQoxt1G5OR8m7aSIgb1UymfWGBWEw== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0: version "1.4.0" @@ -5975,6 +6207,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lowercase-keys@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -5985,7 +6222,7 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^4.0.1, lru-cache@^4.1.1: +lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -6001,9 +6238,9 @@ lru-cache@^5.1.1: yallist "^3.0.2" magic-string@^0.25.3: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + version "0.25.6" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.6.tgz#5586387d1242f919c6d223579cc938bf1420795e" + integrity sha512-3a5LOMSGoCTH5rbqobC2HuDNRtE2glHZ8J7pK+QZYppyWA36yuNpsX994rIY2nCuyP7CZYy7lQq/X2jygiZ89g== dependencies: sourcemap-codec "^1.4.4" @@ -6015,9 +6252,9 @@ make-dir@^1.0.0: pify "^3.0.0" make-dir@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" - integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + version "3.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" + integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw== dependencies: semver "^6.0.0" @@ -6039,9 +6276,9 @@ map-visit@^1.0.0: object-visit "^1.0.0" markdown-escapes@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" - integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.3.tgz#6155e10416efaafab665d466ce598216375195f5" + integrity sha512-XUi5HJhhV5R74k8/0H2oCbCiYf/u4cO/rX8tnGkRvrqhsr5BRNU6Mg0yt/8UIx1iIS8220BNJsDb7XnILhLepw== markdown-table@^1.1.0: version "1.1.3" @@ -6049,24 +6286,17 @@ markdown-table@^1.1.0: integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== markdown-to-jsx@^6.6.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0" - integrity sha512-RH7LCJQ4RFmPqVeZEesKaO1biRzB/k4utoofmTCp3Eiw6D7qfvK8fzZq/2bjEJAtVkfPrM5SMt5APGf2rnaKMg== + version "6.10.3" + resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.10.3.tgz#7f0946684acd321125ff2de7fd258a9b9c7c40b7" + integrity sha512-PSoUyLnW/xoW6RsxZrquSSz5eGEOTwa15H5eqp3enmrp8esmgDJmhzd6zmQ9tgAA9TxJzx1Hmf3incYU/IamoQ== dependencies: prop-types "^15.6.2" unquote "^1.1.0" -matcher@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-2.1.0.tgz#64e1041c15b993e23b786f93320a7474bf833c28" - integrity sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ== - dependencies: - escape-string-regexp "^2.0.0" - math-expression-evaluator@^1.2.14: - version "1.2.22" - resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz#c14dcb3d8b4d150e5dcea9c68c8dad80309b0d5e" - integrity sha512-L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ== + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + integrity sha1-3oGf282E3M2PrlnGrreWFbnSZqw= math-random@^1.0.1: version "1.0.4" @@ -6208,7 +6438,7 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": +mime-db@1.43.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: version "1.43.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== @@ -6283,16 +6513,11 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0: +minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -6344,20 +6569,13 @@ mkdirp@0.5.0: dependencies: minimist "0.0.8" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -mkdirp@^0.5.4, mkdirp@~0.5.1: - version "0.5.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" - integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== - dependencies: - minimist "^1.2.5" - mkpath@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-0.1.0.tgz#7554a6f8d871834cc97b5462b122c4c124d6de91" @@ -6395,6 +6613,11 @@ mocha@4.1.0: mkdirp "0.5.1" supports-color "4.4.0" +modify-filename@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" + integrity sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE= + moment@2.21.0: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" @@ -6478,7 +6701,7 @@ mz@^2.3.1: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.10.0, nan@^2.12.1, nan@^2.14.0: +nan@^2.10.0, nan@^2.11.0, nan@^2.12.1, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -6518,9 +6741,9 @@ ncp@~2.0.0: integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= needle@^2.2.1: - version "2.3.3" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.3.tgz#a041ad1d04a871b0ebb666f40baaf1fb47867117" - integrity sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== dependencies: debug "^3.2.6" iconv-lite "^0.4.4" @@ -6536,6 +6759,11 @@ neo-async@^2.5.0, neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +netmask@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= + nise@^1.2.0: version "1.5.3" resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" @@ -6667,9 +6895,9 @@ node-sass@4.9.3: abbrev "1" nopt@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= dependencies: abbrev "1" osenv "^0.1.4" @@ -6708,6 +6936,15 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + normalize-url@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -6730,14 +6967,6 @@ npm-bundled@^1.0.1: dependencies: npm-normalize-package-bin "^1.0.1" -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - npm-install-package@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/npm-install-package/-/npm-install-package-2.1.0.tgz#d7efe3cfcd7ab00614b896ea53119dc9ab259125" @@ -6749,13 +6978,12 @@ npm-normalize-package-bin@^1.0.1: integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-packlist@^1.1.6: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + version "1.4.7" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" + integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" - npm-normalize-package-bin "^1.0.1" npm-run-path@^2.0.0: version "2.0.2" @@ -7034,6 +7262,11 @@ osenv@0, osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -7044,6 +7277,11 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= + p-limit@^1.0.0, p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -7077,6 +7315,13 @@ p-map@^1.1.1: resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== + dependencies: + p-finally "^1.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -7087,6 +7332,31 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz#115b1e58f92576cac2eba718593ca7b0e37de2ad" + integrity sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ== + dependencies: + agent-base "^4.2.0" + debug "^4.1.1" + get-uri "^2.0.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^3.0.0" + pac-resolver "^3.0.0" + raw-body "^2.2.0" + socks-proxy-agent "^4.0.1" + +pac-resolver@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" + integrity sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA== + dependencies: + co "^4.6.0" + degenerator "^1.0.4" + ip "^1.1.5" + netmask "^1.0.6" + thunkify "^2.1.2" + package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -7097,11 +7367,16 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@^1.0.11, pako@~1.0.5: +pako@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +pako@~1.0.5: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + parallel-transform@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" @@ -7750,7 +8025,7 @@ progress@^1.1.8: resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= -progress@^2.0.0, progress@^2.0.3: +progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -7769,11 +8044,6 @@ prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, pr object-assign "^4.1.1" react-is "^16.8.1" -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - protobufjs@6.8.6: version "6.8.6" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.6.tgz#ce3cf4fff9625b62966c455fc4c15e4331a11ca2" @@ -7794,12 +8064,31 @@ protobufjs@6.8.6: long "^4.0.0" proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== dependencies: forwarded "~0.1.2" - ipaddr.js "1.9.1" + ipaddr.js "1.9.0" + +proxy-agent@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.3.tgz#1c1a33db60ef5f2e9e35b876fd63c2bc681c611d" + integrity sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA== + dependencies: + agent-base "^4.2.0" + debug "^3.1.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + lru-cache "^4.1.2" + pac-proxy-agent "^3.0.0" + proxy-from-env "^1.0.0" + socks-proxy-agent "^4.0.1" + +proxy-from-env@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= prr@~1.0.1: version "1.0.1" @@ -7811,10 +8100,10 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +psl@^1.1.24: + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== public-encrypt@^4.0.0: version "4.0.3" @@ -7863,17 +8152,15 @@ punycode@^1.2.4, punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" - integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== - dependencies: - escape-goat "^2.0.0" +pupa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-1.0.0.tgz#9a9568a5af7e657b8462a6e9d5328743560ceff6" + integrity sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y= q-i@^2.0.1: version "2.0.1" @@ -7927,6 +8214,15 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -7993,6 +8289,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + raw-body@~2.1.5: version "2.1.7" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" @@ -8013,9 +8319,9 @@ rc-align@^2.4.0: rc-util "^4.0.4" rc-animate@2.x: - version "2.10.3" - resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.10.3.tgz#163d5e29281a4ff82d53ee7918eeeac856b756f9" - integrity sha512-A9qQ5Y8BLlM7EhuCO3fWb/dChndlbWtY/P5QvPqBU7h4r5Q2QsvsbpTGgdYZATRDZbTRnJXXfVk9UtlyS7MBLg== + version "2.10.2" + resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.10.2.tgz#217fdc76ff26cbf425a5caf87cc8a36ba4598456" + integrity sha512-cE/A7piAzoWFSgUD69NmmMraqCeqVBa51UErod8NS3LUEqWfppSVagHfa0qHAlwPVPiIBg3emRONyny3eiH0Dg== dependencies: babel-runtime "6.x" classnames "^2.2.6" @@ -8062,14 +8368,13 @@ rc-trigger@^2.2.2: react-lifecycles-compat "^3.0.4" rc-util@^4.0.4, rc-util@^4.15.3, rc-util@^4.4.0: - version "4.20.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.20.1.tgz#a5976eabfc3198ed9b8e79ffb8c53c231db36e77" - integrity sha512-EGlDg9KPN0POzmAR2hk9ZyFc3DmJIrXwlC8NoDxJguX2LTINnVqwadLIVauLfYgYISMiFYFrSHiFW+cqUhZ5dA== + version "4.18.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.18.1.tgz#5a6312503cd5366ef0bede021dce42d373f404a8" + integrity sha512-3aRHG32ZvqBymtJUGoQnbZS+XANzO6XTiFEFAYI3BfuxESEazopAy0kBwcAI6BlLHsW1oLiy3ysE9uYwylh2ag== dependencies: add-dom-event-listener "^1.1.0" babel-runtime "6.x" prop-types "^15.5.10" - react-is "^16.12.0" react-lifecycles-compat "^3.0.4" shallowequal "^1.1.0" @@ -8184,10 +8489,10 @@ react-icons@^2.2.7: dependencies: react-icon-base "2.1.0" -react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2: - version "16.13.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" - integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== +react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2: + version "16.12.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" + integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== react-lifecycles-compat@^3.0.4: version "3.0.4" @@ -8195,9 +8500,9 @@ react-lifecycles-compat@^3.0.4: integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== react-portal@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.1.tgz#12c1599238c06fb08a9800f3070bea2a3f78b1a6" - integrity sha512-fE9kOBagwmTXZ3YGRYb4gcMy+kSA+yLO0xnPankjRlfBv4uCpFXqKPfkpsGQQR15wkZ9EssnvTOl1yMzbkxhPQ== + version "4.2.0" + resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.0.tgz#5400831cdb0ae64dccb8128121cf076089ab1afd" + integrity sha512-Zf+vGQ/VEAb5XAy+muKEn48yhdCNYPZaB1BWg1xc8sAZWD8pXTgPtQT4ihBdmWzsfCq8p8/kqf0GWydSBqc+Eg== dependencies: prop-types "^15.5.8" @@ -8311,10 +8616,10 @@ read-chunk@^1.0.1: resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194" integrity sha1-X2jKswfmY/GZk1J9m1icrORmEZQ= -read-config-file@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-5.0.2.tgz#55e005e5a447a9ce5806358d7b22cb7cefb6436d" - integrity sha512-tVt1lsiSjs+FtL/vtfCivqtKR1UNk3BB3uPJQvJqkgtAYDvZjo0xyXFYSVmzaTcO+Jdi5G7O2K2vDV+p1M/oug== +read-config-file@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-5.0.1.tgz#ead3df0d9822cc96006ca16322eaa79dac8591c2" + integrity sha512-75zp4PDbvtBlECoZK1KEkNlesr9OWdMWL8oi4xq+HXAM+kKHKU+Cx2ksFt+ie2BkrmkLBOKSfONDuz+WIKWoXA== dependencies: dotenv "^8.2.0" dotenv-expand "^5.1.0" @@ -8364,7 +8669,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8377,7 +8682,7 @@ read-pkg@^2.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^1.1.8, readable-stream@~1.1.9: +readable-stream@1.1.x, readable-stream@^1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -8388,9 +8693,9 @@ readable-stream@^1.1.8, readable-stream@~1.1.9: string_decoder "~0.10.x" readable-stream@^3.0.6: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -8495,14 +8800,14 @@ redux@^4.0.0: loose-envify "^1.4.0" symbol-observable "^1.2.0" -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== dependencies: regenerate "^1.4.0" -regenerate@^1.4.0: +regenerate@^1.2.1, regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== @@ -8512,10 +8817,10 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.4: - version "0.13.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== +regenerator-runtime@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" + integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== regex-cache@^0.4.2: version "0.4.4" @@ -8540,24 +8845,34 @@ regexp.prototype.flags@^1.2.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -regexpu-core@^4.5.4, regexpu-core@^4.6.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" - integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs= + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^4.5.4: + version "4.6.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" + integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== dependencies: regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" + unicode-match-property-value-ecmascript "^1.1.0" registry-auth-token@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479" - integrity sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA== + version "4.0.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.0.0.tgz#30e55961eec77379da551ea5c4cf43cbf03522be" + integrity sha512-lpQkHxd9UL6tb3k/aHAVfnVtn+Bcs9ob5InuFLLEDqSqeq+AljB8GZW9xY0x7F+xYwEcjKe07nyoxzEYz6yvkw== dependencies: rc "^1.2.8" + safe-buffer "^5.0.1" registry-url@^5.0.0: version "5.1.0" @@ -8566,15 +8881,27 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -regjsgen@^0.5.1: +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + +regjsgen@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== -regjsparser@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" - integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + +regjsparser@^0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.2.tgz#fd62c753991467d9d1ffe0a9f67f27a529024b96" + integrity sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q== dependencies: jsesc "~0.5.0" @@ -8689,9 +9016,9 @@ request@2.87.0: uuid "^3.1.0" request@2.x, request@^2.45.0, request@^2.65.0, request@^2.81.0, request@^2.83.0, request@^2.87.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -8700,7 +9027,7 @@ request@2.x, request@^2.45.0, request@^2.65.0, request@^2.81.0, request@^2.83.0, extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.3" + har-validator "~5.1.0" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -8710,7 +9037,7 @@ request@2.x, request@^2.45.0, request@^2.65.0, request@^2.81.0, request@^2.83.0, performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.5.0" + tough-cookie "~2.4.3" tunnel-agent "^0.6.0" uuid "^3.3.2" @@ -8783,9 +9110,9 @@ resolve-url@^0.2.1: integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.10.0, resolve@^1.13.1, resolve@^1.3.2: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== + version "1.14.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" + integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== dependencies: path-parse "^1.0.6" @@ -8794,7 +9121,7 @@ resolve@~1.1.0: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -responselike@^1.0.2: +responselike@1.0.2, responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= @@ -8860,22 +9187,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -roarr@^2.15.2: - version "2.15.2" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.2.tgz#34f6229ae3c8c12167c4ae60f58fe75e79a1e394" - integrity sha512-jmaDhK9CO4YbQAV8zzCnq9vjAqeO489MS5ehZ+rXmFiPFFE6B+S9KYO6prjmLJ5A0zY3QxVlQdrIya7E/azz/Q== - dependencies: - boolean "^3.0.0" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - run-async@^2.2.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" - integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= dependencies: is-promise "^2.1.0" @@ -8930,7 +9245,7 @@ samsam@1.3.0: resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" integrity sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg== -sanitize-filename@^1.6.2, sanitize-filename@^1.6.3: +sanitize-filename@^1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== @@ -8988,11 +9303,6 @@ selfsigned@^1.9.1: dependencies: node-forge "0.9.0" -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -9015,7 +9325,12 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.2, semver@^7.1.3: +semver@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.2.tgz#847bae5bce68c5d08889824f02667199b70e3d87" + integrity sha512-BJs9T/H8sEVHbeigqzIEo57Iu/3DG6c4QoqTfbQB3BPA4zgzAomh/Fk9E7QtjWQ8mx2dgA9YCfSF4y9k9bHNpQ== + +semver@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== @@ -9044,13 +9359,6 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-error@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac" - integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA== - dependencies: - type-fest "^0.8.0" - serialize-javascript@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" @@ -9156,12 +9464,7 @@ shell-quote@1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -signal-exit@^3.0.1: +signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= @@ -9205,11 +9508,25 @@ slice-ansi@1.0.0: dependencies: is-fullwidth-code-point "^2.0.0" +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -9260,6 +9577,29 @@ sockjs@0.3.19: faye-websocket "^0.10.0" uuid "^3.0.1" +socks-proxy-agent@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" + integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== + dependencies: + ip "1.1.5" + smart-buffer "^4.1.0" + +sort-keys-length@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" + integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= + dependencies: + sort-keys "^1.0.0" + sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -9267,6 +9607,13 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -9314,9 +9661,9 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + version "1.4.7" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.7.tgz#5b2cd184e3fe51fd30ba049f7f62bf499b4f73ae" + integrity sha512-RuN23NzhAOuUtaivhcrjXx1OPXsFeH9m5sI373/U7+tGLKihjUyboZAzOadytMjnqHp1f45RGk1IzDKCpDpSYA== sparkles@^1.0.0: version "1.0.1" @@ -9384,14 +9731,13 @@ spdy@^4.0.0: select-hose "^2.0.0" spdy-transport "^3.0.0" -spectron@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/spectron/-/spectron-10.0.1.tgz#d89fdd3c9625c7dbb5d1f047fda7cb922eda0125" - integrity sha512-eMAOr7ovYf+e6+DhkoxVWAMRfZvLJMjtZKwWYkL56fv3Ij6rxhYLjOxybKj0phgMYZ7o2cX5zu2NoyiUM756CA== +spectron@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/spectron/-/spectron-5.0.0.tgz#602440da0c892f8d73471652ae68000b98d7769c" + integrity sha512-wJrFe8EZ7xvarYawBPd1pDegmSz81U1jG0rSCx+yXqD1TISUH9ASB21KysLXkPylAnc2vhbpGiWQxrqVFtsiJg== dependencies: - "@types/webdriverio" "^4.8.0" dev-null "^0.1.1" - electron-chromedriver "^8.0.0" + electron-chromedriver "~3.0.0" request "^2.87.0" split "^1.0.0" webdriverio "^4.13.0" @@ -9401,6 +9747,14 @@ speedometer@~0.1.2: resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" integrity sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0= +spellchecker@3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/spellchecker/-/spellchecker-3.7.0.tgz#d63e6fd612352b0108e7bbf942f271665ff63c8b" + integrity sha512-saQT4BR9nivbK70s0YjyIlSbZzO6bfWRULcGL2JU7fi7wotOnWl70P0QoUwwLywNQJQ47osgCo6GmOlqzRTxbQ== + dependencies: + any-promise "^1.3.0" + nan "^2.14.0" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -9415,11 +9769,6 @@ split@^1.0.0: dependencies: through "2" -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -9453,9 +9802,9 @@ stat-mode@^1.0.0: integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg== state-toggle@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" - integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + version "1.0.2" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.2.tgz#75e93a61944116b4959d665c8db2d243631d6ddc" + integrity sha512-8LpelPGR0qQM4PnfLiplOQNJcIN1/r2Gy0xKB2zKnIW2YzPMt2sR4I/+gtPjhN7Svh9kw+zqEg2SFwpBO9iNiw== static-extend@^0.1.1: version "0.1.2" @@ -9694,13 +10043,6 @@ sumchecker@^2.0.2: dependencies: debug "^2.2.0" -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - supports-color@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" @@ -9834,10 +10176,10 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.3" -temp-file@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.3.7.tgz#686885d635f872748e384e871855958470aeb18a" - integrity sha512-9tBJKt7GZAQt/Rg0QzVWA8Am8c1EFl+CAv04/aBVqlx5oyfQ508sFIABshQ0xbZu6mBrFLWIUXO/bbLYghW70g== +temp-file@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.3.6.tgz#bd7a1951338bf93b59380b498ec1804d5b76c449" + integrity sha512-7TPldi8QJqRlPIF/Y33mVvo8+xDfi6+aVTCK4CrCaLqCoaOnVtf3SA4hCU0T5nhYDdOC7erw7o2uWfvijlk4Ug== dependencies: async-exit-hook "^2.0.1" fs-extra "^8.1.0" @@ -9858,7 +10200,7 @@ test-exclude@^4.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -testcheck@^1.0.0-rc: +testcheck@1.0.0-rc.2, testcheck@^1.0.0-rc: version "1.0.0-rc.2" resolved "https://registry.yarnpkg.com/testcheck/-/testcheck-1.0.0-rc.2.tgz#11356a25b84575efe0b0857451e85b5fa74ee4e4" integrity sha1-ETVqJbhFde/gsIV0UehbX6dO5OQ= @@ -9913,6 +10255,11 @@ through@2, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +thunkify@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -9923,6 +10270,11 @@ time-stamp@^2.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz#917e0a66905688790ec7bbbde04046259af83f57" integrity sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA== +timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + timers-browserify@^2.0.4: version "2.0.11" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" @@ -10033,13 +10385,13 @@ tough-cookie@~2.3.3: dependencies: punycode "^1.4.1" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: - psl "^1.1.28" - punycode "^2.1.1" + psl "^1.1.24" + punycode "^1.4.1" "traverse@>=0.3.0 <0.4": version "0.3.9" @@ -10057,9 +10409,9 @@ trim-right@^1.0.1: integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= trim-trailing-lines@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" - integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.2.tgz#d2f1e153161152e9f02fabc670fb40bec2ea2e3a" + integrity sha512-MUjYItdrqqj2zpcHFTkMa9WAv4JHTI6gnRQGPFLrt5L9a6tRMiDnIqYl8JBvu2d2Tc3lWJKQwlGCp0K8AvCM+Q== trim@0.0.1: version "0.0.1" @@ -10067,9 +10419,9 @@ trim@0.0.1: integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= trough@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" - integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.4.tgz#3b52b1f13924f460c3fbfd0df69b587dbcbc762e" + integrity sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q== "true-case-path@^1.0.2": version "1.0.3" @@ -10097,9 +10449,9 @@ ts-loader@4.1.0: semver "^5.0.1" tslib@^1.8.0, tslib@^1.8.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" - integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== tslint-microsoft-contrib@6.0.0: version "6.0.0" @@ -10160,11 +10512,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -10182,7 +10529,7 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.8.0, type-fest@^0.8.1: +type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -10226,9 +10573,9 @@ uglify-es@^3.3.4: source-map "~0.6.1" uglify-js@^3.1.4: - version "3.8.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.8.0.tgz#f3541ae97b2f048d7e7e3aa4f39fd8a1f5d7a805" - integrity sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ== + version "3.7.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.5.tgz#278c7c24927ac5a32d3336fc68fd4ae1177a486a" + integrity sha512-GFZ3EXRptKGvb/C1Sq6nO1iI7AGcjyqmIyOw0DrD0675e+NNbGO72xmMM2iEBdFbxaTLo70NbjM/Wy54uZIlsg== dependencies: commander "~2.20.3" source-map "~0.6.1" @@ -10277,12 +10624,12 @@ underscore@>=1.8.3: integrity sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ== unherit@^1.0.4: - version "1.1.3" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" - integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.2.tgz#14f1f397253ee4ec95cec167762e77df83678449" + integrity sha512-W3tMnpaMG7ZY6xe/moK04U9fBhi6wEiCYHUW5Mop/wQHf12+79EQGwxYejNdhEz2mkqkBlGwm7pxmgBKMVUj0w== dependencies: - inherits "^2.0.0" - xtend "^4.0.0" + inherits "^2.0.1" + xtend "^4.0.1" unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" @@ -10297,15 +10644,15 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== unified@^6.0.0: version "6.2.0" @@ -10396,11 +10743,6 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -universalify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" - integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== - unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -10419,19 +10761,27 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +unused-filename@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unused-filename/-/unused-filename-1.0.0.tgz#d340880f71ae2115ebaa1325bef05cc6684469c6" + integrity sha1-00CID3GuIRXrqhMlvvBcxmhEacY= + dependencies: + modify-filename "^1.1.0" + path-exists "^3.0.0" + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-notifier@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" - integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew== +update-notifier@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.0.0.tgz#f344a6f8b03e00e31b323d632a0e632e9f0e0654" + integrity sha512-p9zf71hWt5GVXM4iEBujpUgx8mK9AWiCCapEJm/O1z5ntCim83Z1ATqzZFBHFYqx03laMqv8LiDgs/7ikXjf/g== dependencies: boxen "^4.2.0" chalk "^3.0.0" - configstore "^5.0.1" + configstore "^5.0.0" has-yarn "^2.1.0" import-lazy "^2.1.0" is-ci "^2.0.0" @@ -10439,7 +10789,6 @@ update-notifier@^4.1.0: is-npm "^4.0.0" is-yarn-global "^0.3.0" latest-version "^5.0.0" - pupa "^2.0.1" semver-diff "^3.1.1" xdg-basedir "^4.0.0" @@ -10477,6 +10826,11 @@ url-regex@^3.0.0: dependencies: ip-regex "^1.0.1" +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -10525,9 +10879,9 @@ uuid@3.3.2: integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== validate-npm-package-license@^3.0.1: version "3.0.4" @@ -10543,9 +10897,9 @@ vary@~1.1.2: integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + version "1.0.3" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0" + integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== verror@1.10.0: version "1.10.0" @@ -10751,6 +11105,16 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== +websocket@1.0.28: + version "1.0.28" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3" + integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA== + dependencies: + debug "^2.2.0" + nan "^2.11.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + wgxpath@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690" @@ -10855,9 +11219,9 @@ write-file-atomic@^1.1.4: signal-exit "^3.0.2" write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + version "3.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.1.tgz#558328352e673b5bb192cf86500d60b230667d4b" + integrity sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw== dependencies: imurmurhash "^0.1.4" is-typedarray "^1.0.0" @@ -10909,6 +11273,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -10931,6 +11300,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -10941,10 +11315,10 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@^18.1.1: - version "18.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37" - integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== +yargs-parser@^16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" + integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -11008,9 +11382,9 @@ yargs@^10.0.3: yargs-parser "^8.1.0" yargs@^15.1.0: - version "15.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" - integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== + version "15.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219" + integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg== dependencies: cliui "^6.0.0" decamelize "^1.2.0" @@ -11022,7 +11396,7 @@ yargs@^15.1.0: string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^18.1.1" + yargs-parser "^16.1.0" yargs@^7.0.0: version "7.1.0" @@ -11050,14 +11424,6 @@ yauzl@2.4.1: dependencies: fd-slicer "~1.0.1" -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - zip-stream@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04" From 1ee2088343f0db99f86208dc5988c0829ca1103b Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Fri, 3 Apr 2020 03:46:39 -0700 Subject: [PATCH 27/30] Fix integration tests slipping into release Per Mikunj --- js/modules/loki_file_server_api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/modules/loki_file_server_api.js b/js/modules/loki_file_server_api.js index 47adf81eb..b522444bb 100644 --- a/js/modules/loki_file_server_api.js +++ b/js/modules/loki_file_server_api.js @@ -4,7 +4,6 @@ /* global log: false */ const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); -const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); const DEVICE_MAPPING_USER_ANNOTATION_TYPE = 'network.loki.messenger.devicemapping'; @@ -60,6 +59,7 @@ class LokiFileServerInstance { async establishConnection(serverUrl, options) { // why don't we extend this? if (process.env.USE_STUBBED_NETWORK) { + const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); this._server = new StubAppDotNetAPI(this.ourKey, serverUrl); } else { this._server = new LokiAppDotNetAPI(this.ourKey, serverUrl); From b517ad5286b8223563d0acf46a8562829f16d347 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Fri, 3 Apr 2020 03:49:32 -0700 Subject: [PATCH 28/30] put integration code under guard --- js/modules/loki_public_chat_api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index 20aa2338d..a2acaa68b 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); -const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); class LokiPublicChatFactoryAPI extends EventEmitter { constructor(ourKey) { @@ -61,6 +60,7 @@ class LokiPublicChatFactoryAPI extends EventEmitter { // after verification then we can start up all the pollers if (process.env.USE_STUBBED_NETWORK) { + const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); thisServer = new StubAppDotNetAPI(this.ourKey, serverUrl); } else { thisServer = new LokiAppDotNetAPI(this.ourKey, serverUrl); From 70c4b9b3c750f71f1bcaafbd206f2827324e1045 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Fri, 3 Apr 2020 03:53:02 -0700 Subject: [PATCH 29/30] lint --- js/modules/loki_file_server_api.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/modules/loki_file_server_api.js b/js/modules/loki_file_server_api.js index b522444bb..b6050a6be 100644 --- a/js/modules/loki_file_server_api.js +++ b/js/modules/loki_file_server_api.js @@ -59,6 +59,7 @@ class LokiFileServerInstance { async establishConnection(serverUrl, options) { // why don't we extend this? if (process.env.USE_STUBBED_NETWORK) { + // eslint-disable-next-line global-require const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); this._server = new StubAppDotNetAPI(this.ourKey, serverUrl); } else { From 3553edcefc93421fcd70e0b3f6f9fe5b789e49ea Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Fri, 3 Apr 2020 03:53:36 -0700 Subject: [PATCH 30/30] lint --- js/modules/loki_public_chat_api.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index a2acaa68b..aa89828a4 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -60,6 +60,7 @@ class LokiPublicChatFactoryAPI extends EventEmitter { // after verification then we can start up all the pollers if (process.env.USE_STUBBED_NETWORK) { + // eslint-disable-next-line global-require const StubAppDotNetAPI = require('../../integration_test/stubs/stub_app_dot_net_api.js'); thisServer = new StubAppDotNetAPI(this.ourKey, serverUrl); } else {