mirror of
https://github.com/anotherhadi/proton-vpn-tui.git
synced 2026-10-05 19:28:28 +02:00
104 lines
2.1 KiB
Go
104 lines
2.1 KiB
Go
package backend
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const protonInterface = "proton0"
|
|
|
|
var ErrNmcliNotFound = errors.New("nmcli: binary not found in PATH")
|
|
|
|
type ConnectionStatus struct {
|
|
Connected bool
|
|
Server string
|
|
Location string
|
|
Load int
|
|
Protocol string
|
|
}
|
|
|
|
func Status() (ConnectionStatus, error) {
|
|
connected, err := isConnectedViaNetworkManager()
|
|
if err != nil {
|
|
return ConnectionStatus{}, err
|
|
}
|
|
if !connected {
|
|
return ConnectionStatus{Connected: false}, nil
|
|
}
|
|
|
|
res, err := Run(context.Background(), "status")
|
|
if err != nil {
|
|
return ConnectionStatus{}, err
|
|
}
|
|
|
|
status := parseStatus(res.Stdout)
|
|
status.Connected = true
|
|
return status, nil
|
|
}
|
|
|
|
func IsConnected() (bool, error) {
|
|
return isConnectedViaNetworkManager()
|
|
}
|
|
|
|
func isConnectedViaNetworkManager() (bool, error) {
|
|
if _, err := exec.LookPath("nmcli"); err != nil {
|
|
return false, ErrNmcliNotFound
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx, "nmcli", "-t", "-f", "DEVICE", "connection", "show", "--active")
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return false, fmt.Errorf("nmcli: %w (stderr: %q)", err, strings.TrimSpace(stderr.String()))
|
|
}
|
|
|
|
for device := range strings.SplitSeq(strings.TrimSpace(stdout.String()), "\n") {
|
|
if device == protonInterface {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func parseStatus(output string) ConnectionStatus {
|
|
var status ConnectionStatus
|
|
|
|
for line := range strings.SplitSeq(output, "\n") {
|
|
key, value, found := strings.Cut(strings.TrimSpace(line), ": ")
|
|
if !found {
|
|
continue
|
|
}
|
|
|
|
switch key {
|
|
case "Status":
|
|
status.Connected = value == "Connected"
|
|
case "Server":
|
|
if server, location, ok := strings.Cut(value, " in "); ok {
|
|
status.Server = server
|
|
status.Location = location
|
|
} else {
|
|
status.Server = value
|
|
}
|
|
case "Load":
|
|
if load, err := strconv.Atoi(strings.TrimSuffix(value, "%")); err == nil {
|
|
status.Load = load
|
|
}
|
|
case "Protocol":
|
|
status.Protocol = value
|
|
}
|
|
}
|
|
|
|
return status
|
|
}
|