Skip to content

Commit

Permalink
Merge branch 'custom-lists-gui-des-198'
Browse files Browse the repository at this point in the history
  • Loading branch information
raksooo committed Oct 9, 2023
2 parents c1f9e78 + 9551fd1 commit 817866f
Show file tree
Hide file tree
Showing 33 changed files with 1,648 additions and 410 deletions.
4 changes: 4 additions & 0 deletions gui/assets/images/icon-edit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions gui/locales/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ msgstr ""
msgid "Reconnect"
msgstr ""

msgid "Save"
msgstr ""

msgid "Search for..."
msgstr ""

Expand Down Expand Up @@ -1005,6 +1008,33 @@ msgctxt "select-location-view"
msgid "%(location)s (%(info)s)"
msgstr ""

#. This is a label shown above a list of options.
#. Available placeholder:
#. %(locationType) - Could be either "Country", "City" and "Relay"
msgctxt "select-location-view"
msgid "Add <b>%(locationType)s</b> to list"
msgstr ""

msgctxt "select-location-view"
msgid "All locations"
msgstr ""

msgctxt "select-location-view"
msgid "City"
msgstr ""

msgctxt "select-location-view"
msgid "Country"
msgstr ""

msgctxt "select-location-view"
msgid "Custom lists"
msgstr ""

msgctxt "select-location-view"
msgid "Edit list name"
msgstr ""

msgctxt "select-location-view"
msgid "Entry"
msgstr ""
Expand All @@ -1017,10 +1047,22 @@ msgctxt "select-location-view"
msgid "Filtered:"
msgstr ""

msgctxt "select-location-view"
msgid "List names must be unique."
msgstr ""

msgctxt "select-location-view"
msgid "Name is already taken."
msgstr ""

msgctxt "select-location-view"
msgid "Providers: %(numberOfProviders)d"
msgstr ""

msgctxt "select-location-view"
msgid "Relay"
msgstr ""

