From 6aaeb8c244ccc17e9a21ea9f8807bc687a5de2c2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 21 Nov 2022 22:42:42 +0200 Subject: [PATCH] Check spec versions supported by homeserver. Fixes #402 --- go.mod | 1 + go.sum | 2 ++ gomuks.go | 25 ++++++++++++++++++++++--- interface/matrix.go | 2 +- main.go | 41 +++++++++++++++++++++++++++++++++++++---- matrix/matrix.go | 37 +++++++++++++++++++++++++++++++++++-- ui/view-login.go | 4 ++-- 7 files changed, 100 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 396ba50..0d2274e 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( golang.org/x/term v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + maunium.net/go/mauflag v1.0.0 // indirect maunium.net/go/maulogger/v2 v2.3.2 // indirect ) diff --git a/go.sum b/go.sum index b82ed58..44d6f53 100644 --- a/go.sum +++ b/go.sum @@ -119,6 +119,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= +maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= maunium.net/go/maulogger/v2 v2.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0= maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/mautrix v0.11.0 h1:B1FBHcvE4Mud+AC+zgNQQOw0JxSVrt40watCejhVA7w= diff --git a/gomuks.go b/gomuks.go index 865a279..a68a699 100644 --- a/gomuks.go +++ b/gomuks.go @@ -17,9 +17,12 @@ package main import ( + "errors" "fmt" "os" "os/signal" + "path/filepath" + "runtime" "strings" "syscall" "time" @@ -60,7 +63,7 @@ func init() { Version = fmt.Sprintf("%s%s.unknown", Version, suffix) } } - VersionString = fmt.Sprintf("gomuks %s (%s)", Version, BuildTime) + VersionString = fmt.Sprintf("gomuks %s (%s with %s)", Version, BuildTime, runtime.Version()) } // Gomuks is the wrapper for everything. @@ -142,7 +145,23 @@ func (gmx *Gomuks) internalStop(save bool) { // If the tview app returns an error, it will be passed into panic(), which // will be recovered as specified in Recover(). func (gmx *Gomuks) Start() { - _ = gmx.matrix.InitClient() + err := gmx.matrix.InitClient(true) + if err != nil { + if errors.Is(err, matrix.ErrServerOutdated) { + _, _ = fmt.Fprintln(os.Stderr, strings.Replace(err.Error(), "homeserver", gmx.config.HS, 1)) + _, _ = fmt.Fprintln(os.Stderr) + _, _ = fmt.Fprintf(os.Stderr, "See `%s --help` if you want to skip this check or clear all data.\n", os.Args[0]) + os.Exit(4) + } else if strings.HasPrefix(err.Error(), "failed to check server versions") { + _, _ = fmt.Fprintln(os.Stderr, "Failed to check versions supported by server:", errors.Unwrap(err)) + _, _ = fmt.Fprintln(os.Stderr) + _, _ = fmt.Fprintf(os.Stderr, "Modify %s if the server has moved.\n", filepath.Join(gmx.config.Dir, "config.yaml")) + _, _ = fmt.Fprintf(os.Stderr, "See `%s --help` if you want to skip this check or clear all data.\n", os.Args[0]) + os.Exit(5) + } else { + panic(err) + } + } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) @@ -152,7 +171,7 @@ func (gmx *Gomuks) Start() { }() go gmx.StartAutosave() - if err := gmx.ui.Start(); err != nil { + if err = gmx.ui.Start(); err != nil { panic(err) } } diff --git a/interface/matrix.go b/interface/matrix.go index 2962575..d4b2baa 100644 --- a/interface/matrix.go +++ b/interface/matrix.go @@ -43,7 +43,7 @@ type UploadedMediaInfo struct { type MatrixContainer interface { Client() *mautrix.Client Preferences() *config.UserPreferences - InitClient() error + InitClient(isStartup bool) error Initialized() bool Start() diff --git a/main.go b/main.go index 777990e..3083683 100644 --- a/main.go +++ b/main.go @@ -26,14 +26,38 @@ import ( "strings" "time" + flag "maunium.net/go/mauflag" + "maunium.net/go/gomuks/debug" ifc "maunium.net/go/gomuks/interface" + "maunium.net/go/gomuks/matrix" "maunium.net/go/gomuks/ui" ) var MainUIProvider ifc.UIProvider = ui.NewGomuksUI +var wantVersion = flag.MakeFull("v", "version", "Show the version of gomuks", "false").Bool() +var clearCache = flag.MakeFull("c", "clear-cache", "Clear the cache directory instead of starting", "false").Bool() +var clearData = flag.Make().LongKey("clear-all-data").Usage("Clear all data instead of starting").Default("false").Bool() +var skipVersionCheck = flag.MakeFull("s", "skip-version-check", "Skip the homeserver version checks at startup and login", "false").Bool() +var wantHelp, _ = flag.MakeHelpFlag() + func main() { + flag.SetHelpTitles( + "gomuks - A terminal Matrix client written in Go.", + "gomuks [-vch] [--clear-all-data]", + ) + err := flag.Parse() + if err != nil { + fmt.Println(err) + os.Exit(1) + } else if *wantHelp { + flag.PrintHelp() + return + } else if *wantVersion { + fmt.Println(VersionString) + return + } debugDir := os.Getenv("DEBUG_DIR") if len(debugDir) > 0 { debug.LogDirectory = debugDir @@ -51,7 +75,6 @@ func main() { defer debug.Recover() var configDir, dataDir, cacheDir, downloadDir string - var err error configDir, err = UserConfigDir() if err != nil { @@ -79,11 +102,21 @@ func main() { debug.Print("Cache directory:", cacheDir) debug.Print("Download directory:", downloadDir) + matrix.SkipVersionCheck = *skipVersionCheck gmx := NewGomuks(MainUIProvider, configDir, dataDir, cacheDir, downloadDir) - if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "-v") { - fmt.Println(VersionString) - os.Exit(0) + if *clearCache { + debug.Print("Clearing cache as requested by CLI flag") + gmx.config.Clear() + fmt.Printf("Cleared cache at %s\n", gmx.config.CacheDir) + return + } else if *clearData { + debug.Print("Clearing all data as requested by CLI flag") + gmx.config.Clear() + gmx.config.ClearData() + _ = os.RemoveAll(gmx.config.Dir) + fmt.Printf("Cleared cache at %s, data at %s and config at %s\n", gmx.config.CacheDir, gmx.config.DataDir, gmx.config.Dir) + return } gmx.Start() diff --git a/matrix/matrix.go b/matrix/matrix.go index 4165b2b..737b497 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -92,10 +92,21 @@ func (c *Container) Crypto() ifc.Crypto { return c.crypto } +var ( + ErrNoHomeserver = errors.New("no homeserver entered") + ErrServerOutdated = errors.New("homeserver is outdated") +) + +var MinSpecVersion = mautrix.SpecV11 +var SkipVersionCheck = false + // InitClient initializes the mautrix client and connects to the homeserver specified in the config. -func (c *Container) InitClient() error { +func (c *Container) InitClient(isStartup bool) error { if len(c.config.HS) == 0 { - return fmt.Errorf("no homeserver entered") + if isStartup { + return nil + } + return ErrNoHomeserver } if c.client != nil { @@ -139,6 +150,28 @@ func (c *Container) InitClient() error { } } + if !SkipVersionCheck && (!isStartup || len(c.client.AccessToken) > 0) { + debug.Printf("Checking versions that %s supports", c.client.HomeserverURL) + resp, err := c.client.Versions() + if err != nil { + debug.Print("Error checking supported versions:", err) + return fmt.Errorf("failed to check server versions: %w", err) + } else if !resp.ContainsGreaterOrEqual(MinSpecVersion) { + debug.Print("Server doesn't support modern spec versions") + bestVersionStr := "nothing" + bestVersion := mautrix.MustParseSpecVersion("r0.0.0") + for _, ver := range resp.Versions { + if ver.GreaterThan(bestVersion) { + bestVersion = ver + bestVersionStr = ver.String() + } + } + return fmt.Errorf("%w (it only supports %s, while gomuks requires %s)", ErrServerOutdated, bestVersionStr, MinSpecVersion.String()) + } else { + debug.Print("Server supports modern spec versions") + } + } + c.stop = make(chan bool, 1) if len(accessToken) > 0 { diff --git a/ui/view-login.go b/ui/view-login.go index 015a1f3..c648076 100644 --- a/ui/view-login.go +++ b/ui/view-login.go @@ -149,7 +149,7 @@ func (view *LoginView) Error(err string) { view.AddComponent(view.error, 1, 11, 3, 1) } view.error.SetText(err) - errorHeight := int(math.Ceil(float64(runewidth.StringWidth(err)) / 45)) + errorHeight := int(math.Ceil(float64(runewidth.StringWidth(err)) / 41)) view.container.SetHeight(14 + errorHeight) view.SetRow(11, errorHeight) } @@ -161,7 +161,7 @@ func (view *LoginView) actuallyLogin(hs, mxid, password string) { debug.Printf("Logging into %s as %s...", hs, mxid) view.config.HS = hs - if err := view.matrix.InitClient(); err != nil { + if err := view.matrix.InitClient(false); err != nil { debug.Print("Init error:", err) view.Error(err.Error()) } else if err = view.matrix.Login(mxid, password); err != nil {