diff options
author | kali kaneko (leap communications) <kali@leap.se> | 2020-09-02 23:47:05 +0200 |
---|---|---|
committer | kali kaneko (leap communications) <kali@leap.se> | 2021-05-04 14:58:39 +0200 |
commit | 4bd6bbd788454367cc89d78543312f333051b840 (patch) | |
tree | 6a3d01bf804ea4bd841c013dbe96ffe3df29323a | |
parent | 335bb742b957370bbf40ae77a661559805ab307f (diff) |
[feat] expose gateway selector in gui
-rw-r--r-- | gui/gui.qrc | 3 | ||||
-rw-r--r-- | gui/handlers.cpp | 5 | ||||
-rw-r--r-- | gui/handlers.h | 1 | ||||
-rw-r--r-- | gui/js/maps.js | 83 | ||||
-rw-r--r-- | gui/qml/VpnState.qml | 64 | ||||
-rw-r--r-- | gui/qml/main.qml | 238 | ||||
-rw-r--r-- | pkg/backend/api.go | 9 | ||||
-rw-r--r-- | pkg/backend/status.go | 36 | ||||
-rw-r--r-- | pkg/bitmask/bitmask.go | 1 | ||||
-rw-r--r-- | pkg/vpn/bonafide/bonafide.go | 6 | ||||
-rw-r--r-- | pkg/vpn/bonafide/eip_service.go | 24 | ||||
-rw-r--r-- | pkg/vpn/bonafide/gateways.go | 42 | ||||
-rw-r--r-- | pkg/vpn/openvpn.go | 9 |
13 files changed, 373 insertions, 148 deletions
diff --git a/gui/gui.qrc b/gui/gui.qrc index 5e0d4ae..fdea109 100644 --- a/gui/gui.qrc +++ b/gui/gui.qrc @@ -2,6 +2,7 @@ <qresource prefix="/"> <file>qml/main.qml</file> + <file>qml/VpnState.qml</file> <file>qml/AboutDialog.qml</file> <file>qml/DonateDialog.qml</file> <file>qml/LoginDialog.qml</file> @@ -24,5 +25,7 @@ <file alias="providers.json">providers/providers.json</file> <file>assets/svg/world.svg</file> + <file>js/maps.js</file> + </qresource> </RCC> diff --git a/gui/handlers.cpp b/gui/handlers.cpp index 8f0e0d0..370c67c 100644 --- a/gui/handlers.cpp +++ b/gui/handlers.cpp @@ -42,6 +42,11 @@ void Backend::donateSeen() DonateSeen(); } +void Backend::useGateway(QString label) +{ + UseGateway(toGoStr(label)); +} + void Backend::login(QString username, QString password) { Login(toGoStr(username), toGoStr(password)); diff --git a/gui/handlers.h b/gui/handlers.h index 8283645..a783207 100644 --- a/gui/handlers.h +++ b/gui/handlers.h @@ -36,6 +36,7 @@ public slots: void switchOff(); void donateAccepted(); void donateSeen(); + void useGateway(QString username); void login(QString username, QString password); void resetError(QString errlabel); void resetNotification(QString label); diff --git a/gui/js/maps.js b/gui/js/maps.js new file mode 100644 index 0000000..c6f44d2 --- /dev/null +++ b/gui/js/maps.js @@ -0,0 +1,83 @@ +// Robinson projection calculation + +// Written by Niklas Bichinger (bichinger.de). This code is Public Domain - use as you like. + +// source of robinson numbers: https://simplemaps.com/static/img/flash/robinson_projection_table.jpg +var robinsonAA = [ + 0.84870000, + 0.84751182, + 0.84479598, + 0.84021300, + 0.83359314, + 0.82578510, + 0.81475200, + 0.80006949, + 0.78216192, + 0.76060494, + 0.73658673, + 0.70866450, + 0.67777182, + 0.64475739, + 0.60987582, + 0.57134484, + 0.52729731, + 0.48562614, + 0.45167814 +]; +var robinsonBB = [ + 0.00000000, + 0.08384260, + 0.16768520, + 0.25152780, + 0.33537040, + 0.41921300, + 0.50305560, + 0.58689820, + 0.67047034, + 0.75336633, + 0.83518048, + 0.91537187, + 0.99339958, + 1.06872269, + 1.14066505, + 1.20841528, + 1.27035062, + 1.31998003, + 1.35230000 +]; + +function project(latitude, longitude, mapWidth, heightFactor, mapOffsetX, mapOffsetY) { + if (typeof heightFactor === 'undefined') { heightFactor = 1; } + if (typeof mapOffsetX === 'undefined') { mapOffsetX = 0; } + if (typeof mapOffsetY === 'undefined') { mapOffsetY = 0; } + + // Robinson's latitude interpolation points are in 5-degree-steps + var latitudeAbs = Math.abs(latitude); + var latitudeStepFloor = Math.floor(latitudeAbs / 5); + var latitudeStepCeil = Math.ceil(latitudeAbs / 5); + // calc interpolation factor (>=0 to <1) between two steps + var latitudeInterpolation = (latitudeAbs - latitudeStepFloor * 5) / 5; + + // interpolate robinson table values + var AA = robinsonAA[latitudeStepFloor] + (robinsonAA[latitudeStepCeil] - robinsonAA[latitudeStepFloor]) * latitudeInterpolation; + var BB = robinsonBB[latitudeStepFloor] + (robinsonBB[latitudeStepCeil] - robinsonBB[latitudeStepFloor]) * latitudeInterpolation; + + var robinsonWidth = 2 * Math.PI * robinsonAA[0]; + var widthFactor = mapWidth / robinsonWidth; + var latitudeSign = Math.sign(latitude) || 1; + var x = (widthFactor * AA * longitude * Math.PI) / 180 + mapOffsetX; + var y = widthFactor * BB * latitudeSign * heightFactor + mapOffsetY; + + return {x: x, y: y}; +} + +function projectAbsolute(latitude, longitude, mapWidth, heightFactor, mapOffsetX, mapOffsetY) { + if (typeof heightFactor === 'undefined') { heightFactor = 1; } + + var relative = project(latitude, longitude, mapWidth, heightFactor, mapOffsetX, mapOffsetY); + var widthHeightRatio = Math.PI * robinsonAA[0] / robinsonBB[18]; + var x = mapWidth / 2 + relative.x; + var y = mapWidth / widthHeightRatio * heightFactor / 2 - relative.y; + + return {x: x, y: y}; +} diff --git a/gui/qml/VpnState.qml b/gui/qml/VpnState.qml new file mode 100644 index 0000000..ea2a3b1 --- /dev/null +++ b/gui/qml/VpnState.qml @@ -0,0 +1,64 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.4 + +StateGroup { + + state: ctx ? ctx.status : "" + + states: [ + State { name: "initializing" }, + State { + name: "off" + PropertyChanges { target: systray; tooltip: toHuman("off"); icon.source: icons["off"] } + PropertyChanges { target: statusItem; text: toHuman("off") } + PropertyChanges { target: mainStatus; text: toHuman("off") } + PropertyChanges { target: mainCurrentGateway; text: "" } + PropertyChanges { target: mainOnBtn; visible: true } + PropertyChanges { target: mainOffBtn; visible: false } + PropertyChanges { target: gwMarker; color: "red"} + }, + State { + name: "on" + StateChangeScript { + script: displayGatewayMarker() + } + PropertyChanges { target: systray; tooltip: toHuman("on"); icon.source: icons["on"] } + PropertyChanges { target: statusItem; text: toHumanWithLocation("on") } + PropertyChanges { target: mainStatus; text: toHuman("on") } + PropertyChanges { target: mainCurrentGateway; text: qsTr("Connected to ") + ctx.currentGateway } + PropertyChanges { target: mainOnBtn; visible: false } + PropertyChanges { target: mainOffBtn; visible: true } + PropertyChanges { target: gwMarker; color: "green"} + }, + State { + name: "starting" + PropertyChanges { target: systray; tooltip: toHuman("connecting"); icon.source: icons["wait"] } + PropertyChanges { target: statusItem; text: toHumanWithLocation("connecting") } + PropertyChanges { target: mainStatus; text: qsTr("Connecting...") } + PropertyChanges { target: mainCurrentGateway; text: "" } + PropertyChanges { target: mainOnBtn; visible: false } + PropertyChanges { target: mainOffBtn; visible: true } + PropertyChanges { target: gwMarker; color: "orange"} + }, + State { + name: "stopping" + PropertyChanges { target: systray; tooltip: toHuman("stopping"); icon.source: icons["wait"] } + PropertyChanges { target: statusItem; text: toHuman("stopping") } + PropertyChanges { target: mainStatus; text: toHuman("stopping") } + PropertyChanges { target: mainCurrentGateway; text: "" } + PropertyChanges { target: mainOnBtn; visible: true } + PropertyChanges { target: mainOffBtn; visible: false } + PropertyChanges { target: gwMarker; color: "orange"} + }, + State { + name: "failed" + PropertyChanges { target: systray; tooltip: toHuman("failed"); icon.source: icons["wait"] } + PropertyChanges { target: statusItem; text: toHuman("failed") } + PropertyChanges { target: mainStatus; text: toHuman("failed") } + PropertyChanges { target: mainCurrentGateway; text: "" } + PropertyChanges { target: mainOnBtn; visible: true } + PropertyChanges { target: mainOffBtn; visible: false } + PropertyChanges { target: gwMarker; color: "red"} + } + ] +} diff --git a/gui/qml/main.qml b/gui/qml/main.qml index 98a4445..bbc3f69 100644 --- a/gui/qml/main.qml +++ b/gui/qml/main.qml @@ -6,12 +6,19 @@ import QtQuick.Extras 1.2 import Qt.labs.platform 1.1 as LabsPlatform +import "qrc:/js/maps.js" as Maps + ApplicationWindow { id: app visible: true - width: 700 - height: 700 + width: 300 + height: 600 + maximumWidth: 300 + minimumWidth: 300 + maximumHeight: 600 + minimumHeight: 600 + // TODO get a nice background color flags: Qt.WindowsStaysOnTopHint | Qt.Popup @@ -19,52 +26,121 @@ ApplicationWindow { property var loginDone property var allowEmptyPass - ColumnLayout{ - anchors.centerIn: parent - width: parent.width - Layout.preferredHeight: parent.height + onWidthChanged: displayGatewayMarker() + onHeightChanged: displayGatewayMarker() + + GridLayout { + visible: true + columns: 3 - Text{ - id: mainStatus - text: "Status: off" - font.pixelSize: 22 - Layout.preferredWidth: parent.width - horizontalAlignment: Text.AlignHCenter - } + Item { + Layout.column: 2 + Layout.topMargin: app.height * 0.15 + Layout.leftMargin: app.width * 0.10 - Label { - text: "gateway selection:" - font.pixelSize: 20 - } + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + + Text{ + id: mainStatus + text: "off" + font.pixelSize: 26 + Layout.alignment: Text.AlignHCenter + } + + Text{ + id: mainCurrentGateway + text: "" + font.pixelSize: 20 + Layout.alignment: Text.AlignHCenter + } - ComboBox { - id: comboGw - editable: false - model: [ qsTr("Automatic"), qsTr("Paris"), qsTr("Amsterdam") ] - onAccepted: { - if (combo.find(currentText) === -1) { - currentIndex = combo.find(editText) - } - } + Button { + id: mainOnBtn + x: 80 + y: 200 + text: qsTr("on") + visible: true + onClicked: backend.switchOn() + } + + Button { + id: mainOffBtn + x: 180 + y: 200 + text: qsTr("off") + visible: false + onClicked: backend.switchOff() + } + + ComboBox { + id: gwSelector + editable: false + model: [qsTr("Automatic")] + onActivated: { + console.debug("Selected gateway:", currentText); + backend.useGateway(currentText.toString()); + } + } + } } - ColumnLayout{ - width: parent.width + Item { + Layout.topMargin: app.height * 0.40 + Layout.row: 3 + Layout.column: 1 + Layout.columnSpan: 3 Image { id: worldMap + width: app.width source: "qrc:/assets/svg/world.svg" - fillMode: Image.PreserveAspectCrop + fillMode: Image.PreserveAspectFit + smooth: true } - } + + Rectangle { + id: gwMarker + x: worldMap.width * 0.5 + y: worldMap.height * 0.5 + width: 10 + height: 10 + radius: 10 + color: "red" + z: worldMap.z + 1 + } + + } + } + + + function displayGatewayMarker() { + let coords = { + 'paris': {'x': 48, 'y': 2}, + 'miami': {'x': 25.7 , 'y': -80.2 }, + 'amsterdam': {'x': 52.4, 'y': 4.9 }, + 'montreal': {'x': 45.3, 'y': -73.4 }, + 'seattle': {'x': 47.4, 'y': -122.2 }, + } + let city = ctx.currentGateway.split('-')[0] + let coord = coords[city] + + // TODO the Robinson projection does not seem to fit super-nicely with + // our map, and this offset doesn't work with bigg-ish sizes. But good + // enough for a proof of concept - if we avoid resizing the window. + let xOffset = -1 * 0.10 * worldMap.width + let p = Maps.projectAbsolute(coord.x, coord.y, worldMap.width, 1, xOffset) + gwMarker.x = p.x + gwMarker.y = p.y } Connections { target: jsonModel onDataChanged: { - ctx = JSON.parse(jsonModel.getJson()) + ctx = JSON.parse(jsonModel.getJson()); + gwSelector.model = Object.keys(ctx.gateways) if (ctx.donateDialog == 'true') { console.debug(jsonModel.getJson()) @@ -170,6 +246,28 @@ ApplicationWindow { } } + function toHumanWithLocation(st) { + switch(st) { + case "off": + //: %1 -> application name + return qsTr("%1 off").arg(ctx.appName); + case "on": + //: %1 -> application name + //: %2 -> current gateway + return qsTr("%1 on - %2").arg(ctx.appName).arg(ctx.currentGateway); + case "connecting": + //: %1 -> application name + //: %2 -> current gateway + return qsTr("Connecting to %1 - %2").arg(ctx.appName).arg(ctx.currentGateway); + case "stopping": + //: %1 -> application name + return qsTr("Stopping %1").arg(ctx.appName); + case "failed": + //: %1 -> application name + return qsTr("%1 blocking internet").arg(ctx.appName); // TODO failed is not handed yet + } + } + property var icons: { "off": "qrc:/assets/icon/png/black/vpn_off.png", "on": "qrc:/assets/icon/png/black/vpn_on.png", @@ -177,8 +275,11 @@ ApplicationWindow { "blocked": "qrc:/assets/icon/png/black/vpn_blocked.png" } + VpnState { + id: vpn + } - + SystemTrayIcon { LabsPlatform.SystemTrayIcon { id: systray @@ -203,76 +304,6 @@ ApplicationWindow { } } - StateGroup { - id: vpn - state: ctx ? ctx.status : "" - - states: [ - State { - name: "initializing" - }, - State { - name: "off" - PropertyChanges { - target: systray - tooltip: toHuman("off") - icon.source: icons["off"] - } - PropertyChanges { - target: statusItem - text: toHuman("off") - } - }, - State { - name: "on" - PropertyChanges { - target: systray - tooltip: toHuman("on") - icon.source: icons["on"] - } - PropertyChanges { - target: statusItem - text: toHuman("on") - } - }, - State { - name: "starting" - PropertyChanges { - target: systray - tooltip: toHuman("connecting") - icon.source: icons["wait"] - } - PropertyChanges { - target: statusItem - text: toHuman("connecting") - } - }, - State { - name: "stopping" - PropertyChanges { - target: systray - tooltip: toHuman("stopping") - icon.source: icons["wait"] - } - PropertyChanges { - target: statusItem - text: toHuman("stopping") - } - }, - State { - name: "failed" - PropertyChanges { - target: systray - tooltip: toHuman("failed") - icon.source: icons["blocked"] - } - PropertyChanges { - target: statusItem - text: toHuman("failed") - } - } - ] - } LabsPlatform.MenuItem { id: statusItem @@ -385,8 +416,6 @@ ApplicationWindow { console.log("System doesn't support systray notifications") } } - - } DonateDialog { @@ -432,5 +461,4 @@ ApplicationWindow { id: initFailure visible: false } - } diff --git a/pkg/backend/api.go b/pkg/backend/api.go index 8d6d049..761c03d 100644 --- a/pkg/backend/api.go +++ b/pkg/backend/api.go @@ -7,6 +7,7 @@ import ( "encoding/json" "log" "strconv" + "time" "unsafe" "0xacab.org/leap/bitmask-vpn/pkg/bitmask" @@ -54,10 +55,14 @@ func SwitchOff() { go stopVPN() } -// TODO implement Reconnect? +// TODO implement Reconnect - do not tear whole fw down in between func UseGateway(label string) { - ctx.bm.UseGateway(label) + ctx.bm.UseGateway(string(label)) + time.Sleep(200 * time.Millisecond) + SwitchOff() + time.Sleep(500 * time.Millisecond) + SwitchOn() } func UseTransport(label string) { diff --git a/pkg/backend/status.go b/pkg/backend/status.go index 16db227..20128ca 100644 --- a/pkg/backend/status.go +++ b/pkg/backend/status.go @@ -8,6 +8,7 @@ import ( "0xacab.org/leap/bitmask-vpn/pkg/bitmask" "0xacab.org/leap/bitmask-vpn/pkg/config" + "0xacab.org/leap/bitmask-vpn/pkg/vpn/bonafide" ) const ( @@ -32,18 +33,20 @@ var updateMutex sync.Mutex // them. type connectionCtx struct { - AppName string `json:"appName"` - Provider string `json:"provider"` - TosURL string `json:"tosURL"` - HelpURL string `json:"helpURL"` - AskForDonations bool `json:"askForDonations"` - DonateDialog bool `json:"donateDialog"` - DonateURL string `json:"donateURL"` - LoginDialog bool `json:"loginDialog"` - LoginOk bool `json:"loginOk"` - Version string `json:"version"` - Errors string `json:"errors"` - Status status `json:"status"` + AppName string `json:"appName"` + Provider string `json:"provider"` + TosURL string `json:"tosURL"` + HelpURL string `json:"helpURL"` + AskForDonations bool `json:"askForDonations"` + DonateDialog bool `json:"donateDialog"` + DonateURL string `json:"donateURL"` + LoginDialog bool `json:"loginDialog"` + LoginOk bool `json:"loginOk"` + Version string `json:"version"` + Errors string `json:"errors"` + Status status `json:"status"` + Gateways map[string]bonafide.Gateway `json:"gateways"` + CurrentGateway string `json:"currentGateway"` bm bitmask.Bitmask autostart bitmask.Autostart cfg *config.Config @@ -51,6 +54,15 @@ type connectionCtx struct { func (c connectionCtx) toJson() ([]byte, error) { statusMutex.Lock() + if c.bm != nil { + c.Gateways = map[string]bonafide.Gateway{} + gateways, _ := c.bm.ListGateways("openvpn") + for _, label := range gateways { + gw, _ := c.bm.GetGatewayDetails(label) + c.Gateways[label] = gw.(bonafide.Gateway) + } + c.CurrentGateway = c.bm.GetCurrentGateway() + } defer statusMutex.Unlock() b, err := json.Marshal(c) if err != nil { diff --git a/pkg/bitmask/bitmask.go b/pkg/bitmask/bitmask.go index 7ffe01a..6d5fa33 100644 --- a/pkg/bitmask/bitmask.go +++ b/pkg/bitmask/bitmask.go @@ -29,6 +29,7 @@ type Bitmask interface { ListGateways(provider string) ([]string, error) UseGateway(name string) error GetCurrentGateway() string + GetGatewayDetails(label string) (interface{}, error) UseTransport(transport string) error NeedsCredentials() bool DoLogin(username, password string) (bool, error) diff --git a/pkg/vpn/bonafide/bonafide.go b/pkg/vpn/bonafide/bonafide.go index 8b60641..561c2bb 100644 --- a/pkg/vpn/bonafide/bonafide.go +++ b/pkg/vpn/bonafide/bonafide.go @@ -224,8 +224,12 @@ func (b *Bonafide) GetAllGateways(transport string) ([]Gateway, error) { return gws, err } +func (b *Bonafide) GetGatewayDetails(label string) (Gateway, error) { + return b.gateways.getGatewayByLabel(label) +} + func (b *Bonafide) SetManualGateway(label string) { - b.gateways.setUserChoice(label) + b.gateways.setUserChoice([]byte(label)) } func (b *Bonafide) SetAutomaticGateway() { diff --git a/pkg/vpn/bonafide/eip_service.go b/pkg/vpn/bonafide/eip_service.go index 26a8f3c..d5dd751 100644 --- a/pkg/vpn/bonafide/eip_service.go +++ b/pkg/vpn/bonafide/eip_service.go @@ -14,7 +14,7 @@ import ( type eipService struct { Gateways []gatewayV3 defaultGateway string - Locations map[string]location + Locations map[string]Location OpenvpnConfiguration openvpnConfig `json:"openvpn_configuration"` auth string } @@ -22,7 +22,7 @@ type eipService struct { type eipServiceV1 struct { Gateways []gatewayV1 defaultGateway string - Locations map[string]location + Locations map[string]Location OpenvpnConfiguration openvpnConfig `json:"openvpn_configuration"` } @@ -45,8 +45,8 @@ type gatewayV3 struct { Location string } -type location struct { - CountryCode string +type Location struct { + CountryCode string `json:"country_code"` Hemisphere string Name string Timezone string @@ -159,13 +159,15 @@ func (eip eipService) getGateways() []Gateway { for _, g := range eip.Gateways { for _, t := range g.Capabilities.Transport { gateway := Gateway{ - Host: g.Host, - IPAddress: g.IPAddress, - Location: g.Location, - Ports: t.Ports, - Protocols: t.Protocols, - Options: t.Options, - Transport: t.Type, + Host: g.Host, + IPAddress: g.IPAddress, + Location: g.Location, + Ports: t.Ports, + Protocols: t.Protocols, + Options: t.Options, + Transport: t.Type, + LocationName: eip.Locations[g.Location].Name, + CountryCode: eip.Locations[g.Location].CountryCode, } gws = append(gws, gateway) } diff --git a/pkg/vpn/bonafide/gateways.go b/pkg/vpn/bonafide/gateways.go index d973530..f454d3c 100644 --- a/pkg/vpn/bonafide/gateways.go +++ b/pkg/vpn/bonafide/gateways.go @@ -16,14 +16,16 @@ const ( // A Gateway is a representation of gateways that is independent of the api version. // If a given physical location offers different transports, they will appear as separate gateways. type Gateway struct { - Host string - IPAddress string - Location string - Ports []string - Protocols []string - Options map[string]string - Transport string - Label string + Host string + IPAddress string + Location string + LocationName string + CountryCode string + Ports []string + Protocols []string + Options map[string]string + Transport string + Label string } /* TODO add a String method with a human representation: Label (cc) */ @@ -35,18 +37,24 @@ type gatewayDistance struct { } type gatewayPool struct { - available []Gateway + available []Gateway + userChoice []byte /* ranked is, for now, just an array of hostnames (fetched from the geoip service). it should be a map in the future, to keep track of quantitative metrics */ - ranked []string - userChoice string - locations map[string]location + ranked []string + + /* TODO locations are just used to get the timezone for each gateway. I + * think it's easier to just merge that info into the version-agnostic + * Gateway, that is passed from the eipService, and do not worry with + * the location here */ + locations map[string]Location } /* genLabels generates unique, human-readable labels for a gateway. It gives a serial number to each gateway in the same location (paris-1, paris-2,...). The current implementation will give a different label to each transport. + An alternative (to discuss) would be to give the same label to the same hostname. */ func (p *gatewayPool) genLabels() { acc := make(map[string]int) @@ -59,7 +67,7 @@ func (p *gatewayPool) genLabels() { gw.Label = gw.Location + "-" + strconv.Itoa(acc[gw.Location]) p.available[i] = gw } - /* skip suffix if only one occurence */ + /* skip suffix if only one occurrence */ for i, gw := range p.available { if acc[gw.Location] == 1 { gw.Label = gw.Location @@ -102,11 +110,11 @@ func (p *gatewayPool) getGatewayByIP(ip string) (Gateway, error) { } func (p *gatewayPool) setAutomaticChoice() { - p.userChoice = "" + p.userChoice = []byte("") } -func (p *gatewayPool) setUserChoice(label string) error { - if !p.isValidLabel(label) { +func (p *gatewayPool) setUserChoice(label []byte) error { + if !p.isValidLabel(string(label)) { return errors.New("bonafide: not a valid label for gateway choice") } p.userChoice = label @@ -132,7 +140,7 @@ func (p *gatewayPool) setRanking(hostnames []string) { func (p *gatewayPool) getBest(transport string, tz, max int) ([]Gateway, error) { gws := make([]Gateway, 0) if len(p.userChoice) != 0 { - gw, err := p.getGatewayByLabel(p.userChoice) + gw, err := p.getGatewayByLabel(string(p.userChoice)) gws = append(gws, gw) return gws, err } else if len(p.ranked) != 0 { diff --git a/pkg/vpn/openvpn.go b/pkg/vpn/openvpn.go index 38a64a9..530f567 100644 --- a/pkg/vpn/openvpn.go +++ b/pkg/vpn/openvpn.go @@ -25,6 +25,7 @@ import ( "strconv" "strings" + "0xacab.org/leap/bitmask-vpn/pkg/vpn/bonafide" "0xacab.org/leap/shapeshifter" ) @@ -244,6 +245,14 @@ func (b *Bitmask) ListGateways(provider string) ([]string, error) { return gatewayNames, nil } +func (b *Bitmask) GetGatewayDetails(label string) (interface{}, error) { + gw, err := b.bonafide.GetGatewayDetails(label) + if err != nil { + return bonafide.Gateway{}, err + } + return gw, nil +} + // UseGateway selects a gateway, by label, as the default gateway func (b *Bitmask) UseGateway(label string) error { b.bonafide.SetManualGateway(label) |