msgctxt "select-location-view"
msgid "The app selects a random bridge server, but servers have a higher probability the closer they are to you."
msgstr ""
Expand Down
1 change: 1 addition & 0 deletions gui/src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"white40": "rgba(255, 255, 255, 0.4)",
"white20": "rgba(255, 255, 255, 0.2)",
"white10": "rgba(255, 255, 255, 0.1)",
"blue10": "rgba(41, 77, 115, 0.1)",
"blue20": "rgba(41, 77, 115, 0.2)",
"blue40": "rgba(41, 77, 115, 0.4)",
"blue60": "rgba(41, 77, 115, 0.6)",
Expand Down
169 changes: 127 additions & 42 deletions gui/src/main/daemon-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
BridgeState,
ConnectionConfig,
Constraint,
CustomListError,
CustomLists,
DaemonEvent,
DeviceEvent,
DeviceState,
Expand All @@ -27,6 +29,7 @@ import {
FirewallPolicyErrorType,
IAppVersionInfo,
IBridgeConstraints,
ICustomList,
IDevice,
IDeviceRemoval,
IDnsOptions,
Expand All @@ -52,6 +55,7 @@ import {
ProxyType,
RelayEndpointType,
RelayLocation,
RelayLocationGeographical,
RelayProtocol,
RelaySettings,
RelaySettingsUpdate,
Expand Down Expand Up @@ -611,6 +615,39 @@ export class DaemonRpc {
await this.call<grpcTypes.DeviceRemoval, Empty>(this.client.removeDevice, grpcDeviceRemoval);
}

public async createCustomList(name: string): Promise<void | CustomListError> {
try {
await this.callString<Empty>(this.client.createCustomList, name);
} catch (e) {
const error = e as grpc.ServiceError;
if (error.code === 6) {
return { type: 'name already exists' };
} else {
throw error;
}
}
}

public async deleteCustomList(id: string): Promise<void> {
await this.callString<Empty>(this.client.deleteCustomList, id);
}

public async updateCustomList(customList: ICustomList): Promise<void | CustomListError> {
try {
await this.call<grpcTypes.CustomList, Empty>(
this.client.updateCustomList,
convertToCustomList(customList),
);
} catch (e) {
const error = e as grpc.ServiceError;
if (error.code === 6) {
return { type: 'name already exists' };
} else {
throw error;
}
}
}

private subscriptionId(): number {
const current = this.nextSubscriptionId;
this.nextSubscriptionId += 1;
Expand Down Expand Up @@ -1062,10 +1099,11 @@ function convertFromSettings(settings: grpcTypes.Settings): ISettings | undefine
const settingsObject = settings.toObject();
const bridgeState = convertFromBridgeState(settingsObject.bridgeState!.state!);
const relaySettings = convertFromRelaySettings(settings.getRelaySettings())!;
const bridgeSettings = convertFromBridgeSettings(settingsObject.bridgeSettings!);
const bridgeSettings = convertFromBridgeSettings(settings.getBridgeSettings()!);
const tunnelOptions = convertFromTunnelOptions(settingsObject.tunnelOptions!);
const splitTunnel = settingsObject.splitTunnel ?? { enableExclusions: false, appsList: [] };
const obfuscationSettings = convertFromObfuscationSettings(settingsObject.obfuscationSettings);
const customLists = convertFromCustomListSettings(settings.getCustomLists());
return {
...settings.toObject(),
bridgeState,
Expand All @@ -1074,6 +1112,7 @@ function convertFromSettings(settings: grpcTypes.Settings): ISettings | undefine
tunnelOptions,
splitTunnel,
obfuscationSettings,
customLists,
};
}

Expand Down Expand Up @@ -1110,10 +1149,8 @@ function convertFromRelaySettings(
}
case grpcTypes.RelaySettings.EndpointCase.NORMAL: {
const normal = relaySettings.getNormal()!;
const grpcLocation = normal.getLocation();
const location = grpcLocation
? { only: convertFromLocation(grpcLocation.toObject()) }
: 'any';
const locationConstraint = convertFromLocationConstraint(normal.getLocation());
const location = locationConstraint ? { only: locationConstraint } : 'any';
const tunnelProtocol = convertFromTunnelTypeConstraint(normal.getTunnelType()!);
const providers = normal.getProvidersList();
const ownership = convertFromOwnership(normal.getOwnership());
Expand All @@ -1139,13 +1176,14 @@ function convertFromRelaySettings(
}
}

function convertFromBridgeSettings(
bridgeSettings: grpcTypes.BridgeSettings.AsObject,
): BridgeSettings {
const normalSettings = bridgeSettings.normal;
function convertFromBridgeSettings(bridgeSettings: grpcTypes.BridgeSettings): BridgeSettings {
const bridgeSettingsObject = bridgeSettings.toObject();
const normalSettings = bridgeSettingsObject.normal;
if (normalSettings) {
const grpcLocation = normalSettings.location;
const location = grpcLocation ? { only: convertFromLocation(grpcLocation) } : 'any';
const locationConstraint = convertFromLocationConstraint(
bridgeSettings.getNormal()?.getLocation(),
);
const location = locationConstraint ? { only: locationConstraint } : 'any';
const providers = normalSettings.providersList;
const ownership = convertFromOwnership(normalSettings.ownership);
return {
Expand All @@ -1161,23 +1199,23 @@ function convertFromBridgeSettings(
return { custom: settings };
};

const localSettings = bridgeSettings.local;
const localSettings = bridgeSettingsObject.local;
if (localSettings) {
return customSettings({
port: localSettings.port,
peer: localSettings.peer,
});
}

const remoteSettings = bridgeSettings.remote;
const remoteSettings = bridgeSettingsObject.remote;
if (remoteSettings) {
return customSettings({
address: remoteSettings.address,
auth: remoteSettings.auth && { ...remoteSettings.auth },
});
}

const shadowsocksSettings = bridgeSettings.shadowsocks!;
const shadowsocksSettings = bridgeSettingsObject.shadowsocks!;
return customSettings({
peer: shadowsocksSettings.peer!,
password: shadowsocksSettings.password!,
Expand Down Expand Up @@ -1229,23 +1267,32 @@ function convertFromConnectionConfig(
}
}

function convertFromLocation(location: grpcTypes.LocationConstraint.AsObject): RelayLocation {
// FIXME: This is a hack that assumes that the LocationConstraint is not a custom list.
// If it is we just set the country to "any" even if that isn't correct.
if (location.location == undefined) {
return { country: 'any' };
}
const loc = location.location;

if (loc.hostname) {
return { hostname: [loc.country, loc.city, loc.hostname] };
function convertFromLocationConstraint(
location?: grpcTypes.LocationConstraint,
): RelayLocation | undefined {
if (location === undefined) {
return undefined;
} else if (location.getTypeCase() === grpcTypes.LocationConstraint.TypeCase.CUSTOM_LIST) {
return { customList: location.getCustomList() };
} else {
const innerLocation = location.getLocation()?.toObject();
return innerLocation && convertFromRelayLocation(innerLocation);
}
}

if (loc.city) {
return { city: [loc.country, loc.city] };
function convertFromRelayLocation(location: grpcTypes.RelayLocation.AsObject): RelayLocation {
if (location.hostname) {
return location;
} else if (location.city) {
return {
country: location.country,
city: location.city,
};
} else {
return {
country: location.country,
};
}

return { country: loc.country };
}

function convertFromTunnelOptions(tunnelOptions: grpcTypes.TunnelOptions.AsObject): ITunnelOptions {
Expand Down Expand Up @@ -1423,7 +1470,8 @@ function convertFromWireguardConstraints(

const entryLocation = constraints.getEntryLocation();
if (entryLocation) {
result.entryLocation = { only: convertFromLocation(entryLocation.toObject()) };
const location = convertFromLocationConstraint(entryLocation);
result.entryLocation = location ? { only: location } : 'any';
}

return result;
Expand Down Expand Up @@ -1467,24 +1515,32 @@ function convertToLocation(
constraint: RelayLocation | undefined,
): grpcTypes.LocationConstraint | undefined {
const locationConstraint = new grpcTypes.LocationConstraint();
const location = new grpcTypes.RelayLocation();
if (constraint && 'hostname' in constraint) {
const [countryCode, cityCode, hostname] = constraint.hostname;
location.setCountry(countryCode);
location.setCity(cityCode);
location.setHostname(hostname);
} else if (constraint && 'city' in constraint) {
location.setCountry(constraint.city[0]);
location.setCity(constraint.city[1]);
} else if (constraint && 'country' in constraint) {
location.setCountry(constraint.country);
if (constraint && 'customList' in constraint && constraint.customList) {
locationConstraint.setCustomList(constraint.customList);
} else {
return undefined;
const location = constraint && convertToRelayLocation(constraint);
locationConstraint.setLocation(location);
}
locationConstraint.setLocation(location);

return locationConstraint;
}

function convertToRelayLocation(location: RelayLocation): grpcTypes.RelayLocation {
const relayLocation = new grpcTypes.RelayLocation();
if ('hostname' in location) {
relayLocation.setCountry(location.country);
relayLocation.setCity(location.city);
relayLocation.setHostname(location.hostname);
} else if ('city' in location) {
relayLocation.setCountry(location.country);
relayLocation.setCity(location.city);
} else if ('country' in location) {
relayLocation.setCountry(location.country);
}

return relayLocation;
}

function convertToTunnelTypeConstraint(
constraint: Constraint<TunnelType>,
): grpcTypes.TunnelTypeConstraint | undefined {
Expand Down Expand Up @@ -1618,6 +1674,35 @@ function convertFromDevice(device: grpcTypes.Device): IDevice {
};
}

function convertFromCustomListSettings(
customListSettings?: grpcTypes.CustomListSettings,
): CustomLists {
return customListSettings ? convertFromCustomLists(customListSettings.getCustomListsList()) : [];
}

function convertFromCustomLists(customLists: Array<grpcTypes.CustomList>): CustomLists {
return customLists.map((list) => ({
id: list.getId(),
name: list.getName(),
locations: list
.getLocationsList()
.map((location) =>
convertFromRelayLocation(location.toObject()),
) as Array<RelayLocationGeographical>,
}));
}

function convertToCustomList(customList: ICustomList): grpcTypes.CustomList {
const grpcCustomList = new grpcTypes.CustomList();
grpcCustomList.setId(customList.id);
grpcCustomList.setName(customList.name);

const locations = customList.locations.map(convertToRelayLocation);
grpcCustomList.setLocationsList(locations);

return grpcCustomList;
}

function ensureExists<T>(value: T | undefined, errorMessage: string): T {
if (value) {
return value;
Expand Down
1 change: 1 addition & 0 deletions gui/src/main/default-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ export function getDefaultSettings(): ISettings {
port: 'any',
},
},
customLists: [],
};
}
10 changes: 10 additions & 0 deletions gui/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,16 @@ class ApplicationMain
this.navigationHistory = history;
});

IpcMainEventChannel.customLists.handleCreateCustomList((name) => {
return this.daemonRpc.createCustomList(name);
});
IpcMainEventChannel.customLists.handleDeleteCustomList((id) => {
return this.daemonRpc.deleteCustomList(id);
});
IpcMainEventChannel.customLists.handleUpdateCustomList((customList) => {
return this.daemonRpc.updateCustomList(customList);
});

problemReport.registerIpcListeners();
this.userInterface!.registerIpcListeners();
this.settings.registerIpcListeners();
Expand Down
3 changes: 3 additions & 0 deletions gui/src/main/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ export default class Settings implements Readonly<ISettings> {
public get obfuscationSettings() {
return this.settingsValue.obfuscationSettings;
}
public get customLists() {
return this.settingsValue.customLists;
}

public get gui() {
return this.guiSettings;
Expand Down
Loading

0 comments on commit 817866f

Please sign in to comment.