Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the ability to specify a proxy in the config.json file #58

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,49 @@ $PROFILE` in which case it becomes `Element-$PROFILE`, or it is using one of
the above created by a pre-1.7 install, in which case it will be `Riot` or
`Riot-$PROFILE`.

See https://github.com/vector-im/element-web/blob/develop/docs/config.md

Electron Config
===============

Electron config is stored in the same folder as the [user specified config](#user-specified-configjson)
in `electron-config.json`.
This is for configuring Electron options as opposed to Matrix/Element options from `config.json`.

This has the following config options:

* `warnBeforeExit`: boolean, optional (default: `true`)
* `true`: Element will display a confirmation box before exiting.
* `minimizeToTray`: boolean, optional (default: `true`)
* `true`: Element will enable the tray.
* `spellCheckerEnabled`: boolean, optional (default: `true`)
* `true`: Element spell checking is enabled.
* `autoHideMenuBar`: boolean, optional (default: `true`)
* `true`: Element will automatically hide the menu bar.
* `locale`: string[], optional
* A list of locales for Element to enable.
* `proxy`: object, optional (default: `null`)
* Proxy configuration for Electron to use. [More info](https://www.electronjs.org/docs/api/session#sessetproxyconfig).
* `mode`: string, optional (default: `null`)
* Proxy mode. If it's unspecified, it will be automatically determined based on other specified options.
* `direct`: All connections go direct without a proxy involved.
* `auto_detect`: The proxy configuration is determined by a PAC script that will be downloaded from `wpad/wpad.dat`.
* `pac_script`: The proxy configuration is determined by a PAC script specified in `pacScript`.
This is the default mode if `pacScript` is specified.
* `fixed_servers`: The proxy configuration is specified in `proxyRules`.
This is the default mode if `proxyRules` is specified and `pacScript` is not specified.
* `system`: The proxy configuration is taken from the operating system.
* `pacScript`: string, optional (default: null)
* The URL for a PAC file.
* This supports local files through the `file:` URI scheme,
e.g. `file:///home/$USER/proxy.pac` or `file://c:/Users/Username/Documents/proxy.pac`,
as well as `data:`, `http:`, and `https:` URI schemes.
* A `data:` URI should follow the format `data:application/x-javascript-config;base64,$BASE64_PAC_CONTENT`;
* `proxyRules`: string, optional (default: `null`)
* Rules indicating which proxies to use.
* `proxyBypassRules`: string, optional (default: `null`)
* Rules indication which URLs should bypass the proxy settings.

Translations
==========================

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"auto-launch": "^5.0.5",
"counterpart": "^0.18.6",
"electron-store": "^6.0.1",
"electron-updater": "^4.3.9",
"electron-window-state": "^5.0.3",
"minimist": "^1.2.3",
"png-to-ico": "^2.1.1",
Expand Down
18 changes: 17 additions & 1 deletion src/electron-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ limitations under the License.

// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
import "./squirrelhooks";
import { app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol, dialog } from "electron";
import { app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, protocol, dialog } from "electron";
import { autoUpdater } from "electron-updater";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to document somewhere that electron-updater is for proxy config, but I don't have any good ideas for where that could be recorded.

import AutoLaunch from "auto-launch";
import path from "path";
import windowStateKeeper from 'electron-window-state';
Expand All @@ -35,6 +36,7 @@ import webContentsHandler from './webcontents-handler';
import * as updater from './updater';
import { getProfileFromDeeplink, protocolInit, recordSSOSession } from './protocol';
import { _t, AppLocalization } from './language-helper';
import { ProxyConfig, Proxy } from './proxy-helper';

