mirror of
https://github.com/anotherhadi/proton-vpn-tui.git
synced 2026-10-05 19:28:28 +02:00
110 lines
2.3 KiB
Go
110 lines
2.3 KiB
Go
package backend
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const DefaultTimeout = 15 * time.Second
|
|
|
|
var BinaryName = "protonvpn"
|
|
|
|
var ErrTimeout = errors.New("protonvpn: command timeout")
|
|
|
|
var ErrNotFound = errors.New("protonvpn: binary not found in PATH")
|
|
|
|
type Result struct {
|
|
Stdout string
|
|
Stderr string
|
|
}
|
|
|
|
type CommandError struct {
|
|
Result *Result
|
|
Err error
|
|
}
|
|
|
|
const unexpectedErrorBanner = "An unexpected error occurred. Please try again."
|
|
|
|
func (e *CommandError) Error() string {
|
|
if strings.HasPrefix(e.Result.Stderr, unexpectedErrorBanner) ||
|
|
strings.HasPrefix(e.Result.Stdout, unexpectedErrorBanner) {
|
|
return unexpectedErrorBanner
|
|
}
|
|
if e.Result.Stderr != "" {
|
|
return fmt.Sprintf("%v (stderr: %q)", e.Err, e.Result.Stderr)
|
|
}
|
|
if e.Result.Stdout != "" {
|
|
return e.Result.Stdout
|
|
}
|
|
return e.Err.Error()
|
|
}
|
|
|
|
func (e *CommandError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
func IsAuthRequired(err error) bool {
|
|
var cmdErr *CommandError
|
|
if !errors.As(err, &cmdErr) {
|
|
return false
|
|
}
|
|
return strings.Contains(cmdErr.Result.Stderr, "protonvpn signin") ||
|
|
strings.Contains(cmdErr.Result.Stdout, "protonvpn signin")
|
|
}
|
|
|
|
func IsFreePlanRestricted(err error) bool {
|
|
var cmdErr *CommandError
|
|
if !errors.As(err, &cmdErr) {
|
|
return false
|
|
}
|
|
return strings.Contains(cmdErr.Result.Stderr, "not available on the free plan") ||
|
|
strings.Contains(cmdErr.Result.Stdout, "not available on the free plan")
|
|
}
|
|
|
|
func Available() error {
|
|
if _, err := exec.LookPath(BinaryName); err != nil {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Run(ctx context.Context, args ...string) (*Result, error) {
|
|
if ctx == nil {
|
|
var cancel context.CancelFunc
|
|
ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout)
|
|
defer cancel()
|
|
}
|
|
|
|
cmd := exec.CommandContext(ctx, BinaryName, args...)
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
runErr := cmd.Run()
|
|
|
|
res := &Result{
|
|
Stdout: strings.TrimSpace(stdout.String()),
|
|
Stderr: strings.TrimSpace(stderr.String()),
|
|
}
|
|
|
|
if ctx.Err() == context.DeadlineExceeded {
|
|
return res, &CommandError{Result: res, Err: ErrTimeout}
|
|
}
|
|
|
|
if runErr != nil {
|
|
return res, &CommandError{Result: res, Err: runErr}
|
|
}
|
|
|
|
if strings.HasPrefix(res.Stdout, "Error:") {
|
|
return res, &CommandError{Result: res, Err: errors.New(res.Stdout)}
|
|
}
|
|
|
|
return res, nil
|
|
}
|