From 5ff747635921b4d3332b7743b470b822874e1953 Mon Sep 17 00:00:00 2001 From: Vince Date: Fri, 3 Apr 2020 16:20:49 +1100 Subject: [PATCH 1/5] 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 1ee2088343f0db99f86208dc5988c0829ca1103b Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Fri, 3 Apr 2020 03:46:39 -0700 Subject: [PATCH 2/5] 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 3/5] 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 4/5] 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 5/5] 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 {