From 3f47444d7ebe60b5cc71c84ef64c24f29d8f2174 Mon Sep 17 00:00:00 2001 From: Michele Bertasi Date: Thu, 24 Mar 2022 20:56:14 +0100 Subject: [PATCH 1/2] Fix build warning about redundant newline. --- cmd/gmailctl/cmd/apply_cmd.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/gmailctl/cmd/apply_cmd.go b/cmd/gmailctl/cmd/apply_cmd.go index d9e64da3..3dc0e2f4 100644 --- a/cmd/gmailctl/cmd/apply_cmd.go +++ b/cmd/gmailctl/cmd/apply_cmd.go @@ -20,6 +20,7 @@ const renameLabelWarning = `Warning: You are going to delete labels. This operat irreversible, because it also removes those labels from messages. If you are looking for renaming labels, please use the GMail UI. + ` // applyCmd represents the apply command @@ -84,7 +85,7 @@ func apply(path string, interactive, test bool) error { } if len(diff.LabelsDiff.Removed) > 0 { - fmt.Println(renameLabelWarning) + fmt.Print(renameLabelWarning) if !applyRemoveLabels { return errors.WithDetails(errors.New("no changes have been made"), "To protect you, deletion is disabled unless you\n"+ From 8ebadcc888b40cd8bfcaf07fc73f8e9efd28fb4a Mon Sep 17 00:00:00 2001 From: Michele Bertasi Date: Thu, 24 Mar 2022 21:57:01 +0100 Subject: [PATCH 2/2] Add refresh-expired flag. This should help with tokens that expire quickly (7d). It should mitigate #232. --- cmd/gmailctl/cmd/api_provider.go | 6 ++++++ cmd/gmailctl/cmd/init_cmd.go | 16 +++++++++++++++- cmd/gmailctl/localcred/local_provider.go | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/cmd/gmailctl/cmd/api_provider.go b/cmd/gmailctl/cmd/api_provider.go index 3fc86bd3..b84ba5ee 100644 --- a/cmd/gmailctl/cmd/api_provider.go +++ b/cmd/gmailctl/cmd/api_provider.go @@ -31,6 +31,12 @@ type APIKeyProvider interface { APIKey() string } +// TokenRefresher allows to refresh API tokens if they are expired. +type TokenRefresher interface { + // RefreshToken refreshes the token if it is expired. + RefreshToken(ctx context.Context, cfgDir string) error +} + func openAPI() (*api.GmailAPI, error) { srv, err := APIProvider.Service(context.Background(), cfgDir) if err != nil { diff --git a/cmd/gmailctl/cmd/init_cmd.go b/cmd/gmailctl/cmd/init_cmd.go index 16e8e242..13c146ec 100644 --- a/cmd/gmailctl/cmd/init_cmd.go +++ b/cmd/gmailctl/cmd/init_cmd.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "fmt" "os" "path" @@ -10,7 +11,10 @@ import ( "github.com/mbrt/gmailctl/internal/data" ) -var initReset bool +var ( + initReset bool + initRefreshExpired bool +) // initCmd represents the init command var initCmd = &cobra.Command{ @@ -23,6 +27,8 @@ setting up the API authorizations and initial settings.`, var err error if initReset { err = resetConfig() + } else if initRefreshExpired { + err = refreshToken() } else { err = continueConfig() } @@ -37,6 +43,7 @@ func init() { // Flags and configuration settings initCmd.Flags().BoolVar(&initReset, "reset", false, "Reset the configuration.") + initCmd.Flags().BoolVar(&initRefreshExpired, "refresh-expired", false, "Refresh auth token if expired.") } func resetConfig() error { @@ -58,6 +65,13 @@ func continueConfig() error { return nil } +func refreshToken() error { + if rt, ok := APIProvider.(TokenRefresher); ok { + return rt.RefreshToken(context.Background(), cfgDir) + } + return nil +} + func handleCfgDir() (err error) { // Create the config dir if _, err := os.Stat(cfgDir); err != nil { diff --git a/cmd/gmailctl/localcred/local_provider.go b/cmd/gmailctl/localcred/local_provider.go index 514f4d7a..f9069504 100644 --- a/cmd/gmailctl/localcred/local_provider.go +++ b/cmd/gmailctl/localcred/local_provider.go @@ -10,6 +10,7 @@ import ( "github.com/mbrt/gmailctl/cmd/gmailctl/cmd" "github.com/mbrt/gmailctl/internal/engine/api" + "github.com/mbrt/gmailctl/internal/errors" ) const ( @@ -88,6 +89,23 @@ func (Provider) ResetConfig(cfgDir string) error { return nil } +func (Provider) RefreshToken(ctx context.Context, cfgDir string) error { + auth, err := openCredentials(credentialsPath(cfgDir)) + if err != nil { + return errors.WithDetails(fmt.Errorf("invalid credentials: %w", err), + "Please run 'gmailctl init' to initialize the credentials.") + } + svc, err := openToken(ctx, auth, tokenPath(cfgDir)) + if err != nil { + return setupToken(auth, tokenPath(cfgDir)) + } + // Check whether the token works by getting a label. + if _, err := svc.Users.Labels.Get("me", "INBOX").Context(ctx).Do(); err != nil { + return setupToken(auth, tokenPath(cfgDir)) + } + return nil +} + func openCredentials(path string) (*api.Authenticator, error) { cred, err := os.Open(path) if err != nil {