diff --git a/patches/protonmail/captcha-3.patch b/patches/protonmail/captcha-3.patch new file mode 100644 index 000000000..bb8cdb5ed --- /dev/null +++ b/patches/protonmail/captcha-3.patch @@ -0,0 +1,12 @@ +diff --git a/packages/components/containers/api/humanVerification/Captcha.tsx b/packages/components/containers/api/humanVerification/Captcha.tsx +index af1defc99..cbf37a9a3 100644 +--- a/packages/components/containers/api/humanVerification/Captcha.tsx ++++ b/packages/components/containers/api/humanVerification/Captcha.tsx +@@ -11,6 +11,7 @@ const getIframeUrl = (token: string, theme?: CaptchaTheme) => { + if (theme === 'dark') { + url.searchParams.set('Dark', 'true'); + } ++ url.protocol = "https:"; + return url; + }; + diff --git a/patches/protonmail/common.patch b/patches/protonmail/common.patch index d0bc8de79..f45f2374e 100644 --- a/patches/protonmail/common.patch +++ b/patches/protonmail/common.patch @@ -25,42 +25,6 @@ index 55715b89d..c87879ad4 100755 stdio: 'inherit', } -diff --git a/packages/pack/webpack.config.js b/packages/pack/webpack.config.js -index dbfc3b18d..89807e7dd 100644 ---- a/packages/pack/webpack.config.js -+++ b/packages/pack/webpack.config.js -@@ -28,7 +28,20 @@ const getConfig = (env) => { - }, - }; - -- return { -+ return (() => { -+ const file = path.resolve("./proton.config.js"); -+ if (require("fs").existsSync(file)) { -+ console.log( -+ /*reset:*/"\x1b[0m" + -+ /*yellow:*/"\x1b[33m" + -+ ">>> " + -+ /*reset:*/"\x1b[0m", -+ `Found ${file}, extend the config`, -+ ) -+ return require(file); -+ } -+ return (value) => value; -+ })()({ - target: options.isProduction ? `browserslist:${options.browserslist}` : 'web', // dev-server bug https://github.com/webpack/webpack-dev-server/issues/2812 - mode: options.isProduction ? 'production' : 'development', - bail: options.isProduction, -@@ -103,7 +116,7 @@ const getConfig = (env) => { - }, - }), - }, -- }; -+ }); - }; - - module.exports = getConfig; - diff --git a/packages/pack/webpack/css.loader.js b/packages/pack/webpack/css.loader.js index c50ab972c..a505eb314 100644 --- a/packages/pack/webpack/css.loader.js @@ -92,9 +56,9 @@ index 9aaa78a28..f3d24b47c 100644 +{ + const platform = String(navigator.platform); + const userAgents = { -+ linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", -+ windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", -+ macos: "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" ++ linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36", ++ windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36", ++ macos: "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36" + } as const; + uaParser.setUA( + platform.startsWith("Linux") @@ -135,58 +99,6 @@ index 9aaa78a28..f3d24b47c 100644 + ); }; -diff --git a/packages/shared/lib/constants.ts b/packages/shared/lib/constants.ts -index 2bd472b..52cf521 100644 ---- a/packages/shared/lib/constants.ts -+++ b/packages/shared/lib/constants.ts -@@ -36,8 +36,8 @@ export const APPS = { - } as const; - export const APPS_CONFIGURATION = { - [APPS.PROTONACCOUNT]: { -- publicPath: '', -- subdomain: 'account', -+ publicPath: '/account', -+ subdomain: '', - name: 'Proton Account', - bareName: 'Account', - clientID: 'WebAccount', -@@ -46,7 +46,7 @@ export const APPS_CONFIGURATION = { - }, - [APPS.PROTONMAIL]: { - publicPath: '', -- subdomain: 'mail', -+ subdomain: '', - name: 'ProtonMail', - bareName: 'Mail', - clientID: 'WebMail', -@@ -55,7 +55,7 @@ export const APPS_CONFIGURATION = { - }, - [APPS.PROTONCONTACTS]: { - publicPath: '/contacts', -- subdomain: 'contacts', -+ subdomain: '', - name: 'ProtonContacts', - bareName: 'Contacts', - clientID: 'WebContacts', -@@ -64,7 +64,7 @@ export const APPS_CONFIGURATION = { - }, - [APPS.PROTONDRIVE]: { - publicPath: '/drive', -- subdomain: 'drive', -+ subdomain: '', - name: 'ProtonDrive', - bareName: 'Drive', - clientID: 'WebDrive', -@@ -73,7 +73,7 @@ export const APPS_CONFIGURATION = { - }, - [APPS.PROTONCALENDAR]: { - publicPath: '/calendar', -- subdomain: 'calendar', -+ subdomain: '', - name: 'ProtonCalendar', - bareName: 'Calendar', - clientID: 'WebCalendar', - diff --git a/packages/components/components/link/SettingsLink.tsx b/packages/components/components/link/SettingsLink.tsx index ebce00d20..96498d8c8 100644 --- a/packages/components/components/link/SettingsLink.tsx diff --git a/patches/protonmail/constants-1.patch b/patches/protonmail/constants-1.patch new file mode 100644 index 000000000..f01c589c6 --- /dev/null +++ b/patches/protonmail/constants-1.patch @@ -0,0 +1,51 @@ +diff --git a/packages/shared/lib/constants.ts b/packages/shared/lib/constants.ts +index 2bd472b..52cf521 100644 +--- a/packages/shared/lib/constants.ts ++++ b/packages/shared/lib/constants.ts +@@ -36,8 +36,8 @@ export const APPS = { + } as const; + export const APPS_CONFIGURATION = { + [APPS.PROTONACCOUNT]: { +- publicPath: '', +- subdomain: 'account', ++ publicPath: '/account', ++ subdomain: '', + name: 'Proton Account', + bareName: 'Account', + clientID: 'WebAccount', +@@ -46,7 +46,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONMAIL]: { + publicPath: '', +- subdomain: 'mail', ++ subdomain: '', + name: 'ProtonMail', + bareName: 'Mail', + clientID: 'WebMail', +@@ -55,7 +55,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONCONTACTS]: { + publicPath: '/contacts', +- subdomain: 'contacts', ++ subdomain: '', + name: 'ProtonContacts', + bareName: 'Contacts', + clientID: 'WebContacts', +@@ -64,7 +64,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONDRIVE]: { + publicPath: '/drive', +- subdomain: 'drive', ++ subdomain: '', + name: 'ProtonDrive', + bareName: 'Drive', + clientID: 'WebDrive', +@@ -73,7 +73,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONCALENDAR]: { + publicPath: '/calendar', +- subdomain: 'calendar', ++ subdomain: '', + name: 'ProtonCalendar', + bareName: 'Calendar', + clientID: 'WebCalendar', diff --git a/patches/protonmail/constants-2.patch b/patches/protonmail/constants-2.patch new file mode 100644 index 000000000..6510ae241 --- /dev/null +++ b/patches/protonmail/constants-2.patch @@ -0,0 +1,51 @@ +diff --git a/packages/shared/lib/constants.ts b/packages/shared/lib/constants.ts +index e359e3f66..128155d13 100644 +--- a/packages/shared/lib/constants.ts ++++ b/packages/shared/lib/constants.ts +@@ -38,8 +38,8 @@ export const APPS = { + } as const; + export const APPS_CONFIGURATION = { + [APPS.PROTONACCOUNT]: { +- publicPath: '', +- subdomain: 'account', ++ publicPath: '/account', ++ subdomain: '', + name: 'Proton Account', + bareName: 'Account', + clientID: 'web-account', +@@ -66,7 +66,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONCONTACTS]: { + publicPath: '/contacts', +- subdomain: 'contacts', ++ subdomain: '', + name: 'ProtonContacts', + bareName: 'Contacts', + clientID: 'web-contacts', +@@ -75,7 +75,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONDRIVE]: { + publicPath: '/drive', +- subdomain: 'drive', ++ subdomain: '', + name: 'ProtonDrive', + bareName: 'Drive', + clientID: 'web-drive', +@@ -84,7 +84,7 @@ export const APPS_CONFIGURATION = { + }, + [APPS.PROTONCALENDAR]: { + publicPath: '/calendar', +- subdomain: 'calendar', ++ subdomain: '', + name: 'ProtonCalendar', + bareName: 'Calendar', + clientID: 'web-calendar', +@@ -92,7 +92,7 @@ export const APPS_CONFIGURATION = { + settingsSlug: 'calendar', + }, + [APPS.PROTONVPN_SETTINGS]: { +- publicPath: '', ++ publicPath: 'account/vpn', + subdomain: '', + name: 'ProtonVPN', + bareName: 'VPN', diff --git a/patches/protonmail/meta.json b/patches/protonmail/meta.json index 04e899a2f..5b6a79ba0 100644 --- a/patches/protonmail/meta.json +++ b/patches/protonmail/meta.json @@ -1,40 +1,50 @@ { + "proton-mail": [ + "common.patch", + "constants-2.patch", + "sentry-4.patch", + "captcha-3.patch", + "pack-api-arg-3.patch", + "pack-webpack-config-2.patch", + "session-storage-2.patch", + "proton-mail.patch" + ], "proton-account": [ "common.patch", - "sentry-3.patch", - "captcha-2.patch", - "pack-api-arg.patch", + "constants-2.patch", + "sentry-4.patch", + "captcha-3.patch", + "pack-api-arg-3.patch", + "pack-webpack-config-2.patch", "session-storage-2.patch", "proton-account.patch" ], "proton-calendar": [ "common.patch", - "sentry-3.patch", - "captcha-2.patch", - "pack-api-arg.patch", + "constants-2.patch", + "sentry-4.patch", + "captcha-3.patch", + "pack-api-arg-2.patch", + "pack-webpack-config-1.patch", "session-storage-2.patch" ], "proton-drive": [ "common.patch", - "sentry-3.patch", - "captcha-2.patch", - "pack-api-arg.patch", + "constants-2.patch", + "sentry-4.patch", + "captcha-3.patch", + "pack-api-arg-3.patch", + "pack-webpack-config-2.patch", "session-storage-2.patch", "proton-drive.patch" ], - "proton-mail": [ - "common.patch", - "sentry-3.patch", - "captcha-2.patch", - "pack-api-arg.patch", - "session-storage-2.patch", - "proton-mail.patch" - ], "proton-vpn-settings": [ "common.patch", + "constants-1.patch", "sentry-3.patch", "captcha-2.patch", - "pack-api-arg.patch", + "pack-api-arg-1.patch", + "pack-webpack-config-1.patch", "session-storage-2.patch" ] } diff --git a/patches/protonmail/pack-api-arg.patch b/patches/protonmail/pack-api-arg-1.patch similarity index 100% rename from patches/protonmail/pack-api-arg.patch rename to patches/protonmail/pack-api-arg-1.patch diff --git a/patches/protonmail/pack-api-arg-2.patch b/patches/protonmail/pack-api-arg-2.patch new file mode 100644 index 000000000..f1bdcab00 --- /dev/null +++ b/patches/protonmail/pack-api-arg-2.patch @@ -0,0 +1,33 @@ +diff --git a/packages/pack/bin/protonPack.js b/packages/pack/bin/protonPack.js +index 1c55efdf8..b49014080 100755 +--- a/packages/pack/bin/protonPack.js ++++ b/packages/pack/bin/protonPack.js +@@ -62,6 +62,14 @@ const commandWithLog = (...args) => { + + addGlobalOptions(program.command('build').description('create an optimized production build')) + .option('--no-sri', 'disable sri') ++ .option( ++ '--api ', ++ '', ++ (api) => { ++ return getApi(api); ++ }, ++ getApi('') ++ ) + .action(async (options, env) => { + const configData = getConfigData(options); + await writeConfig(getConfigFile(configData)); + +diff --git a/packages/pack/lib/config.js b/packages/pack/lib/config.js +index 1b026386c..3d4546df2 100644 +--- a/packages/pack/lib/config.js ++++ b/packages/pack/lib/config.js +@@ -128,7 +128,7 @@ const getConfigFile = ({ buildData, appData }) => { + export const BRANCH = '${buildData.branch}'; + export const DATE_VERSION = '${buildData.date}'; + export const APP_NAME = '${appData.appName}'; +- export const API_URL = '/api'; ++ export const API_URL = '${appData.api}'; + export const LOCALES = ${JSON.stringify(LOCALES)}; + export const VERSION_PATH = '${appData.publicPath}assets/version.json'; + export const SENTRY_DSN = '${appData.sentryDsn}'; diff --git a/patches/protonmail/pack-api-arg-3.patch b/patches/protonmail/pack-api-arg-3.patch new file mode 100644 index 000000000..ca3401aeb --- /dev/null +++ b/patches/protonmail/pack-api-arg-3.patch @@ -0,0 +1,32 @@ +diff --git a/packages/pack/bin/protonPack.js b/packages/pack/bin/protonPack.js +index 86d8314c1..9dcb04131 100755 +--- a/packages/pack/bin/protonPack.js ++++ b/packages/pack/bin/protonPack.js +@@ -68,6 +68,14 @@ const commandWithLog = (...args) => { + + addGlobalOptions(program.command('build').description('create an optimized production build')) + .option('--no-sri', 'disable sri') ++ .option( ++ '--api ', ++ '', ++ (api) => { ++ return getApi(api); ++ }, ++ getApi('') ++ ) + .action(async (options, env) => { + console.log(chalk.magenta('Creating a production build...\n')); + +diff --git a/packages/pack/lib/config.js b/packages/pack/lib/config.js +index 1b026386c..3d4546df2 100644 +--- a/packages/pack/lib/config.js ++++ b/packages/pack/lib/config.js +@@ -128,7 +128,7 @@ const getConfigFile = ({ buildData, appData }) => { + export const BRANCH = '${buildData.branch}'; + export const DATE_VERSION = '${buildData.date}'; + export const APP_NAME = '${appData.appName}'; +- export const API_URL = '/api'; ++ export const API_URL = '${appData.api}'; + export const LOCALES = ${JSON.stringify(LOCALES)}; + export const VERSION_PATH = '${appData.publicPath}assets/version.json'; + export const SENTRY_DSN = '${appData.sentryDsn}'; diff --git a/patches/protonmail/pack-webpack-config-1.patch b/patches/protonmail/pack-webpack-config-1.patch new file mode 100644 index 000000000..e8cc0ca1c --- /dev/null +++ b/patches/protonmail/pack-webpack-config-1.patch @@ -0,0 +1,35 @@ +diff --git a/packages/pack/webpack.config.js b/packages/pack/webpack.config.js +index dbfc3b18d..89807e7dd 100644 +--- a/packages/pack/webpack.config.js ++++ b/packages/pack/webpack.config.js +@@ -28,7 +28,20 @@ const getConfig = (env) => { + }, + }; + +- return { ++ return (() => { ++ const file = path.resolve("./proton.config.js"); ++ if (require("fs").existsSync(file)) { ++ console.log( ++ /*reset:*/"\x1b[0m" + ++ /*yellow:*/"\x1b[33m" + ++ ">>> " + ++ /*reset:*/"\x1b[0m", ++ `Found ${file}, extend the config`, ++ ) ++ return require(file); ++ } ++ return (value) => value; ++ })()({ + target: options.isProduction ? `browserslist:${options.browserslist}` : 'web', // dev-server bug https://github.com/webpack/webpack-dev-server/issues/2812 + mode: options.isProduction ? 'production' : 'development', + bail: options.isProduction, +@@ -103,7 +116,7 @@ const getConfig = (env) => { + }, + }), + }, +- }; ++ }); + }; + + module.exports = getConfig; diff --git a/patches/protonmail/pack-webpack-config-2.patch b/patches/protonmail/pack-webpack-config-2.patch new file mode 100644 index 000000000..888d43e22 --- /dev/null +++ b/patches/protonmail/pack-webpack-config-2.patch @@ -0,0 +1,35 @@ +diff --git a/packages/pack/webpack.config.js b/packages/pack/webpack.config.js +index d9dc7c72b..9c9370966 100644 +--- a/packages/pack/webpack.config.js ++++ b/packages/pack/webpack.config.js +@@ -29,7 +29,20 @@ const getConfig = (env) => { + silent: env.silent || false, + }; + +- return { ++ return (() => { ++ const file = path.resolve("./proton.config.js"); ++ if (require("fs").existsSync(file)) { ++ console.log( ++ /*reset:*/"\x1b[0m" + ++ /*yellow:*/"\x1b[33m" + ++ ">>> " + ++ /*reset:*/"\x1b[0m", ++ `Found ${file}, extend the config`, ++ ) ++ return require(file); ++ } ++ return (value) => value; ++ })()({ + target: options.isProduction ? `browserslist:${options.browserslist}` : 'web', // dev-server bug https://github.com/webpack/webpack-dev-server/issues/2812 + mode: options.isProduction ? 'production' : 'development', + bail: options.isProduction, +@@ -115,7 +128,7 @@ const getConfig = (env) => { + }, + }), + }, +- }; ++ }); + }; + + module.exports = getConfig; diff --git a/patches/protonmail/proton-account.patch b/patches/protonmail/proton-account.patch index c1dfa3bf4..0c94d839a 100644 --- a/patches/protonmail/proton-account.patch +++ b/patches/protonmail/proton-account.patch @@ -36,15 +36,16 @@ index 19828d5..0d7ce23 100644 export default Setup; diff --git a/applications/account/src/app/content/MainContainer.tsx b/applications/account/src/app/content/MainContainer.tsx -index 73380a9..75922b7 100644 +index f14e76d5a..b21b54c97 100644 --- a/applications/account/src/app/content/MainContainer.tsx +++ b/applications/account/src/app/content/MainContainer.tsx -@@ -43,7 +43,7 @@ const MainContainer = () => { +@@ -91,7 +91,7 @@ const MainContainer = () => { setExpand(false); }, [location.pathname, location.hash]); -- const app = getAppFromPathnameSafe(location.pathname); -+ const app = getAppFromPathnameSafe(window.location.pathname); +- const app = getAppFromPathnameSafe(location.pathname) || DEFAULT_APP; ++ const app = getAppFromPathnameSafe(window.location.pathname) || DEFAULT_APP; + const appSlug = getSlugFromApp(app); + + /* - if (!app) { - return ; diff --git a/patches/protonmail/proton-drive.patch b/patches/protonmail/proton-drive.patch index 83e5849e1..7c1457cdb 100644 --- a/patches/protonmail/proton-drive.patch +++ b/patches/protonmail/proton-drive.patch @@ -1,7 +1,7 @@ -diff --git a/applications/drive/src/app/components/downloads/fileSaver/download.ts b/applications/drive/src/app/components/downloads/fileSaver/download.ts -index 92eb4e5e2..df95b7a7f 100644 ---- a/applications/drive/src/app/components/downloads/fileSaver/download.ts -+++ b/applications/drive/src/app/components/downloads/fileSaver/download.ts +diff --git a/applications/drive/src/app/store/downloads/fileSaver/download.ts b/applications/drive/src/app/store/downloads/fileSaver/download.ts +index 7bef98525..0650e8019 100644 +--- a/applications/drive/src/app/store/downloads/fileSaver/download.ts ++++ b/applications/drive/src/app/store/downloads/fileSaver/download.ts @@ -11,8 +11,7 @@ import { TransferMeta } from '@proton/shared/lib/interfaces/drive/transfer'; * IOS - forces all browsers to use webkit, so same problems as safari in all browsers. * For them download is done in-memory using blob response. @@ -13,11 +13,11 @@ index 92eb4e5e2..df95b7a7f 100644 // createDownloadIframe opens download URL created in service worker to // initialize the download in the browser. The response has headers to -diff --git a/applications/drive/src/app/utils/link.ts b/applications/drive/src/app/utils/link.ts -index 31b5d775d..c08a2317d 100644 ---- a/applications/drive/src/app/utils/link.ts -+++ b/applications/drive/src/app/utils/link.ts -@@ -65,7 +65,7 @@ export const getSharedLink = (sharedURL?: { Token: string; Password: string; Fla +diff --git a/applications/drive/src/app/store/shares/shareUrl.ts b/applications/drive/src/app/store/shares/shareUrl.ts +index 4a2fa5caa..95884ed0d 100644 +--- a/applications/drive/src/app/store/shares/shareUrl.ts ++++ b/applications/drive/src/app/store/shares/shareUrl.ts +@@ -35,6 +35,6 @@ export const getSharedLink = (sharedURL?: { Token: string; Password: string; Fla const [generatedPassword] = splitGeneratedAndCustomPassword(sharedURL.Password, sharedURL); @@ -25,4 +25,3 @@ index 31b5d775d..c08a2317d 100644 + const baseUrl = "https://drive.protonmail.com/urls"; return `${baseUrl}/${sharedURL.Token}${generatedPassword !== '' ? `#${generatedPassword}` : ''}`; }; - diff --git a/patches/protonmail/proton-mail.patch b/patches/protonmail/proton-mail.patch index 3f6a0b594..f4cd0e21e 100644 --- a/patches/protonmail/proton-mail.patch +++ b/patches/protonmail/proton-mail.patch @@ -36,165 +36,25 @@ index 6fef05c..1719cc8 100644 {hasChallenge && ( -+
- {showToolbar && ( - - - loadingLabels || loadingFolders || loadingMailSettings || loadingESFeature || loadingScheduledFeature; - - // Switches -- const showEncryptedSearch = !isMobile() && !!esFeature && !!esFeature.Value && !!isPaid(user); -+ const showEncryptedSearch = false; - const showAdvancedSearch = !showEncryptedSearch || showMore; - const dropdownSearchButtonProps = { - ref: anchorRef, - -diff --git a/applications/mail/src/app/containers/EncryptedSearchProvider.tsx b/applications/mail/src/app/containers/EncryptedSearchProvider.tsx -index 444313146..4ed701c49 100644 ---- a/applications/mail/src/app/containers/EncryptedSearchProvider.tsx -+++ b/applications/mail/src/app/containers/EncryptedSearchProvider.tsx -@@ -717,125 +717,7 @@ const EncryptedSearchProvider = ({ children }: Props) => { - /** - * Execute an encrypted search - */ -- const encryptedSearch: EncryptedSearch = async (labelID, setCache) => { -- // Prevent old searches from interfering with newer ones -- abortSearchingRef.current.abort(); -- setESStatus((esStatus) => { -- return { -- ...esStatus, -- isSearching: false, -- isSearchPartial: false, -- }; -- }); -- -- const t1 = performance.now(); -- const { -- dbExists, -- esEnabled, -- previousNormSearchParams, -- permanentResults, -- isSearchPartial: wasSearchPartial, -- cachedIndexKey, -- isCaching, -- isFirstSearch, -- } = esStatus; -- -- if (!dbExists || !esEnabled) { -- return false; -- } -- -- const isIDBIntact = await canUseES(userID); -- if (!isIDBIntact) { -- return dbCorruptError().then(() => false); -- } -- -- abortSearchingRef.current = new AbortController(); -- -- // Caching needs to be triggered here for when a refresh happens on a search URL -- if (!isCaching && !esCacheRef.current.isCacheReady) { -- void cacheIndexedDB(); -- } -- -- const { searchParameters, filterParameter, sortParameter } = parseSearchParams(history.location); -- const normalisedSearchParams = normaliseSearchParams(searchParameters, labelID, filterParameter, sortParameter); -- -- // In case only sorting changed, for complete searches it doesn't make sense to perform a new search -- if (!wasSearchPartial && previousNormSearchParams) { -- const shouldSortOnly = shouldOnlySortResults(normalisedSearchParams, previousNormSearchParams); -- if (shouldSortOnly) { -- setCache(permanentResults, pageRef.current); -- return true; -- } -- } -- -- setESStatus((esStatus) => { -- return { -- ...esStatus, -- isSearching: true, -- isSearchPartial: true, -- isFirstSearch: false, -- }; -- }); -- -- const controlledSetCache = (Elements: Element[]) => { -- if (!abortSearchingRef.current.signal.aborted) { -- setCache(Elements, pageRef.current); -- } -- }; -- -- let searchResults: ESMessage[] = []; -- let isSearchPartial = false; -- let lastEmail: LastEmail | undefined; -- try { -- ({ searchResults, isSearchPartial, lastEmail } = await hybridSearch( -- esCacheRef, -- normalisedSearchParams, -- cachedIndexKey, -- getUserKeys, -- userID, -- controlledSetCache, -- abortSearchingRef -- )); -- } catch (error: any) { -- esSentryReport('encryptedSearch: hybridSearch', { error }); -- // If the key is the problem, then we want to wipe the DB and fall back to -- // server-side search, otherwise we want to show a generic error and still -- // fall back to server-side search -- if (error.message === 'Key not found') { -- return dbCorruptError().then(() => false); -- } -- throw error; -- } -- -- if (!abortSearchingRef.current.signal.aborted) { -- setESStatus((esStatus) => { -- return { -- ...esStatus, -- permanentResults: searchResults, -- labelID, -- setElementsCache: setCache, -- lastEmail, -- previousNormSearchParams: normalisedSearchParams, -- page: 0, -- isSearchPartial, -- isSearching: false, -- }; -- }); -- setCache(searchResults, pageRef.current); -- -- const t2 = performance.now(); -- void sendSearchingMetrics( -- api, -- userID, -- esCacheRef.current.cacheSize, -- Math.ceil(t2 - t1), -- isFirstSearch, -- esCacheRef.current.isCacheLimited -- ); -- } -- -- return true; -- }; -+ const encryptedSearch: EncryptedSearch = async (labelID, setCache) => false; +diff --git a/packages/encrypted-search/lib/esUtils.ts b/packages/encrypted-search/lib/esUtils.ts +index 565746063..24978868a 100644 +--- a/packages/encrypted-search/lib/esUtils.ts ++++ b/packages/encrypted-search/lib/esUtils.ts +@@ -280,13 +280,7 @@ export const updateSizeIDB = (userID: string, addend: number) => { + * but also because IDB is not corrupt, i.e. the object store exists + */ + export const canUseES = async (userID: string, storeName: string) => { +- if (!indexKeyExists(userID)) { +- return false; +- } +- const esDB = await openESDB(userID); +- const isIntact = esDB.objectStoreNames.contains(storeName); +- esDB.close(); +- return isIntact; ++ return Promise.resolve(false); + }; - /** - * Increase the number of results in case the cache is limited as the user changes page + /** diff --git a/applications/mail/src/app/App.tsx b/applications/mail/src/app/App.tsx index 3da997386..c629fde0d 100644 @@ -221,45 +81,113 @@ index 3da997386..c629fde0d 100644 const [hasInitialAuth] = useState(() => { return !window.location.pathname.startsWith(G_OAUTH_REDIRECT_PATH); +diff --git a/applications/mail/src/app/components/header/AdvancedSearchDropdown.tsx b/applications/mail/src/app/components/header/AdvancedSearchDropdown.tsx +index 7da0f183e..c07f51d91 100644 +--- a/applications/mail/src/app/components/header/AdvancedSearchDropdown.tsx ++++ b/applications/mail/src/app/components/header/AdvancedSearchDropdown.tsx +@@ -200,7 +200,7 @@ const AdvancedSearchDropdown = ({ keyword: fullInput = '', isNarrow }: Props) => + loadingScheduledFeature; + + // Switches +- const showEncryptedSearch = !isMobile() && !!esFeature && !!esFeature.Value && !!isPaid(user); ++ const showEncryptedSearch = false; + const showAdvancedSearch = !showEncryptedSearch || showMore; + const dropdownSearchButtonProps = { + ref: anchorRef, + +diff --git a/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx b/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx +index 562696c4e..6531defba 100644 +--- a/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx ++++ b/applications/mail/src/app/containers/mailbox/MailboxContainer.tsx +@@ -247,6 +247,7 @@ const MailboxContainer = ({ + tabIndex={-1} + className="flex-item-fluid flex flex-column flex-nowrap outline-none" + data-testid="mailbox" ++ electron-mail-mailbox-container-component + > + {showToolbar && ( + + +diff --git a/applications/mail/src/app/helpers/attachment/attachmentLoader.ts b/applications/mail/src/app/helpers/attachment/attachmentLoader.ts +index c7d094d1b..3667af1eb 100644 +--- a/applications/mail/src/app/helpers/attachment/attachmentLoader.ts ++++ b/applications/mail/src/app/helpers/attachment/attachmentLoader.ts +@@ -48,6 +48,7 @@ export const getRequest = ({ ID = '' }: Attachment = {}, api: Api, messageKeys: + return api(getAttachment(ID)); + }; + ++/* electron-mail mark */ + export const getDecryptedAttachment = async ( + attachment: Attachment, + verification: MessageVerification | undefined, +@@ -82,6 +83,7 @@ export const getDecryptedAttachment = async ( + throw newError; + } + }; ++/* electron-mail mark */ + + export const getAndVerify = async ( + attachment: Attachment = {}, + diff --git a/applications/mail/src/app/helpers/message/messageDecrypt.ts b/applications/mail/src/app/helpers/message/messageDecrypt.ts -index d135a42fd..0c01fb0a0 100644 +index 375be1b51..a6bd8390e 100644 --- a/applications/mail/src/app/helpers/message/messageDecrypt.ts +++ b/applications/mail/src/app/helpers/message/messageDecrypt.ts -@@ -141,12 +141,12 @@ const decryptLegacyMessage = async (message: Message, privateKeys: OpenPGPKey[]) +@@ -47,6 +47,7 @@ const binaryToString = (data: Uint8Array) => + // nbsp can be contained in message body and "crash" DOMPurify + .replace(/\u00A0/g, ' '); + ++/* electron-mail mark */ + export interface DecryptMessageResult { + decryptedBody: string; + decryptedRawContent: Uint8Array; +@@ -56,6 +57,7 @@ export interface DecryptMessageResult { + errors?: MessageErrors; + mimetype?: MIME_TYPES; + } ++/* electron-mail mark */ + + const decryptMimeMessage = async ( + message: Message, +@@ -164,6 +166,7 @@ const decryptLegacyMessage = async ( * Willingly not dealing with public keys and signature verification * It will be done separately when public keys will be ready */ --export const decryptMessage = async ( -+export const decryptMessage = async ( /* electron-mail mark */ ++/* electron-mail mark */ + export const decryptMessage = async ( message: Message, privateKeys: OpenPGPKey[], - getAttachment?: (ID: string) => DecryptResultPmcrypto | undefined, - onUpdateAttachment?: (ID: string, attachment: DecryptResultPmcrypto) => void --): Promise => { -+): Promise => { /* electron-mail mark */ - if (isMIME(message)) { - return decryptMimeMessage(message, privateKeys, getAttachment, onUpdateAttachment); +@@ -176,6 +179,7 @@ export const decryptMessage = async ( } - -diff --git a/applications/mail/src/app/helpers/attachment/attachmentLoader.ts b/applications/mail/src/app/helpers/attachment/attachmentLoader.ts -index aff462ffb..dc1b62d4a 100644 ---- a/applications/mail/src/app/helpers/attachment/attachmentLoader.ts -+++ b/applications/mail/src/app/helpers/attachment/attachmentLoader.ts -@@ -44,12 +44,12 @@ export const getRequest = ({ ID = '' }: Attachment = {}, api: Api): Promise => { -+): Promise => { /* electron-mail mark */ - const encryptedBinary = await getRequest(attachment, api); - try { - const sessionKey = await getSessionKey(attachment, messageKeys.privateKeys); + /** + * Verify the extracted `signature` of a decryption result against its `decryptedRawContent` + +diff --git a/applications/mail/src/app/logic/messages/messagesTypes.ts b/applications/mail/src/app/logic/messages/messagesTypes.ts +index 91dcf4e63..aa1a6a1cf 100644 +--- a/applications/mail/src/app/logic/messages/messagesTypes.ts ++++ b/applications/mail/src/app/logic/messages/messagesTypes.ts +@@ -44,6 +44,8 @@ export interface MessageVerification { + */ + verificationErrors: Error[] | undefined; + ++ /* electron-mail mark */ ++ + /** + * Pinned public keys of the sender, if any + */ +@@ -54,6 +56,8 @@ export interface MessageVerification { + */ + senderVerified: boolean | undefined; + ++ /* electron-mail mark */ ++ + /** + * If the message is signed, the public key that verifies the signature + */ diff --git a/applications/mail/src/app/hooks/message/useGetMessageKeys.ts b/applications/mail/src/app/hooks/message/useGetMessageKeys.ts index 4c16c85b5..8b93c6084 100644 diff --git a/patches/protonmail/sentry-2.patch b/patches/protonmail/sentry-4.patch similarity index 89% rename from patches/protonmail/sentry-2.patch rename to patches/protonmail/sentry-4.patch index b9a356db4..d23f669ed 100644 --- a/patches/protonmail/sentry-2.patch +++ b/patches/protonmail/sentry-4.patch @@ -1,8 +1,8 @@ diff --git a/packages/shared/lib/helpers/sentry.ts b/packages/shared/lib/helpers/sentry.ts -index 5664f0fa6..375d20e29 100644 +index aff0d3010..375d20e29 100644 --- a/packages/shared/lib/helpers/sentry.ts +++ b/packages/shared/lib/helpers/sentry.ts -@@ -1,95 +1,12 @@ +@@ -1,96 +1,12 @@ import * as Sentry from '@sentry/browser'; import { ProtonConfig } from '../interfaces'; @@ -21,13 +21,13 @@ index 5664f0fa6..375d20e29 100644 - } - - // Assumes SENTRY_DSN is: https://111b3eeaaec34cae8e812df705690a36@sentry/11 -- // To get https://111b3eeaaec34cae8e812df705690a36@mail.protonmail.com/api/reports/sentry/11 -- const dsn = SENTRY_DSN.replace('sentry', `${host}/api/reports/sentry`); +- // To get https://111b3eeaaec34cae8e812df705690a36@protonmail.com/api/core/v4/reports/sentry/11 +- const dsn = SENTRY_DSN.replace('sentry', `${host}/api/core/v4/reports/sentry`); - - Sentry.init({ - dsn, - release: isProduction(host) ? APP_VERSION : COMMIT, -- environment: host, +- environment: host.split('.').splice(1).join('.'), - normalizeDepth: 5, - beforeSend(event, hint) { - const error = hint?.originalException; @@ -77,6 +77,7 @@ index 5664f0fa6..375d20e29 100644 - 'UploadConflictError', // User uploading the same file again in Drive. - 'UploadUserError', // Upload error on user's side in Drive. - 'ChunkLoadError', // WebPack loading source code. +- 'ResizeObserver loop limit exceeded', // Chromium bug https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded - ], - }); - diff --git a/patches/protonmail/session-storage-1.patch b/patches/protonmail/session-storage-1.patch deleted file mode 100644 index 5a93e9ec5..000000000 --- a/patches/protonmail/session-storage-1.patch +++ /dev/null @@ -1,51 +0,0 @@ -diff --git a/packages/shared/lib/helpers/secureSessionStorage.ts b/packages/shared/lib/helpers/secureSessionStorage.ts -index 486337ccf..5a59ae185 100644 ---- a/packages/shared/lib/helpers/secureSessionStorage.ts -+++ b/packages/shared/lib/helpers/secureSessionStorage.ts -@@ -138,7 +138,7 @@ export const separateParts = (data: any) => - }, - { share1: {}, share2: {} } - ); -- -+/* electron-mail mark */ - export const save = (keys: string[], data: any) => { - if (!hasSessionStorage()) { - return; -@@ -192,3 +192,4 @@ export const load2 = () => { - return {}; - } - }; -+/* electron-mail mark */ - -diff --git a/packages/components/containers/app/ProtonApp.tsx b/packages/components/containers/app/ProtonApp.tsx -index c9e13872e..ddabc9259 100644 ---- a/packages/components/containers/app/ProtonApp.tsx -+++ b/packages/components/containers/app/ProtonApp.tsx -@@ -105,10 +105,12 @@ interface Props { - - const ProtonApp = ({ config, children, hasInitialAuth }: Props) => { - const authentication = useInstance(() => { -+ /* electron-mail mark */ - if (isSSOMode) { - return createAuthentication(createSecureSessionStorage2()); - } - return createAuthentication(createSecureSessionStorage([MAILBOX_PASSWORD_KEY, UID_KEY])); -+ /* electron-mail mark */ - }); - const pathRef = useRef(); - const cacheRef = useRef>(); - -diff --git a/packages/shared/lib/authentication/createSecureSessionStorage.ts b/packages/shared/lib/authentication/createSecureSessionStorage.ts -index 845f4a517..7883422f1 100644 ---- a/packages/shared/lib/authentication/createSecureSessionStorage.ts -+++ b/packages/shared/lib/authentication/createSecureSessionStorage.ts -@@ -1,3 +1,4 @@ -+/* electron-mail mark */ - import createStore from '../helpers/store'; - import { load, save } from '../helpers/secureSessionStorage'; - -@@ -30,3 +31,4 @@ const createSecureSessionStorage = (keys: string[] = []) => { - export type SecureSessionStorage = ReturnType; - - export default createSecureSessionStorage; -+/* electron-mail mark */ diff --git a/scripts/prepare-webclient/webclients.ts b/scripts/prepare-webclient/webclients.ts index 97f37474a..6bfee5797 100644 --- a/scripts/prepare-webclient/webclients.ts +++ b/scripts/prepare-webclient/webclients.ts @@ -47,7 +47,7 @@ const reposOnlyFilter: DeepReadonly<{ value: Array { const {configApiParam} = options; @@ -55,7 +55,6 @@ async function configure( writeFile( path.join(cwd, envFileName), JSON.stringify({ - appConfig: PROVIDER_REPO_MAP[repoType].protonPack.appConfig, [configApiParam]: { // https://github.com/ProtonMail/WebClient/issues/166#issuecomment-561060855 api: `https://${folderNameAsDomain}/api`, @@ -73,10 +72,12 @@ async function configure( function resolveWebpackConfigPatchingCode( { webpackConfigVarName, + repoType, webpackIndexEntryItems, }: { webpackConfigVarName: string - webpackIndexEntryItems?: unknown + repoType: keyof typeof PROVIDER_REPO_MAP, + webpackIndexEntryItems?: unknown, }, ): string { const disableMangling = Boolean(webpackIndexEntryItems); @@ -120,7 +121,9 @@ function resolveWebpackConfigPatchingCode( // terserPluginInstance.options.minify = false; terserPluginInstance.options.parallel = false; Object.assign( - terserPluginInstance.options.terserOptions, + ${repoType === "proton-drive" || repoType === "proton-vpn-settings" + ? 'terserPluginInstance.options.terserOptions' + : 'terserPluginInstance.options.minimizer.options'}, { // proton v4: needed to preserve original function names // just "{keep_fnames: true, mangle: false}" is not sufficient @@ -313,7 +316,7 @@ async function executeBuildFlow( } else { // building await state.buildingSetup(); - const {configApiParam} = await configure({cwd: appDir, repoType}, folderAsDomainEntry); + const {configApiParam} = await configure({cwd: appDir}, folderAsDomainEntry); const publicPath: string | undefined = PROVIDER_REPO_MAP[repoType].basePath ? `/${PROVIDER_REPO_MAP[repoType].basePath}/` : undefined; @@ -331,6 +334,7 @@ async function executeBuildFlow( ${ resolveWebpackConfigPatchingCode({ webpackConfigVarName: "webpackConfig", + repoType, webpackIndexEntryItems, }) } diff --git a/src/electron-preload/webview/primary/provider-api/index.ts b/src/electron-preload/webview/primary/provider-api/index.ts index db92da384..7ebafa07a 100644 --- a/src/electron-preload/webview/primary/provider-api/index.ts +++ b/src/electron-preload/webview/primary/provider-api/index.ts @@ -4,7 +4,7 @@ import {distinctUntilChanged, first, map, mergeMap} from "rxjs/operators"; import {assertTypeOf, curryFunctionMembers} from "src/shared/util"; import {attachRateLimiting} from "./rate-limiting"; -import {EncryptionPreferences, MessageKeys, ProviderApi} from "./model"; +import {EncryptionPreferences, MessageVerification, ProviderApi} from "./model"; import {FETCH_NOTIFICATION_SKIP_SYMBOL} from "./const"; import {HttpApi, resolveStandardSetupPublicApi} from "src/electron-preload/webview/lib/provider-api/standart-setup-internals"; import {Logger} from "src/shared/model/common"; @@ -106,13 +106,15 @@ export const initProviderApi = async (): Promise => { async decryptMessage(message) { const privateApi = await resolvePrivateApi(); const messageKeys = await privateApi.getMessageKeys(message); - const { - decryptedSubject, - decryptedBody - } = await internals["./src/app/helpers/message/messageDecrypt.ts"].value.decryptMessage( + const decryptMessage = await internals["./src/app/helpers/message/messageDecrypt.ts"].value.decryptMessage( message, messageKeys.privateKeys, ); + if (decryptMessage.errors) { + logger.error(decryptMessage.errors); + throw new Error("Failed to decrypt a message"); + } + const {decryptedSubject, decryptedBody} = decryptMessage; if (typeof decryptedBody !== "string") { throw new Error("Invalid message body content"); } @@ -202,22 +204,20 @@ export const initProviderApi = async (): Promise => { }, attachmentLoader: { getDecryptedAttachment: (() => { - const constructExtendedMessage = ( + const constructMessageVerification = ( encryptionPreferences: EncryptionPreferences, - messageKeys: MessageKeys, - ): Parameters>["getDecryptedAttachment"]>[1] => { - const extendedMessage: ReturnType = { + ): NoExtraProps => { + const result = { senderPinnedKeys: encryptionPreferences.pinnedKeys, senderVerified: Boolean(encryptionPreferences.isContactSignatureVerified), - privateKeys: messageKeys.privateKeys, - }; + } as const; // this proxy helps early detecting unexpected/not-yet-reviewed protonmail's "getDecryptedAttachment" behaviour // if/likely-when the behaviour gets changed by protonmail return new Proxy( - extendedMessage, + result, { get(target, prop) { - if (!(prop in extendedMessage)) { + if (!(prop in result)) { throw new Error([ "Unexpected email message prop accessing detected", `during the attachment download (${JSON.stringify({prop})})`, @@ -240,8 +240,13 @@ export const initProviderApi = async (): Promise => { privateApi.getMessageKeys(message), privateApi.getEncryptionPreferences(message.Sender.Address), ]); - const extendedMessage = constructExtendedMessage(encryptionPreferences, messageKeys); - const {data} = await privateApi.getDecryptedAttachment(attachment, extendedMessage, messageKeys, protonApi); + const verification = constructMessageVerification(encryptionPreferences); + const {data} = await privateApi.getDecryptedAttachment( + attachment, + verification, + messageKeys, + protonApi, + ); // the custom error also has the "data" prop, so this test won't suppress/override the custom error // so this test should help detecting at early stage the protonmail's code change diff --git a/src/electron-preload/webview/primary/provider-api/model.ts b/src/electron-preload/webview/primary/provider-api/model.ts index 8c2a6c6de..4fb59297d 100644 --- a/src/electron-preload/webview/primary/provider-api/model.ts +++ b/src/electron-preload/webview/primary/provider-api/model.ts @@ -28,25 +28,30 @@ export type ProviderInternals = AddInitializedProp<{ // https://github.com/ProtonMail/proton-mail/blob/77b133013cdb5695aa23c0c4c29cc6578878faa5/src/app/helpers/attachment/attachmentLoader.ts#L46 readonly getDecryptedAttachment: ( attachment: RestModel.Attachment, - message: NoExtraProps, - // only actually accessed props get listed here - | "senderPinnedKeys" - | "senderVerified" - | "privateKeys">>, + verification: MessageExtended["verification"] | undefined, messageKeys: MessageKeys, - api: HttpApi + api: HttpApi, ) => Promise<{ data: Uint8Array }> } }, (arg: unknown) => import("react").ReactNode> } & WrapToValueProp<{ [K in StrictExtract]: { - // https://github.com/ProtonMail/WebClients/blob/251c8d9e990a14fd30be196a59852953d0189e2c/applications/mail/src/app/helpers/message/messageDecrypt.ts#L144 + // https://github.com/ProtonMail/WebClients/blob/03822ade27ff3cbaa7549492232f290cb14924e8/applications/mail/src/app/helpers/message/messageDecrypt.ts#L167 readonly decryptMessage: ( message: RestModel.Message, privateKeys: MessageKeys["privateKeys"], // getAttachment?: (ID: string) => DecryptResultPmcrypto | undefined, - // onUpdateAttachment?: (ID: string, attachment: DecryptResultPmcrypto) => void - ) => Promise<{ readonly decryptedSubject?: string, readonly decryptedBody?: string }> + // onUpdateAttachment?: (ID: string, attachment: DecryptResultPmcrypto) => void, + // password?: string + ) => Promise> } } & { [K in StrictExtract]: { @@ -198,9 +203,12 @@ export interface MessageKeys { readonly privateKeys: readonly unknown[] } +export interface MessageVerification { + senderPinnedKeys: EncryptionPreferences["pinnedKeys"] | undefined; + senderVerified: boolean | undefined; +} + export type MessageExtended = NoExtraProps<{ readonly data?: DeepReadonly - readonly senderPinnedKeys?: EncryptionPreferences["pinnedKeys"] - readonly senderVerified?: EncryptionPreferences["isContactSignatureVerified"] - readonly privateKeys?: MessageKeys["privateKeys"] + readonly verification?: MessageVerification }>; diff --git a/src/shared/proton-apps-constants.ts b/src/shared/proton-apps-constants.ts index b33986afb..d13034190 100644 --- a/src/shared/proton-apps-constants.ts +++ b/src/shared/proton-apps-constants.ts @@ -21,9 +21,8 @@ export const PROVIDER_REPO_MAP = { [PROVIDER_APP_NAMES[0]]: { repoRelativeDistDir: "./dist", basePath: "", - tag: "proton-mail@4.13.6", + tag: "proton-mail@4.17.0", protonPack: { - appConfig: {clientId: "WebMail"}, webpackIndexEntryItems: [ // immediate "../../packages/shared/lib/api/contacts.ts", @@ -48,15 +47,14 @@ export const PROVIDER_REPO_MAP = { [PROVIDER_APP_NAMES[1]]: { repoRelativeDistDir: "./dist", basePath: "account", - tag: "proton-account@4.16.1", - protonPack: {appConfig: {clientId: "WebAccount"}} + tag: "proton-account@4.21.0", + protonPack: {} }, [PROVIDER_APP_NAMES[2]]: { repoRelativeDistDir: "./dist", basePath: "calendar", - tag: "proton-calendar@4.9.2", + tag: "proton-calendar@4.10.2", protonPack: { - appConfig: {clientId: "WebCalendar"}, webpackIndexEntryItems: [ // immediate "./src/app/content/PrivateApp.tsx", @@ -67,14 +65,14 @@ export const PROVIDER_REPO_MAP = { [PROVIDER_APP_NAMES[3]]: { repoRelativeDistDir: "./dist", basePath: "drive", - tag: "proton-drive@4.8.0", - protonPack: {appConfig: {clientId: "WebDrive"}}, + tag: "proton-drive@4.10.0", + protonPack: {}, }, [PROVIDER_APP_NAMES[4]]: { repoRelativeDistDir: "./dist", basePath: "account/vpn", tag: "proton-vpn-settings@4.13.0", - protonPack: {appConfig: {clientId: "WebVPNSettings"}}, + protonPack: {}, }, } as const;