const argv = minimist(process.argv, {
alias: { help: "h" },
Expand Down Expand Up @@ -81,6 +83,7 @@ let iconPath;
let trayConfig;
let launcher;
let appLocalization;
let proxy;

if (argv["help"]) {
console.log("Options:");
Expand Down Expand Up @@ -261,6 +264,7 @@ const store = new Store<{
spellCheckerEnabled?: boolean;
autoHideMenuBar?: boolean;
locale?: string | string[];
proxy?: ProxyConfig;
}>({ name: "electron-config" });

let eventIndex = null;
Expand Down Expand Up @@ -938,6 +942,17 @@ app.on('ready', async () => {
webgl: false,
},
});

proxy = new Proxy({
store: store,
sessions: [
mainWindow.webContents.session, // apply proxy to main window
autoUpdater.netSession, // apply proxy to autoUpdater
],
});

await proxy.applyProxy(); // wait for proxy settings to be applied

mainWindow.loadURL('vector://vector/webapp/');

// Handle spellchecker
Expand Down Expand Up @@ -1018,6 +1033,7 @@ function beforeQuit() {
if (mainWindow) {
mainWindow.webContents.send('before-quit');
}
if (proxy) proxy.close();
}

app.on('before-quit', beforeQuit);
Expand Down
109 changes: 109 additions & 0 deletions src/proxy-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2021 New Vector Ltd
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if you intended to assign copyright to us or not, but feel free to use your name instead.


Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { Session } from 'electron';
import fs from 'fs';
import type Store from 'electron-store';

export interface ProxyConfig {
mode?: "direct" | "auto_detect" | "pac_script" | "fixed_servers" | "system";
pacScript?: string;
proxyRules?: string;
proxyBypassRules?: string;
}

type TypedStore = Store<{ proxy?: ProxyConfig }>;

export class Proxy {
public ready: Promise<any>;
private static readonly STORE_KEY = "proxy";

private readonly store: TypedStore;
private readonly sessions: Array<Session>;
private proxy: ProxyConfig;
private pacWatcher: fs.FSWatcher;

constructor( { store, sessions = [] }: { store: TypedStore, sessions: Session[] }) {
this.store = store;
this.sessions = sessions;

if (this.store.has(Proxy.STORE_KEY)) {
console.log("Setting up proxy.");
this.setProxy(this.store.get(Proxy.STORE_KEY));
}
}

public setProxy(proxy: ProxyConfig): void {
this.proxy = proxy;
this.store.set(Proxy.STORE_KEY, this.proxy);
if (this.proxy.pacScript) {
// Add custom handling for the file: URI handler as chromium does not support it
// https://bugs.chromium.org/p/chromium/issues/detail?id=839566#c40
const pacURL = new URL(this.proxy.pacScript);
if (pacURL.protocol === 'file:') {
this.setProxyFromPACFile(pacURL.pathname);
this.watchProxyPACFile(pacURL.pathname);
}
}
}

public async applyProxy(): Promise<any> {
// Apply the proxy config to the sessions
if (!this.proxy) return;

return Promise.allSettled(
this.sessions.map((session) =>
session.closeAllConnections() // Ensure all in-progress connections are closed
.then(() => session.setProxy(this.proxy)) // Set the proxy settings
.then(() => session.forceReloadProxyConfig()), // Ensure the updated config has been reloaded
));
}

private setProxyFromPACFile(pacFile: fs.PathLike): void {
// Convert PAC file path into a base64 data: URI
if (!this.proxy) return;

const pacBuf = fs.readFileSync(pacFile);
this.proxy.pacScript = `data:application/x-javascript-config;base64,${pacBuf.toString('base64')}`;
}

private watchProxyPACFile(pacFile: fs.PathLike): void {
// Watch the PAC file for changes and reapply config if a change is detected
if (this.pacWatcher) return;

this.pacWatcher = fs.watch(pacFile, async (event) => {
console.log("Started watching PAC file.");
});

this.pacWatcher.on('change', (eventType: string) => {
console.log("PAC file changed, updating proxy settings.");
this.setProxyFromPACFile(pacFile);
this.applyProxy();
});

this.pacWatcher.on('close', () => {
console.log("Stopped watching PAC file.");
});
}

public close(): void {
// Cleanup the fs watcher
if (!this.pacWatcher) return;

this.pacWatcher.close();
this.pacWatcher = null;
}
}
3 changes: 2 additions & 1 deletion src/updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { app, autoUpdater, ipcMain } from "electron";
import { app, ipcMain } from "electron";
import { autoUpdater } from "electron-updater";

const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
Expand Down
29 changes: 29 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,11 @@
"@types/node" "*"
xmlbuilder ">=11.0.1"

"@types/semver@^7.3.5":
version "7.3.8"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.8.tgz#508a27995498d7586dcecd77c25e289bfaf90c59"
integrity sha512-D/2EJvAlCEtYFEYmmlGwbGXuK886HzyCc3nZX/tkFTQdEU8jZDAgiv08P162yB17y4ZXZoq7yFAnW4GDBb9Now==

"@types/verror@^1.10.3":
version "1.10.5"
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.5.tgz#2a1413aded46e67a1fe2386800e291123ed75eb1"
Expand Down Expand Up @@ -1947,6 +1952,20 @@ electron-store@^6.0.1:
conf "^7.1.2"
type-fest "^0.16.0"

electron-updater@^4.3.9:
version "4.3.9"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.3.9.tgz#247c660bafad7c07935e1b81acd3e9a5fd733154"
integrity sha512-LCNfedSwZfS4Hza+pDyPR05LqHtGorCStaBgVpRnfKxOlZcvpYEX0AbMeH5XUtbtGRoH2V8osbbf2qKPNb7AsA==
dependencies:
"@types/semver" "^7.3.5"
builder-util-runtime "8.7.5"
fs-extra "^10.0.0"
js-yaml "^4.1.0"
lazy-val "^1.0.4"
lodash.escaperegexp "^4.1.2"
lodash.isequal "^4.5.0"
semver "^7.3.5"

electron-window-state@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-5.0.3.tgz#4f36d09e3f953d87aff103bf010f460056050aa8"
Expand Down Expand Up @@ -3133,11 +3152,21 @@ lodash.difference@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=

lodash.escaperegexp@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=

lodash.flatten@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=

lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=

lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
Expand Down