diff options
Diffstat (limited to 'packages/main/src')
-rw-r--r-- | packages/main/src/index.ts | 56 | ||||
-rw-r--r-- | packages/main/src/userAgent.ts | 61 |
2 files changed, 51 insertions, 66 deletions
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index b6df67b..70acb15 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts | |||
@@ -19,6 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { app, BrowserView, BrowserWindow } from 'electron'; | 21 | import { app, BrowserView, BrowserWindow } from 'electron'; |
22 | import { readFile, readFileSync } from 'fs'; | ||
22 | import { autorun } from 'mobx'; | 23 | import { autorun } from 'mobx'; |
23 | import { getSnapshot, onPatch } from 'mobx-state-tree'; | 24 | import { getSnapshot, onPatch } from 'mobx-state-tree'; |
24 | import { join } from 'path'; | 25 | import { join } from 'path'; |
@@ -39,7 +40,6 @@ import { | |||
39 | openDevToolsWhenReady, | 40 | openDevToolsWhenReady, |
40 | } from './devTools'; | 41 | } from './devTools'; |
41 | import { createRootStore } from './stores/RootStore'; | 42 | import { createRootStore } from './stores/RootStore'; |
42 | import { reduceUserAgent } from './userAgent'; | ||
43 | 43 | ||
44 | const isDevelopment = import.meta.env.MODE === 'development'; | 44 | const isDevelopment = import.meta.env.MODE === 'development'; |
45 | 45 | ||
@@ -67,12 +67,33 @@ app.commandLine.appendSwitch( | |||
67 | 67 | ||
68 | // Remove sophie and electron from the user-agent string to avoid detection. | 68 | // Remove sophie and electron from the user-agent string to avoid detection. |
69 | const originalUserAgent = app.userAgentFallback; | 69 | const originalUserAgent = app.userAgentFallback; |
70 | const userAgent = reduceUserAgent(originalUserAgent); | 70 | const userAgent = originalUserAgent.replaceAll(/\s(sophie|Electron)\/\S+/g, ''); |
71 | const platformInUa = userAgent.match(/\((Win|Mac|X11; L)/); | ||
72 | let platform = 'Unknown'; | ||
73 | if (platformInUa !== null) { | ||
74 | switch (platformInUa[1]) { | ||
75 | case 'Win': | ||
76 | platform = 'Windows'; | ||
77 | break; | ||
78 | case 'Mac': | ||
79 | platform = 'macOS'; | ||
80 | break; | ||
81 | case 'X11; L': | ||
82 | platform = 'Linux'; | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | const chromiumVersion = process.versions.chrome.split('.')[0]; | ||
71 | // Removing the electron version breaks redux devtools, so we only do this in production. | 87 | // Removing the electron version breaks redux devtools, so we only do this in production. |
72 | if (!isDevelopment) { | 88 | if (!isDevelopment) { |
73 | app.userAgentFallback = userAgent; | 89 | app.userAgentFallback = userAgent; |
74 | } | 90 | } |
75 | 91 | ||
92 | let serviceInjectRelativePath = '../../service-inject/dist/index.cjs'; | ||
93 | let serviceInjectPath = join(__dirname, serviceInjectRelativePath); | ||
94 | let serviceInjectUrl = new URL(serviceInjectRelativePath, `file://${__dirname}`).toString(); | ||
95 | let serviceInject: string = readFileSync(serviceInjectPath, 'utf8'); | ||
96 | |||
76 | if (isDevelopment) { | 97 | if (isDevelopment) { |
77 | installDevToolsExtensions(app); | 98 | installDevToolsExtensions(app); |
78 | } | 99 | } |
@@ -92,7 +113,7 @@ function createWindow(): Promise<unknown> { | |||
92 | }); | 113 | }); |
93 | 114 | ||
94 | if (isDevelopment) { | 115 | if (isDevelopment) { |
95 | openDevToolsWhenReady(mainWindow); | 116 | //openDevToolsWhenReady(mainWindow); |
96 | } | 117 | } |
97 | 118 | ||
98 | mainWindow.on('ready-to-show', () => { | 119 | mainWindow.on('ready-to-show', () => { |
@@ -131,7 +152,14 @@ function createWindow(): Promise<unknown> { | |||
131 | store.setPaletteMode(paletteMode.parse(args[0])) | 152 | store.setPaletteMode(paletteMode.parse(args[0])) |
132 | break; | 153 | break; |
133 | case RendererToMainIpcMessage.ReloadAllServices: | 154 | case RendererToMainIpcMessage.ReloadAllServices: |
134 | browserView.webContents.reload(); | 155 | readFile(serviceInjectPath, 'utf8', (err, data) => { |
156 | if (err === null) { | ||
157 | serviceInject = data; | ||
158 | } else { | ||
159 | console.error('Error while reloading', serviceInjectPath, err); | ||
160 | } | ||
161 | browserView.webContents.reload(); | ||
162 | }); | ||
135 | break; | 163 | break; |
136 | default: | 164 | default: |
137 | console.error('Unknown IPC message:', channel, args); | 165 | console.error('Unknown IPC message:', channel, args); |
@@ -149,6 +177,16 @@ function createWindow(): Promise<unknown> { | |||
149 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { | 177 | browserView.webContents.on('ipc-message', (_event, channel, ...args) => { |
150 | try { | 178 | try { |
151 | switch (channel) { | 179 | switch (channel) { |
180 | case ServiceToMainIpcMessage.ApiExposedInMainWorld: | ||
181 | browserView.webContents.executeJavaScriptInIsolatedWorld(0, [ | ||
182 | { | ||
183 | code: serviceInject, | ||
184 | url: serviceInjectUrl, | ||
185 | } | ||
186 | ]).catch((err) => { | ||
187 | console.error('Cannot inject script:', err); | ||
188 | }); | ||
189 | break; | ||
152 | case ServiceToMainIpcMessage.SetUnreadCount: | 190 | case ServiceToMainIpcMessage.SetUnreadCount: |
153 | console.log('Unread count:', unreadCount.parse(args[0])); | 191 | console.log('Unread count:', unreadCount.parse(args[0])); |
154 | break; | 192 | break; |
@@ -172,13 +210,21 @@ function createWindow(): Promise<unknown> { | |||
172 | ); | 210 | ); |
173 | }); | 211 | }); |
174 | 212 | ||
213 | browserView.webContents.session.webRequest.onBeforeSendHeaders(({ requestHeaders }, callback) => { | ||
214 | requestHeaders['User-Agent'] = userAgent; | ||
215 | requestHeaders['Sec-CH-UA'] = `" Not A;Brand";v="99", "Chromium";v="${chromiumVersion}"`; | ||
216 | requestHeaders['Sec-CH-UA-Mobile'] = '?0'; | ||
217 | requestHeaders['Sec-CH-UA-Platform'] = platform; | ||
218 | callback({ requestHeaders }); | ||
219 | }); | ||
220 | |||
175 | const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) | 221 | const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) |
176 | ? import.meta.env.VITE_DEV_SERVER_URL | 222 | ? import.meta.env.VITE_DEV_SERVER_URL |
177 | : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString(); | 223 | : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString(); |
178 | 224 | ||
179 | return Promise.all([ | 225 | return Promise.all([ |
180 | mainWindow.loadURL(pageUrl), | 226 | mainWindow.loadURL(pageUrl), |
181 | browserView.webContents.loadURL('https://git.marussy.com/sophie/about'), | 227 | browserView.webContents.loadURL('https://gmail.com').then(() => browserView.webContents.openDevTools()), |
182 | ]); | 228 | ]); |
183 | } | 229 | } |
184 | 230 | ||
diff --git a/packages/main/src/userAgent.ts b/packages/main/src/userAgent.ts deleted file mode 100644 index 298e565..0000000 --- a/packages/main/src/userAgent.ts +++ /dev/null | |||
@@ -1,61 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * This file is part of Sophie. | ||
5 | * | ||
6 | * Sophie is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Affero General Public License as | ||
8 | * published by the Free Software Foundation, version 3. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Affero General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Affero General Public License | ||
16 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * SPDX-License-Identifier: AGPL-3.0-only | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file Based on the javascript code snippet available at | ||
23 | * https://github.com/GoogleChrome/developer.chrome.com/blob/c33f7f4f2964e7deb28aee44de745c3725b70063/site/en/docs/privacy-sandbox/user-agent/snippets/index.md | ||
24 | * | ||
25 | * Used under the Apache 2.0 license according to | ||
26 | * https://github.com/GoogleChrome/developer.chrome.com/blob/c33f7f4f2964e7deb28aee44de745c3725b70063/LICENSE | ||
27 | */ | ||
28 | |||
29 | const electronUAParts = / (sophie|Electron)\/[^\s]+/g; | ||
30 | const chromeUAs = /^Mozilla\/5\.0 \(((?<platform>Lin|Win|Mac|X11; C|X11; L)+[^\)]+)\) AppleWebKit\/537.36 \(KHTML, like Gecko\) Chrome\/(?<major>\d+)[\d\.]+(?<mobile>[ Mobile]*) Safari\/537\.36$/; | ||
31 | const unifiedPlatform = { | ||
32 | 'Lin': 'Linux; Android 10; K', | ||
33 | 'Win': 'Windows NT 10.0; Win64; x64', | ||
34 | 'Mac': 'Macintosh; Intel Mac OS X 10_15_7', | ||
35 | 'X11; C': 'X11; CrOS x86_64', | ||
36 | 'X11; L': 'X11; Linux x86_64', | ||
37 | }; | ||
38 | |||
39 | /** | ||
40 | * Reduces the information exposed in the user-agent string. | ||
41 | * | ||
42 | * @param userAgent The original user-agent string. | ||
43 | * @returns The reduces user-agent string. | ||
44 | * @see https://developer.chrome.com/docs/privacy-sandbox/user-agent/ | ||
45 | */ | ||
46 | export function reduceUserAgent(userAgent: string): string { | ||
47 | const userAgentWithoutElectron = userAgent.replaceAll(electronUAParts, ''); | ||
48 | const matched = chromeUAs.exec(userAgentWithoutElectron) as unknown as { | ||
49 | groups: { | ||
50 | platform: 'Lin' | 'Win' | 'Mac' | 'X11; C' | 'X11; L', | ||
51 | major: string, | ||
52 | mobile: string, | ||
53 | } | ||
54 | }; | ||
55 | if (matched) { | ||
56 | return `Mozilla/5.0 (${unifiedPlatform[matched.groups.platform]}) ` + | ||
57 | `AppleWebKit/537.36 (KHTML, like Gecko) ` + | ||
58 | `Chrome/${matched.groups.major}.0.0.0${matched.groups.mobile} Safari/537.36` | ||
59 | } | ||
60 | return userAgentWithoutElectron; | ||
61 | } | ||