summaryrefslogtreecommitdiff
path: root/client/client.go
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2023-05-15 11:52:20 +0200
committerJeroen Wijenbergh <46386452+jwijenbergh@users.noreply.github.com>2023-09-25 09:43:37 +0200
commit76e709193614b3668d3c31a078e667473af20369 (patch)
tree66f0e6caf03e19b5034c687e3b4c3cbd904be03a /client/client.go
parentc1847c9cf76cb17e5b027ac4c0aea99d15852d45 (diff)
Initial i18n implementation
Diffstat (limited to 'client/client.go')
-rw-r--r--client/client.go173
1 files changed, 82 insertions, 91 deletions
diff --git a/client/client.go b/client/client.go
index 124d9b5..3c18027 100644
--- a/client/client.go
+++ b/client/client.go
@@ -1,13 +1,15 @@
+//go:generate go run golang.org/x/text/cmd/gotext -srclang=en update -out=zgotext.go -lang=da,de,en,es,fr,it,nl,ukr
+
// Package client implements the public interface for creating eduVPN/Let's Connect! clients
package client
import (
"context"
- "fmt"
"strings"
"sync"
"time"
+ "github.com/eduvpn/eduvpn-common/i18nerr"
"github.com/eduvpn/eduvpn-common/internal/config"
"github.com/eduvpn/eduvpn-common/internal/discovery"
"github.com/eduvpn/eduvpn-common/internal/failover"
@@ -73,15 +75,6 @@ func userAgentName(clientID string) string {
}
}
-func (c *Client) logError(err error) {
- // Logs the error with the same level/verbosity as the error
- if c.Debug {
- log.Logger.Inherit(err, fmt.Sprintf("\nwith stacktrace: %s\n", err.(*errors.Error).ErrorStack()))
- } else {
- log.Logger.Inherit(err, "")
- }
-}
-
func (c *Client) isLetsConnect() bool {
// see https://git.sr.ht/~fkooman/vpn-user-portal/tree/v3/item/src/OAuth/VpnClientDb.php
return strings.HasPrefix(c.Name, "org.letsconnect-vpn.app")
@@ -121,7 +114,7 @@ type Client struct {
func (c *Client) updateTokens(srv server.Server) error {
if c.TokenGetter == nil {
- return errors.New("no tokken getter defined")
+ return errors.New("no token getter defined")
}
pSrv, err := c.pubCurrentServer(srv)
if err != nil {
@@ -168,10 +161,10 @@ func (c *Client) forwardTokens(srv server.Server) error {
func (c *Client) goTransition(id fsm.StateID) error {
handled, err := c.FSM.GoTransition(id)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "Internal state transition error")
}
if !handled {
- log.Logger.Debugf("transition not handled by the client: %s", GetStateName(id))
+ log.Logger.Debugf("transition not handled by the client to internal state: '%s'", GetStateName(id))
}
return nil
}
@@ -188,11 +181,11 @@ func New(name string, version string, directory string, stateCallback func(FSMSt
c = &Client{}
if !isAllowedClientID(name) {
- return nil, errors.Errorf("client ID is not allowed: '%v', see https://git.sr.ht/~fkooman/vpn-user-portal/tree/v3/item/src/OAuth/VpnClientDb.php for a list of allowed IDs", name)
+ return nil, i18nerr.Newf("The client registered with an invalid client ID: '%v'", name)
}
if len([]rune(version)) > 20 {
- return nil, errors.Errorf("version is not allowed: '%s', must be max 20 characters", version)
+ return nil, i18nerr.Newf("The client registered with an invalid version: '%v'", version)
}
// Initialize the logger
@@ -202,7 +195,7 @@ func New(name string, version string, directory string, stateCallback func(FSMSt
}
if err = log.Logger.Init(lvl, directory); err != nil {
- return nil, err
+ return nil, i18nerr.Wrapf(err, "The log file with directory: '%s' failed to initialize", directory)
}
// set client name
@@ -235,11 +228,11 @@ func New(name string, version string, directory string, stateCallback func(FSMSt
// Registering means updating the FSM to get to the initial state correctly
func (c *Client) Register() error {
if !c.FSM.InState(StateDeregistered) {
- return errors.Errorf("fsm attempt to register while in '%v'", c.FSM.Current)
+ return i18nerr.Wrapf(errors.New("The client tried to re-initialize without deregistering first"), "Client has an invalid state")
}
err := c.goTransition(StateNoServer)
if err != nil {
- return errors.WrapPrefix(err, "failed to register", 0)
+ return err
}
return nil
}
@@ -253,7 +246,7 @@ func (c *Client) Deregister() {
// Save the config
if err := c.Config.Save(&c); err != nil {
- log.Logger.Infof("c.Config.Save failed: %s\nstacktrace:\n%s", err.Error(), err.(*errors.Error).ErrorStack())
+ log.Logger.Infof("failed saving state configuration: '%v'", err)
}
// Empty out the state
@@ -265,15 +258,9 @@ func (c *Client) Deregister() {
// If this is the case then a previous version of the list is returned if there is any.
// This takes into account the frequency of updates, see: https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md#organization-list.
func (c *Client) DiscoOrganizations(ck *cookie.Cookie) (orgs *discotypes.Organizations, err error) {
- defer func() {
- if err != nil {
- c.logError(err)
- }
- }()
-
// Not supported with Let's Connect!
if c.isLetsConnect() {
- return nil, errors.Errorf("discovery with Let's Connect is not supported")
+ return nil, i18nerr.Newf("Server/organization discovery with Let's Connect is not supported")
}
// Mark organizations as expired if we have not set an organization yet
@@ -281,8 +268,11 @@ func (c *Client) DiscoOrganizations(ck *cookie.Cookie) (orgs *discotypes.Organiz
c.Discovery.MarkOrganizationsExpired()
}
- // TODO: pass a context
- return c.Discovery.Organizations(ck.Context())
+ orgs, err = c.Discovery.Organizations(ck.Context())
+ if err != nil {
+ err = i18nerr.Wrap(err, "An error occurred after getting the discovery files for the list of organizations")
+ }
+ return
}
// DiscoServers gets the servers list from the discovery server
@@ -290,19 +280,16 @@ func (c *Client) DiscoOrganizations(ck *cookie.Cookie) (orgs *discotypes.Organiz
// If this is the case then a previous version of the list is returned if there is any.
// This takes into account the frequency of updates, see: https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md#server-list.
func (c *Client) DiscoServers(ck *cookie.Cookie) (dss *discotypes.Servers, err error) {
- defer func() {
- if err != nil {
- c.logError(err)
- }
- }()
-
// Not supported with Let's Connect!
if c.isLetsConnect() {
- return nil, errors.Errorf("discovery with Let's Connect is not supported")
+ return nil, i18nerr.Newf("Server/organization discovery with Let's Connect is not supported")
}
- // TODO: pass a context
- return c.Discovery.Servers(ck.Context())
+ dss, err = c.Discovery.Servers(ck.Context())
+ if err != nil {
+ err = i18nerr.Wrap(err, "An error occurred after getting the discovery files for the list of servers")
+ }
+ return
}
// ExpiryTimes returns the different Unix timestamps regarding expiry
@@ -314,17 +301,15 @@ func (c *Client) ExpiryTimes() (*srvtypes.Expiry, error) {
// Get current expiry time
srv, err := c.Servers.Current()
if err != nil {
- c.logError(err)
- return nil, err
+ return nil, i18nerr.Wrap(err, "The current server could not be found when getting it for expiry")
}
b, err := srv.Base()
if err != nil {
- c.logError(err)
return nil, err
}
if b.StartTime.IsZero() {
- return nil, errors.New("start time is zero, did you get a configuration?")
+ return nil, i18nerr.New("No start time is defined for this server")
}
bT := b.RenewButtonTime()
@@ -387,7 +372,7 @@ func (c *Client) callbacks(ck *cookie.Cookie, srv server.Server, forceauth bool)
if srv.NeedsLocation() {
err := c.locationCallback(ck)
if err != nil {
- return err
+ return i18nerr.Wrap(err, "The secure internet location could not be set")
}
}
@@ -412,7 +397,7 @@ func (c *Client) callbacks(ck *cookie.Cookie, srv server.Server, forceauth bool)
}
err := c.loginCallback(ck, srv)
if err != nil {
- return err
+ return i18nerr.Wrap(err, "The authorization procedure failed to complete")
}
}
err = c.goTransition(StateAuthorized)
@@ -426,19 +411,18 @@ func (c *Client) callbacks(ck *cookie.Cookie, srv server.Server, forceauth bool)
func (c *Client) profileCallback(ck *cookie.Cookie, srv server.Server) error {
vp, err := server.HasValidProfile(ck.Context(), srv, c.SupportsWireguard)
if err != nil {
- return err
+ log.Logger.Warningf("failed to determine whether the current protocol is valid with error: %v", err)
}
if !vp {
- b, err := srv.Base()
+ vps, err := server.ValidProfiles(srv, c.SupportsWireguard)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "No suitable profiles could be found")
}
- ps := b.Profiles.Public()
errChan := make(chan error)
go func() {
err := c.FSM.GoTransitionRequired(StateAskProfile, &srvtypes.RequiredAskTransition{
C: ck,
- Data: ps,
+ Data: vps.Public(),
})
if err != nil {
errChan <- err
@@ -446,11 +430,11 @@ func (c *Client) profileCallback(ck *cookie.Cookie, srv server.Server) error {
}()
pID, err := ck.Receive(errChan)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "Profile with ID: '%s' could not be set", pID)
}
err = server.Profile(srv, pID)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "Profile with ID: '%s' could not be obtained from the server", pID)
}
}
err = c.goTransition(StateChosenProfile)
@@ -473,13 +457,13 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes.
}
// If we must run callbacks, go to the previous state if we're not in it
if !ni && !c.FSM.InState(previousState) {
- c.FSM.GoTransition(previousState)
+ c.FSM.GoTransition(previousState) //nolint:errcheck
}
}()
if !ni {
- // This only returns a boolean if it was handled
err = c.goTransition(StateLoadingServer)
+ // this is already wrapped in an UI error
if err != nil {
return err
}
@@ -488,7 +472,7 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes.
if _type != srvtypes.TypeSecureInternet {
identifier, err = http.EnsureValidURL(identifier, true)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "The identifier that was passed to the library is incorrect")
}
}
@@ -498,11 +482,11 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes.
case srvtypes.TypeInstituteAccess:
dSrv, err := c.Discovery.ServerByURL(identifier, "institute_access")
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "Could not retrieve institute access server with URL: '%s' from discovery", identifier)
}
srv, err = c.Servers.AddInstituteAccess(ck.Context(), c.Name ,dSrv)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "The institute access server with URL: '%s' could not be added", identifier)
}
case srvtypes.TypeSecureInternet:
dOrg, dSrv, err := c.Discovery.SecureHomeArgs(identifier)
@@ -511,19 +495,19 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes.
// Note that in the docs it states that it only should happen when the Org ID doesn't exist
// However, this is nice as well because it also catches the error where the SecureInternetHome server is not found
c.Discovery.MarkOrganizationsExpired()
- return err
+ return i18nerr.Wrapf(err, "The secure internet server with organisation ID: '%s' could not be retrieved from discovery", identifier)
}
srv, err = c.Servers.AddSecureInternet(ck.Context(), c.Name, dOrg, dSrv)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "The secure internet server with organisation ID: '%s' could not be added", identifier)
}
case srvtypes.TypeCustom:
srv, err = c.Servers.AddCustom(ck.Context(), c.Name, identifier)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "The custom server with URL: '%s' could not be added", identifier)
}
default:
- return errors.Errorf("not a valid server type: %v", _type)
+ return i18nerr.Newf("Server type: '%v' is not valid to be added", _type)
}
// if we are non interactive, we run no callbacks
@@ -533,6 +517,7 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes.
// callbacks
err = c.callbacks(ck, srv, false)
+ // error is already UI wrapped
if err != nil {
return err
}
@@ -562,16 +547,13 @@ func (c *Client) config(ck *cookie.Cookie, srv server.Server, pTCP bool, forceAu
cfgS, err := server.Config(ck.Context(), srv, c.SupportsWireguard, pTCP)
if err != nil {
- return nil, err
+ return nil, i18nerr.Wrap(err, "The VPN configuration could not be obtained")
}
p, err := server.CurrentProfile(srv)
if err != nil {
- return nil, err
+ return nil, i18nerr.Wrap(err, "The current profile could not be found")
}
pcfg := cfgS.Public(p.DefaultGateway)
- if err != nil {
- return nil, err
- }
return &pcfg, nil
}
@@ -587,7 +569,7 @@ func (c *Client) server(identifier string, _type srvtypes.Type) (srv server.Serv
srv, err = c.Servers.CustomServer(identifier)
setter = c.Servers.SetCustom
default:
- return nil, nil, errors.Errorf("not a valid server type: %v", _type)
+ return nil, nil, i18nerr.Newf("Not a valid server type: %v", _type)
}
return srv, setter, err
}
@@ -599,18 +581,16 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes.
previousState := c.FSM.Current
defer func() {
if err == nil {
- c.FSM.GoTransition(StateGotConfig)
- } else {
- if !c.FSM.InState(previousState) {
- // go back to the previous state if an error occurred
- c.FSM.GoTransition(previousState)
- }
+ c.FSM.GoTransition(StateGotConfig) //nolint:errcheck
+ } else if !c.FSM.InState(previousState) {
+ // go back to the previous state if an error occurred
+ c.FSM.GoTransition(previousState) //nolint:errcheck
}
}()
if _type != srvtypes.TypeSecureInternet {
identifier, err = http.EnsureValidURL(identifier, true)
if err != nil {
- return nil, err
+ return nil, i18nerr.Wrapf(err, "Identifier: '%s' for server with type: '%d' is not valid", identifier, _type)
}
}
err = c.goTransition(StateLoadingServer)
@@ -625,10 +605,9 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes.
err = server.RefreshEndpoints(ck.Context(), srv)
// If we get a canceled error, return that, otherwise just log the error
- cErr := context.Canceled
if err != nil {
- if errors.As(err, &cErr) {
- return nil, err
+ if errors.Is(err, context.Canceled) {
+ return nil, i18nerr.Wrap(err, "The operation for getting a VPN configuration was canceled")
}
log.Logger.Warningf("failed to refresh server endpoints: %v", err)
@@ -638,6 +617,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes.
cfg, err = c.config(ck, srv, pTCP, false)
tErr := &oauth.TokensInvalidError{}
if err != nil && errors.As(err, &tErr) {
+ log.Logger.Debugf("the tokens were invalid, trying again...")
cfg, err = c.config(ck, srv, pTCP, true)
}
@@ -656,7 +636,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes.
// set the current server
if err = set(srv); err != nil {
- return nil, err
+ return nil, i18nerr.Wrapf(err, "Failed to set the server with identifier: '%s' as the current", identifier)
}
return cfg, nil
@@ -666,19 +646,25 @@ func (c *Client) RemoveServer(identifier string, _type srvtypes.Type) (err error
if _type != srvtypes.TypeSecureInternet {
identifier, err = http.EnsureValidURL(identifier, true)
if err != nil {
- return err
+ return i18nerr.Wrapf(err, "Identifier: '%s' for server with type: '%d' is not valid for removal", identifier, _type)
}
}
+ // miscellaneous error
+ var mErr error
switch _type {
case srvtypes.TypeInstituteAccess:
- return c.Servers.RemoveInstituteAccess(identifier)
+ mErr = c.Servers.RemoveInstituteAccess(identifier)
case srvtypes.TypeSecureInternet:
- return c.Servers.RemoveSecureInternet(identifier)
+ mErr = c.Servers.RemoveSecureInternet(identifier)
case srvtypes.TypeCustom:
- return c.Servers.RemoveCustom(identifier)
+ mErr = c.Servers.RemoveCustom(identifier)
default:
- return errors.Errorf("not a valid server type: %v", _type)
+ return i18nerr.Newf("Not a valid server type: %v", _type)
+ }
+ if mErr != nil {
+ log.Logger.Debugf("failed to remove server with identifier: '%s' and type: '%d', error: %v", identifier, _type, mErr)
}
+ return nil
}
func (c *Client) CurrentServer() (*srvtypes.Current, error) {
@@ -759,7 +745,7 @@ func (c *Client) pubServer(srv server.Server) (interface{}, error) {
func (c *Client) ServerList() (*srvtypes.List, error) {
if c.FSM.InState(StateDeregistered) {
- return nil, errors.New("client is not registered")
+ return nil, i18nerr.New("Client is not registered")
}
var customServers []srvtypes.Server
for _, v := range c.Servers.CustomServers.Map {
@@ -821,7 +807,7 @@ func (c *Client) Cleanup(ck *cookie.Cookie) (err error) {
// get the current server
srv, err := c.Servers.Current()
if err != nil {
- return err
+ return i18nerr.Wrap(err, "Failed to get the current server to cleanup the connection")
}
err = c.updateTokens(srv)
if err != nil {
@@ -829,7 +815,7 @@ func (c *Client) Cleanup(ck *cookie.Cookie) (err error) {
}
err = server.Disconnect(ck.Context(), srv)
if err != nil {
- return err
+ return i18nerr.Wrap(err, "Failed to cleanup the VPN connection for the current server")
}
err = c.forwardTokens(srv)
if err != nil {
@@ -840,11 +826,11 @@ func (c *Client) Cleanup(ck *cookie.Cookie) (err error) {
func (c *Client) SetSecureLocation(ck *cookie.Cookie, countryCode string) (err error) {
if c.isLetsConnect() {
- return errors.Errorf("setting a secure internet location with Let's Connect! is not supported")
+ return i18nerr.Newf("Setting a secure internet location with Let's Connect! is not supported")
}
if !c.Servers.HasSecureInternet() {
- return errors.Errorf("no secure internet server available to set a location for")
+ return i18nerr.Newf("No secure internet server available to set a location for")
}
dSrv, err := c.Discovery.ServerByCountryCode(countryCode)
@@ -860,13 +846,13 @@ func (c *Client) RenewSession(ck *cookie.Cookie) (err error) {
defer c.mu.Unlock()
srv, err := c.Servers.Current()
if err != nil {
- return err
+ return i18nerr.Wrap(err, "Failed to get current server for renewing the session")
}
// The server has not been chosen yet, this means that we want to manually renew
// TODO: is this needed?
if !c.FSM.InState(StateChosenServer) {
- c.FSM.GoTransition(StateLoadingServer)
- c.FSM.GoTransition(StateChosenServer)
+ c.FSM.GoTransition(StateLoadingServer) //nolint:errcheck
+ c.FSM.GoTransition(StateChosenServer) //nolint:errcheck
}
// update tokens in the end
defer func() {
@@ -884,12 +870,17 @@ func (c *Client) RenewSession(ck *cookie.Cookie) (err error) {
func (c *Client) StartFailover(ck *cookie.Cookie, gateway string, mtu int, readRxBytes func() (int64, error)) (bool, error) {
f := failover.New(readRxBytes)
- return f.Start(ck.Context(), gateway, mtu)
+ d, err := f.Start(ck.Context(), gateway, mtu)
+ if err != nil {
+ return d, i18nerr.Wrapf(err, "Failover failed to complete with gateway: '%s' and mtu: '%d'", gateway, mtu)
+ }
+ return d, nil
}
func (c *Client) SetState(state FSMStateID) error {
c.mu.Lock()
defer c.mu.Unlock()
+ curr := c.FSM.Current
_, err := c.FSM.GoTransition(state)
if err != nil {
// self-transitions are only debug errors
@@ -897,7 +888,7 @@ func (c *Client) SetState(state FSMStateID) error {
log.Logger.Debugf("attempt an invalid self-transition: %s", c.FSM.GetStateName(state))
return nil
}
- return err
+ return i18nerr.Wrapf(err, "Failed internal state transition requested by the client from: '%s' to '%s'", GetStateName(curr), GetStateName(state))
}
return nil
}