diff --git a/action/action.go b/action/action.go index d788970857..3367d29e79 100644 --- a/action/action.go +++ b/action/action.go @@ -54,7 +54,7 @@ func New(ctx context.Context, cfg *config.Config, sv semver.Version) (*Action, e } var err error - act.gpg, err = gpgcli.New(gpgcli.Config{ + act.gpg, err = gpgcli.New(ctx, gpgcli.Config{ Umask: umask(), Args: gpgOpts(), }) @@ -64,7 +64,7 @@ func New(ctx context.Context, cfg *config.Config, sv semver.Version) (*Action, e store, err := root.New(ctx, cfg, act.gpg) if err != nil { - return nil, err + return nil, exitError(ctx, ExitUnknown, err, "failed to init root store: %s", err) } act.Store = store diff --git a/action/audit.go b/action/audit.go index e8846fc4c2..ce3e3c3224 100644 --- a/action/audit.go +++ b/action/audit.go @@ -31,7 +31,7 @@ type auditedSecret struct { func (s *Action) Audit(ctx context.Context, c *cli.Context) error { t, err := s.Store.Tree() if err != nil { - return s.exitError(ctx, ExitList, err, "failed to get store tree: %s", err) + return exitError(ctx, ExitList, err, "failed to get store tree: %s", err) } list := t.List(0) @@ -111,7 +111,7 @@ func (s *Action) Audit(ctx context.Context, c *cli.Context) error { if foundWeakPasswords || foundDuplicates || foundErrors { _ = notify.Notify("gopass - audit", "Finished. Found weak passwords and/or duplicates") - return s.exitError(ctx, ExitAudit, nil, "found weak passwords or duplicates") + return exitError(ctx, ExitAudit, nil, "found weak passwords or duplicates") } _ = notify.Notify("gopass - audit", "Finished. No weak passwords or duplicates found!") diff --git a/action/binary.go b/action/binary.go index 7d9bac9534..1c037e38b9 100644 --- a/action/binary.go +++ b/action/binary.go @@ -29,7 +29,7 @@ const ( func (s *Action) BinaryCat(ctx context.Context, c *cli.Context) error { name := c.Args().First() if name == "" { - return s.exitError(ctx, ExitNoName, nil, "need a name") + return exitError(ctx, ExitNoName, nil, "need a name") } if !strings.HasSuffix(name, BinarySuffix) { name += BinarySuffix @@ -38,7 +38,7 @@ func (s *Action) BinaryCat(ctx context.Context, c *cli.Context) error { // handle pipe to stdin info, err := os.Stdin.Stat() if err != nil { - return s.exitError(ctx, ExitIO, err, "failed to stat stdin: %s", err) + return exitError(ctx, ExitIO, err, "failed to stat stdin: %s", err) } // if content is piped to stdin, read and save it @@ -46,7 +46,7 @@ func (s *Action) BinaryCat(ctx context.Context, c *cli.Context) error { content := &bytes.Buffer{} if written, err := io.Copy(content, os.Stdin); err != nil { - return s.exitError(ctx, ExitIO, err, "Failed to copy after %d bytes: %s", written, err) + return exitError(ctx, ExitIO, err, "Failed to copy after %d bytes: %s", written, err) } return s.Store.Set( @@ -58,7 +58,7 @@ func (s *Action) BinaryCat(ctx context.Context, c *cli.Context) error { buf, err := s.binaryGet(ctx, name) if err != nil { - return s.exitError(ctx, ExitDecrypt, err, "failed to read secret: %s", err) + return exitError(ctx, ExitDecrypt, err, "failed to read secret: %s", err) } color.Yellow(string(buf)) return nil @@ -68,7 +68,7 @@ func (s *Action) BinaryCat(ctx context.Context, c *cli.Context) error { func (s *Action) BinarySum(ctx context.Context, c *cli.Context) error { name := c.Args().First() if name == "" { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s binary sha256 name", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s binary sha256 name", s.Name) } if !strings.HasSuffix(name, BinarySuffix) { name += BinarySuffix @@ -76,7 +76,7 @@ func (s *Action) BinarySum(ctx context.Context, c *cli.Context) error { buf, err := s.binaryGet(ctx, name) if err != nil { - return s.exitError(ctx, ExitDecrypt, err, "failed to read secret: %s", err) + return exitError(ctx, ExitDecrypt, err, "failed to read secret: %s", err) } h := sha256.New() @@ -93,7 +93,7 @@ func (s *Action) BinaryCopy(ctx context.Context, c *cli.Context) error { to := c.Args().Get(1) if err := s.binaryCopy(ctx, from, to, false); err != nil { - return s.exitError(ctx, ExitUnknown, err, "%s", err) + return exitError(ctx, ExitUnknown, err, "%s", err) } return nil } @@ -106,7 +106,7 @@ func (s *Action) BinaryMove(ctx context.Context, c *cli.Context) error { to := c.Args().Get(1) if err := s.binaryCopy(ctx, from, to, true); err != nil { - return s.exitError(ctx, ExitUnknown, err, "%s", err) + return exitError(ctx, ExitUnknown, err, "%s", err) } return nil } diff --git a/action/clipboard.go b/action/clipboard.go index 0ae289f9ad..0fd276f74e 100644 --- a/action/clipboard.go +++ b/action/clipboard.go @@ -2,7 +2,6 @@ package action import ( "context" - "fmt" "github.com/atotto/clipboard" "github.com/fatih/color" @@ -25,6 +24,6 @@ func (s *Action) copyToClipboard(ctx context.Context, name string, content []byt return errors.Wrapf(err, "failed to clear clipboard") } - fmt.Printf("Copied %s to clipboard. Will clear in %d seconds.\n", color.YellowString(name), ctxutil.GetClipTimeout(ctx)) + out.Print(ctx, "Copied %s to clipboard. Will clear in %d seconds.\n", color.YellowString(name), ctxutil.GetClipTimeout(ctx)) return nil } diff --git a/action/clone.go b/action/clone.go index ec543cea50..e593361241 100644 --- a/action/clone.go +++ b/action/clone.go @@ -17,7 +17,7 @@ import ( // Clone will fetch and mount a new password store from a git repo func (s *Action) Clone(ctx context.Context, c *cli.Context) error { if len(c.Args()) < 1 { - return errors.Errorf("Usage: %s clone repo [mount]", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s clone repo [mount]", s.Name) } repo := c.Args()[0] @@ -36,28 +36,28 @@ func (s *Action) clone(ctx context.Context, repo, mount, path string) error { path = config.PwStoreDir(mount) } if mount == "" && s.Store.Initialized() { - return s.exitError(ctx, ExitAlreadyInitialized, nil, "Can not clone %s to the root store, as this store is already initialized. Please try cloning to a submount: `%s clone %s sub`", repo, s.Name, repo) + return exitError(ctx, ExitAlreadyInitialized, nil, "Can not clone %s to the root store, as this store is already initialized. Please try cloning to a submount: `%s clone %s sub`", repo, s.Name, repo) } // clone repo if err := gitClone(ctx, repo, path); err != nil { - return s.exitError(ctx, ExitGit, err, "failed to clone repo '%s' to '%s'", repo, path) + return exitError(ctx, ExitGit, err, "failed to clone repo '%s' to '%s'", repo, path) } // add mount if mount != "" { if !s.Store.Initialized() { - return s.exitError(ctx, ExitNotInitialized, nil, "Root-Store is not initialized. Clone or init root store first") + return exitError(ctx, ExitNotInitialized, nil, "Root-Store is not initialized. Clone or init root store first") } if err := s.Store.AddMount(ctx, mount, path); err != nil { - return s.exitError(ctx, ExitMount, err, "Failed to add mount: %s", err) + return exitError(ctx, ExitMount, err, "Failed to add mount: %s", err) } fmt.Printf("Mounted password store %s at mount point `%s` ...\n", path, mount) } // save new mount in config file if err := s.cfg.Save(); err != nil { - return s.exitError(ctx, ExitIO, err, "Failed to update config: %s", err) + return exitError(ctx, ExitIO, err, "Failed to update config: %s", err) } // try to init git config @@ -73,14 +73,14 @@ func (s *Action) clone(ctx context.Context, repo, mount, path string) error { var err error userName, err = s.askForString(ctx, color.CyanString("Please enter a user name for password store git config"), userName) if err != nil { - return errors.Wrapf(err, "failed to ask for user input") + return exitError(ctx, ExitIO, err, "Failed to read user input: %s", err) } } if userEmail == "" { var err error userEmail, err = s.askForString(ctx, color.CyanString("Please enter an email address for password store git config"), userEmail) if err != nil { - return errors.Wrapf(err, "failed to ask for user input") + return exitError(ctx, ExitIO, err, "Failed to read user input: %s", err) } } if err := s.Store.GitInitConfig(ctx, mount, sk, userName, userEmail); err != nil { diff --git a/action/config.go b/action/config.go index 788d17383e..fda0ffdc85 100644 --- a/action/config.go +++ b/action/config.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" + "github.com/justwatchcom/gopass/utils/out" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -12,40 +13,40 @@ import ( // Config handles changes to the gopass configuration func (s *Action) Config(ctx context.Context, c *cli.Context) error { if len(c.Args()) < 1 { - if err := s.printConfigValues(""); err != nil { - return s.exitError(ctx, ExitUnknown, err, "Error printing config") + if err := s.printConfigValues(ctx, ""); err != nil { + return exitError(ctx, ExitUnknown, err, "Error printing config") } return nil } if len(c.Args()) == 1 { - if err := s.printConfigValues("", c.Args()[0]); err != nil { - return s.exitError(ctx, ExitUnknown, err, "Error printing config value") + if err := s.printConfigValues(ctx, "", c.Args()[0]); err != nil { + return exitError(ctx, ExitUnknown, err, "Error printing config value") } return nil } if len(c.Args()) > 2 { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s config key value", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s config key value", s.Name) } if err := s.setConfigValue(ctx, c.String("store"), c.Args()[0], c.Args()[1]); err != nil { - return s.exitError(ctx, ExitUnknown, err, "Error setting config value") + return exitError(ctx, ExitUnknown, err, "Error setting config value") } return nil } -func (s *Action) printConfigValues(store string, needles ...string) error { +func (s *Action) printConfigValues(ctx context.Context, store string, needles ...string) error { prefix := "" if len(needles) < 1 { - fmt.Printf("root store config:\n") + out.Print(ctx, "root store config:\n") prefix = " " } m := s.cfg.Root.ConfigMap() if store == "" { for _, k := range filter(m, needles) { - fmt.Printf("%s%s: %s\n", prefix, k, m[k]) + out.Print(ctx, "%s%s: %s\n", prefix, k, m[k]) } } for mp, sc := range s.cfg.Mounts { @@ -53,7 +54,7 @@ func (s *Action) printConfigValues(store string, needles ...string) error { continue } if len(needles) < 1 { - fmt.Printf("mount '%s' config:\n", mp) + out.Print(ctx, "mount '%s' config:\n", mp) mp = " " } else { mp += "/" @@ -61,7 +62,7 @@ func (s *Action) printConfigValues(store string, needles ...string) error { sm := sc.ConfigMap() for _, k := range filter(sm, needles) { if sm[k] != m[k] || store != "" { - fmt.Printf("%s%s: %s\n", mp, k, sm[k]) + out.Print(ctx, "%s%s: %s\n", mp, k, sm[k]) } } } @@ -96,7 +97,7 @@ func (s *Action) setConfigValue(ctx context.Context, store, key, value string) e if err := s.cfg.SetConfigValue(store, key, value); err != nil { return errors.Wrapf(err, "failed to set config value '%s'", key) } - return s.printConfigValues(store, key) + return s.printConfigValues(ctx, store, key) } // ConfigComplete will print the list of valid config keys diff --git a/action/copy.go b/action/copy.go index 0e8e4493d5..f30cbdf484 100644 --- a/action/copy.go +++ b/action/copy.go @@ -12,24 +12,24 @@ func (s *Action) Copy(ctx context.Context, c *cli.Context) error { force := c.Bool("force") if len(c.Args()) != 2 { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s cp old-path new-path", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s cp old-path new-path", s.Name) } from := c.Args()[0] to := c.Args()[1] if !s.Store.Exists(ctx, from) { - return s.exitError(ctx, ExitNotFound, nil, "%s does not exist", from) + return exitError(ctx, ExitNotFound, nil, "%s does not exist", from) } if !force { if s.Store.Exists(ctx, to) && !s.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) { - return s.exitError(ctx, ExitAborted, nil, "not overwriting your current secret") + return exitError(ctx, ExitAborted, nil, "not overwriting your current secret") } } if err := s.Store.Copy(ctx, from, to); err != nil { - return s.exitError(ctx, ExitIO, err, "failed to copy from '%s' to '%s'", from, to) + return exitError(ctx, ExitIO, err, "failed to copy from '%s' to '%s'", from, to) } return nil diff --git a/action/create.go b/action/create.go index efc34f5a25..42d7459f5e 100644 --- a/action/create.go +++ b/action/create.go @@ -43,7 +43,7 @@ func (s *Action) Create(ctx context.Context, c *cli.Context) error { return s.createGeneric(ctx, c) } default: - return s.exitError(ctx, ExitAborted, nil, "user aborted") + return exitError(ctx, ExitAborted, nil, "user aborted") } return nil } @@ -115,7 +115,7 @@ func (s *Action) createWebsite(ctx context.Context, c *cli.Context) error { _ = sec.SetValue("username", username) _ = sec.SetValue("comment", comment) if err := s.Store.Set(sub.WithReason(ctx, "Created new entry"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) } if genPw { fmt.Printf( @@ -188,7 +188,7 @@ func (s *Action) createPIN(ctx context.Context, c *cli.Context) error { _ = sec.SetValue("application", application) _ = sec.SetValue("comment", comment) if err := s.Store.Set(sub.WithReason(ctx, "Created new entry"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) } if genPw { fmt.Printf( @@ -256,7 +256,7 @@ func (s *Action) createAWS(ctx context.Context, c *cli.Context) error { _ = sec.SetValue("accesskey", accesskey) _ = sec.SetValue("region", region) if err := s.Store.Set(sub.WithReason(ctx, "Created new entry"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) } return nil } @@ -319,7 +319,7 @@ func (s *Action) createGCP(ctx context.Context, c *cli.Context) error { } sec := secret.New("", string(buf)) if err := s.Store.Set(sub.WithReason(ctx, "Created new entry"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) } return nil } @@ -409,7 +409,7 @@ func (s *Action) createGeneric(ctx context.Context, c *cli.Context) error { _ = sec.SetValue(key, val) } if err := s.Store.Set(sub.WithReason(ctx, "Created new entry"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) } if genPw { fmt.Printf( diff --git a/action/delete.go b/action/delete.go index 3b6bd348bc..c4decdf700 100644 --- a/action/delete.go +++ b/action/delete.go @@ -16,7 +16,7 @@ func (s *Action) Delete(ctx context.Context, c *cli.Context) error { name := c.Args().First() if name == "" { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s rm name", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s rm name", s.Name) } key := c.Args().Get(1) @@ -33,7 +33,7 @@ func (s *Action) Delete(ctx context.Context, c *cli.Context) error { if recursive { if err := s.Store.Prune(ctx, name); err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to prune '%s': %s", name, err) + return exitError(ctx, ExitUnknown, err, "failed to prune '%s': %s", name, err) } return nil } @@ -46,19 +46,19 @@ func (s *Action) Delete(ctx context.Context, c *cli.Context) error { if key != "" { sec, err := s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitIO, err, "Can not delete key '%s' from '%s': %s", key, name, err) + return exitError(ctx, ExitIO, err, "Can not delete key '%s' from '%s': %s", key, name, err) } if err := sec.DeleteKey(key); err != nil { - return s.exitError(ctx, ExitIO, err, "Can not delete key '%s' from '%s': %s", key, name, err) + return exitError(ctx, ExitIO, err, "Can not delete key '%s' from '%s': %s", key, name, err) } if err := s.Store.Set(sub.WithReason(ctx, "Updated Key in YAML"), name, sec); err != nil { - return s.exitError(ctx, ExitIO, err, "Can not delete key '%s' from '%s': %s", key, name, err) + return exitError(ctx, ExitIO, err, "Can not delete key '%s' from '%s': %s", key, name, err) } return nil } if err := s.Store.Delete(ctx, name); err != nil { - return s.exitError(ctx, ExitIO, err, "Can not delete '%s': %s", name, err) + return exitError(ctx, ExitIO, err, "Can not delete '%s': %s", name, err) } return nil } diff --git a/action/edit.go b/action/edit.go index 64052add06..ec9fbffa43 100644 --- a/action/edit.go +++ b/action/edit.go @@ -24,19 +24,21 @@ import ( func (s *Action) Edit(ctx context.Context, c *cli.Context) error { name := c.Args().First() if name == "" { - return errors.Errorf("provide a secret name") + return exitError(ctx, ExitUsage, nil, "Usage: %s edit secret", s.Name) } + editor := getEditor(c) + var content []byte var changed bool if s.Store.Exists(ctx, name) { sec, err := s.Store.Get(ctx, name) if err != nil { - return errors.Errorf("failed to decrypt %s: %v", name, err) + return exitError(ctx, ExitDecrypt, err, "failed to decrypt %s: %s", name, err) } content, err = sec.Bytes() if err != nil { - return errors.Errorf("failed to decode %s: %v", name, err) + return exitError(ctx, ExitDecrypt, err, "failed to decode %s: %s", name, err) } } else if tmpl, found := s.Store.LookupTemplate(ctx, name); found { changed = true @@ -49,9 +51,9 @@ func (s *Action) Edit(ctx context.Context, c *cli.Context) error { } } - nContent, err := s.editor(ctx, content) + nContent, err := s.editor(ctx, editor, content) if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to invoke editor: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to invoke editor: %s", err) } // If content is equal, nothing changed, exiting @@ -68,12 +70,13 @@ func (s *Action) Edit(ctx context.Context, c *cli.Context) error { printAuditResult(ctx, pw) } - return s.Store.Set(sub.WithReason(ctx, fmt.Sprintf("Edited with %s", getEditor())), name, nSec) + if err := s.Store.Set(sub.WithReason(ctx, fmt.Sprintf("Edited with %s", editor)), name, nSec); err != nil { + return exitError(ctx, ExitEncrypt, err, "failed to encrypt secret %s: %s", name, err) + } + return nil } -func (s *Action) editor(ctx context.Context, content []byte) ([]byte, error) { - editor := getEditor() - +func (s *Action) editor(ctx context.Context, editor string, content []byte) ([]byte, error) { tmpfile, err := fsutil.TempFile(ctx, "gopass-edit") if err != nil { return []byte{}, errors.Errorf("failed to create tmpfile %s: %s", editor, err) diff --git a/action/edit_linux.go b/action/edit_linux.go index 4422684bc2..c790ba81fd 100644 --- a/action/edit_linux.go +++ b/action/edit_linux.go @@ -5,9 +5,14 @@ package action import ( "os" "os/exec" + + "github.com/urfave/cli" ) -func getEditor() string { +func getEditor(c *cli.Context) string { + if ed := c.String("editor"); ed != "" { + return ed + } if ed := os.Getenv("EDITOR"); ed != "" { return ed } diff --git a/action/edit_others.go b/action/edit_others.go index f3433e4a5f..fa93963ef8 100644 --- a/action/edit_others.go +++ b/action/edit_others.go @@ -2,9 +2,16 @@ package action -import "os" +import ( + "os" -func getEditor() string { + "github.com/urfave/cli" +) + +func getEditor(c *cli.Context) string { + if ed := c.String("editor"); ed != "" { + return ed + } if ed := os.Getenv("EDITOR"); ed != "" { return ed } diff --git a/action/edit_windows.go b/action/edit_windows.go index 429eaeb1c7..20363a60fa 100644 --- a/action/edit_windows.go +++ b/action/edit_windows.go @@ -2,9 +2,16 @@ package action -import "os" +import ( + "os" -func getEditor() string { + "github.com/urfave/cli" +) + +func getEditor(c *cli.Context) string { + if ed := c.String("editor"); ed != "" { + return ed + } if ed := os.Getenv("EDITOR"); ed != "" { return ed } diff --git a/action/errors.go b/action/errors.go index 6f7842f1d2..e7487a9af9 100644 --- a/action/errors.go +++ b/action/errors.go @@ -53,7 +53,7 @@ const ( ExitGPG ) -func (s *Action) exitError(ctx context.Context, exitCode int, err error, format string, args ...interface{}) error { +func exitError(ctx context.Context, exitCode int, err error, format string, args ...interface{}) error { if err != nil { out.Debug(ctx, "Stacktrace: %+v", err) } diff --git a/action/find.go b/action/find.go index c19ef39508..ffcbb09805 100644 --- a/action/find.go +++ b/action/find.go @@ -17,12 +17,12 @@ func (s *Action) Find(ctx context.Context, c *cli.Context) error { ctx = WithClip(ctx, c.Bool("clip")) if !c.Args().Present() { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s find arg", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s find arg", s.Name) } l, err := s.Store.List(0) if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list store: %s", err) + return exitError(ctx, ExitList, err, "failed to list store: %s", err) } needle := strings.ToLower(c.Args().First()) @@ -73,6 +73,6 @@ func (s *Action) Find(ctx context.Context, c *cli.Context) error { } return s.show(ctx, c, needle, "", true) default: - return s.exitError(ctx, ExitAborted, nil, "user aborted") + return exitError(ctx, ExitAborted, nil, "user aborted") } } diff --git a/action/fix.go b/action/fix.go index 1f27e3e594..1d5cb38b2f 100644 --- a/action/fix.go +++ b/action/fix.go @@ -23,7 +23,7 @@ func (s *Action) Fix(ctx context.Context, c *cli.Context) error { t, err := s.Store.Tree() if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list store: %s", err) + return exitError(ctx, ExitList, err, "failed to list store: %s", err) } pwList := t.List(0) diff --git a/action/fsck.go b/action/fsck.go index afe566b011..d29a3c946a 100644 --- a/action/fsck.go +++ b/action/fsck.go @@ -23,7 +23,7 @@ func (s *Action) Fsck(ctx context.Context, c *cli.Context) error { // make sure config is in the right place // we may have loaded it from one of the fallback locations if err := s.cfg.Save(); err != nil { - return s.exitError(ctx, ExitConfig, err, "failed to save config: %s", err) + return exitError(ctx, ExitConfig, err, "failed to save config: %s", err) } // clean up any previous config locations oldCfg := filepath.Join(config.Homedir(), ".gopass.yml") @@ -34,7 +34,7 @@ func (s *Action) Fsck(ctx context.Context, c *cli.Context) error { } if _, err := s.Store.Fsck(ctx, ""); err != nil { - return s.exitError(ctx, ExitFsck, err, "fsck found errors") + return exitError(ctx, ExitFsck, err, "fsck found errors") } return nil } diff --git a/action/generate.go b/action/generate.go index 02b186935c..352d757506 100644 --- a/action/generate.go +++ b/action/generate.go @@ -58,13 +58,13 @@ func (s *Action) Generate(ctx context.Context, c *cli.Context) error { var err error name, err = s.askForString(ctx, "Which name do you want to use?", "") if err != nil || name == "" { - return s.exitError(ctx, ExitNoName, err, "please provide a password name") + return exitError(ctx, ExitNoName, err, "please provide a password name") } } if !force { // don't check if it's force anyway if s.Store.Exists(ctx, name) && key == "" && !s.AskForConfirmation(ctx, fmt.Sprintf("An entry already exists for %s. Overwrite the current password?", name)) { - return s.exitError(ctx, ExitAborted, nil, "user aborted. not overwriting your current password") + return exitError(ctx, ExitAborted, nil, "user aborted. not overwriting your current password") } } @@ -78,19 +78,19 @@ func (s *Action) Generate(ctx context.Context, c *cli.Context) error { } iv, err := s.askForInt(ctx, question, candidateLength) if err != nil { - return s.exitError(ctx, ExitUsage, err, "password length must be a number") + return exitError(ctx, ExitUsage, err, "password length must be a number") } pwlen = iv } else { iv, err := strconv.Atoi(length) if err != nil { - return s.exitError(ctx, ExitUsage, err, "password length must be a number") + return exitError(ctx, ExitUsage, err, "password length must be a number") } pwlen = iv } if pwlen < 1 { - return s.exitError(ctx, ExitUsage, nil, "password length must not be zero") + return exitError(ctx, ExitUsage, nil, "password length must not be zero") } var password string @@ -108,32 +108,32 @@ func (s *Action) Generate(ctx context.Context, c *cli.Context) error { if key != "" { sec, err := s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } if err := sec.SetValue(key, string(password)); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } if err := s.Store.Set(sub.WithReason(ctx, "Generated password for YAML key"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } } else if s.Store.Exists(ctx, name) { sec, err := s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } sec.SetPassword(password) if err := s.Store.Set(sub.WithReason(ctx, "Generated password for YAML key"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } } else { if err := s.Store.Set(sub.WithReason(ctx, "Generated Password"), name, secret.New(string(password), "")); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to create '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to create '%s': %s", name, err) } } if (edit || ctxutil.IsAskForMore(ctx)) && s.AskForConfirmation(ctx, fmt.Sprintf("Do you want to add more data for %s?", name)) { if err := s.Edit(ctx, c); err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to edit '%s': %s", name, err) + return exitError(ctx, ExitUnknown, err, "failed to edit '%s': %s", name, err) } } @@ -148,5 +148,8 @@ func (s *Action) Generate(ctx context.Context, c *cli.Context) error { return nil } - return s.copyToClipboard(ctx, name, []byte(password)) + if err := s.copyToClipboard(ctx, name, []byte(password)); err != nil { + return exitError(ctx, ExitIO, err, "failed to copy to clipboard: %s", err) + } + return nil } diff --git a/action/git.go b/action/git.go index 15acf15aa0..269d641816 100644 --- a/action/git.go +++ b/action/git.go @@ -20,7 +20,7 @@ func (s *Action) Git(ctx context.Context, c *cli.Context) error { force := c.Bool("force") if err := s.Store.Git(ctx, store, recurse, force, c.Args()...); err != nil { - return s.exitError(ctx, ExitGit, err, "git operation failed: %s", err) + return exitError(ctx, ExitGit, err, "git operation failed: %s", err) } return nil } @@ -31,7 +31,7 @@ func (s *Action) GitInit(ctx context.Context, c *cli.Context) error { sk := c.String("sign-key") if err := s.gitInit(ctx, store, sk); err != nil { - return s.exitError(ctx, ExitGit, err, "failed to initialize git: %s", err) + return exitError(ctx, ExitGit, err, "failed to initialize git: %s", err) } return nil } diff --git a/action/grep.go b/action/grep.go index 5785412bdb..64484dc036 100644 --- a/action/grep.go +++ b/action/grep.go @@ -13,14 +13,14 @@ import ( // Grep searches a string inside the content of all files func (s *Action) Grep(ctx context.Context, c *cli.Context) error { if !c.Args().Present() { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s grep arg", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s grep arg", s.Name) } search := c.Args().First() l, err := s.Store.List(0) if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list store: %s", err) + return exitError(ctx, ExitList, err, "failed to list store: %s", err) } for _, v := range l { diff --git a/action/hibp.go b/action/hibp.go index 789c60fa78..57dfee3062 100644 --- a/action/hibp.go +++ b/action/hibp.go @@ -36,7 +36,7 @@ func (s *Action) HIBP(ctx context.Context, c *cli.Context) error { // a very efficient stream compare in O(n) t, err := s.Store.Tree() if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list store: %s", err) + return exitError(ctx, ExitList, err, "failed to list store: %s", err) } pwList := t.List(0) @@ -54,7 +54,7 @@ func (s *Action) HIBP(ctx context.Context, c *cli.Context) error { // check for context cancelation select { case <-ctx.Done(): - return s.exitError(ctx, ExitAborted, nil, "user aborted") + return exitError(ctx, ExitAborted, nil, "user aborted") default: } @@ -115,7 +115,7 @@ func (s *Action) HIBP(ctx context.Context, c *cli.Context) error { out.Red(ctx, "\t- %s", m) } out.Cyan(ctx, "The passwords in the listed secrets were included in public leaks in the past. This means they are likely included in many word-list attacks and provide only very little security. Strongly consider changing those passwords!") - return s.exitError(ctx, ExitAudit, nil, "weak passwords found") + return exitError(ctx, ExitAudit, nil, "weak passwords found") } func (s *Action) findHIBPMatches(ctx context.Context, fn string, shaSums map[string]string, sortedShaSums []string, matches chan<- string, done chan<- struct{}) { diff --git a/action/init.go b/action/init.go index a1f3560003..3b6a98113d 100644 --- a/action/init.go +++ b/action/init.go @@ -19,7 +19,7 @@ import ( // prepared. func (s *Action) Initialized(ctx context.Context, c *cli.Context) error { if s.gpg.Binary() == "" { - return s.exitError(ctx, ExitGPG, nil, "gpg not found but required") + return exitError(ctx, ExitGPG, nil, "gpg not found but required") } if s.gpg.Version(ctx).LT(semver.Version{Major: 2, Minor: 0, Patch: 0}) { out.Red(ctx, "Warning: Using GPG 1.x. Using GPG 2.0 or later is highly recommended") @@ -27,10 +27,13 @@ func (s *Action) Initialized(ctx context.Context, c *cli.Context) error { if !s.Store.Initialized() { if ctxutil.IsInteractive(ctx) { if ok, err := s.askForBool(ctx, "It seems you are new to gopass. Do you want to run the onboarding wizard?", true); err == nil && ok { - return s.InitOnboarding(ctx, c) + if err := s.InitOnboarding(ctx, c); err != nil { + return exitError(ctx, ExitUnknown, err, "failed to run onboarding wizard: %s", err) + } + return nil } } - return s.exitError(ctx, ExitNotInitialized, nil, "password-store is not initialized. Try '%s init'", s.Name) + return exitError(ctx, ExitNotInitialized, nil, "password-store is not initialized. Try '%s init'", s.Name) } return nil } @@ -45,7 +48,7 @@ func (s *Action) Init(ctx context.Context, c *cli.Context) error { out.Cyan(ctx, "Initializing a new password store ...") if err := s.init(ctx, alias, path, nogit, c.Args()...); err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to initialized store: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to initialized store: %s", err) } return nil } @@ -99,7 +102,7 @@ func (s *Action) init(ctx context.Context, alias, path string, nogit bool, keys // write config if err := s.cfg.Save(); err != nil { - return s.exitError(ctx, ExitConfig, err, "failed to write config: %s", err) + return exitError(ctx, ExitConfig, err, "failed to write config: %s", err) } return nil diff --git a/action/insert.go b/action/insert.go index 5ed431d5e6..390c25d363 100644 --- a/action/insert.go +++ b/action/insert.go @@ -28,7 +28,7 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { name := c.Args().Get(0) if name == "" { - return s.exitError(ctx, ExitNoName, nil, "Usage: %s insert name", s.Name) + return exitError(ctx, ExitNoName, nil, "Usage: %s insert name", s.Name) } key := c.Args().Get(1) @@ -40,7 +40,7 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { buf := &bytes.Buffer{} if written, err := io.Copy(buf, os.Stdin); err != nil { - return s.exitError(ctx, ExitIO, err, "failed to copy after %d bytes: %s", written, err) + return exitError(ctx, ExitIO, err, "failed to copy after %d bytes: %s", written, err) } content = buf.Bytes() @@ -51,7 +51,7 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { if ctxutil.IsInteractive(ctx) { pw, err := s.askForString(ctx, name+":"+key, "") if err != nil { - return s.exitError(ctx, ExitIO, err, "failed to ask for user input: %s", err) + return exitError(ctx, ExitIO, err, "failed to ask for user input: %s", err) } content = []byte(pw) } @@ -61,14 +61,14 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { var err error sec, err = s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } } if err := sec.SetValue(key, string(content)); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } if err := s.Store.Set(sub.WithReason(ctx, "Inserted YAML value from STDIN"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err) } return nil } @@ -79,14 +79,14 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { out.Red(ctx, "WARNING: Invalid YAML: %s", err) } if err := s.Store.Set(sub.WithReason(ctx, "Read secret from STDIN"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to set '%s': %s", name, err) } return nil } if !force { // don't check if it's force anyway if s.Store.Exists(ctx, name) && !s.AskForConfirmation(ctx, fmt.Sprintf("An entry already exists for %s. Overwrite it?", name)) { - return s.exitError(ctx, ExitAborted, nil, "not overwriting your current secret") + return exitError(ctx, ExitAborted, nil, "not overwriting your current secret") } } @@ -97,23 +97,24 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { var err error sec, err := s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitDecrypt, err, "failed to decrypt existing secret: %s", err) + return exitError(ctx, ExitDecrypt, err, "failed to decrypt existing secret: %s", err) } buf, err = sec.Bytes() if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to encode secret: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to encode secret: %s", err) } } - content, err := s.editor(ctx, buf) + editor := getEditor(c) + content, err := s.editor(ctx, editor, buf) if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to start editor: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to start editor: %s", err) } sec, err := secret.Parse(content) if err != nil { out.Red(ctx, "WARNING: Invalid YAML: %s", err) } - if err := s.Store.Set(sub.WithReason(ctx, fmt.Sprintf("Inserted user supplied password with %s", getEditor())), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to store secret '%s': %s", name, err) + if err := s.Store.Set(sub.WithReason(ctx, fmt.Sprintf("Inserted user supplied password with %s", editor)), name, sec); err != nil { + return exitError(ctx, ExitEncrypt, err, "failed to store secret '%s': %s", name, err) } return nil } @@ -128,7 +129,7 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { pw, err := s.askForPassword(ctx, name, promptFn) if err != nil { - return s.exitError(ctx, ExitIO, err, "failed to ask for password: %s", err) + return exitError(ctx, ExitIO, err, "failed to ask for password: %s", err) } sec := &secret.Secret{} @@ -136,14 +137,14 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error { var err error sec, err = s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitDecrypt, err, "failed to decrypt existing secret: %s", err) + return exitError(ctx, ExitDecrypt, err, "failed to decrypt existing secret: %s", err) } } sec.SetPassword(pw) printAuditResult(ctx, sec.Password()) if err := s.Store.Set(sub.WithReason(ctx, "Inserted user supplied password"), name, sec); err != nil { - return s.exitError(ctx, ExitEncrypt, err, "failed to write secret '%s': %s", name, err) + return exitError(ctx, ExitEncrypt, err, "failed to write secret '%s': %s", name, err) } return nil } diff --git a/action/list.go b/action/list.go index 79f2a701ae..4ec2e725a2 100644 --- a/action/list.go +++ b/action/list.go @@ -27,7 +27,7 @@ func (s *Action) List(ctx context.Context, c *cli.Context) error { l, err := s.Store.Tree() if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list store: %s", err) + return exitError(ctx, ExitList, err, "failed to list store: %s", err) } var stdout io.Writer @@ -50,7 +50,7 @@ func (s *Action) List(ctx context.Context, c *cli.Context) error { fmt.Fprintln(stdout, l.Format(limit)) if buf != nil { if err := s.pager(ctx, buf); err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to invoke pager: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to invoke pager: %s", err) } } return nil @@ -88,7 +88,7 @@ func (s *Action) List(ctx context.Context, c *cli.Context) error { fmt.Fprintln(stdout, subtree.Format(limit)) if buf != nil { if err := s.pager(ctx, buf); err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to invoke pager: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to invoke pager: %s", err) } } return nil diff --git a/action/mount.go b/action/mount.go index daef7f19bd..8c38a5222e 100644 --- a/action/mount.go +++ b/action/mount.go @@ -16,7 +16,7 @@ import ( // MountRemove removes an existing mount func (s *Action) MountRemove(ctx context.Context, c *cli.Context) error { if len(c.Args()) != 1 { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s mount remove [alias]", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s mount remove [alias]", s.Name) } if err := s.Store.RemoveMount(ctx, c.Args()[0]); err != nil { @@ -24,7 +24,7 @@ func (s *Action) MountRemove(ctx context.Context, c *cli.Context) error { } if err := s.cfg.Save(); err != nil { - return s.exitError(ctx, ExitConfig, err, "failed to write config: %s", err) + return exitError(ctx, ExitConfig, err, "failed to write config: %s", err) } out.Green(ctx, "Password Store %s umounted", c.Args()[0]) @@ -66,7 +66,7 @@ func (s *Action) MountAdd(ctx context.Context, c *cli.Context) error { alias := c.Args().Get(0) localPath := c.Args().Get(1) if alias == "" { - return s.exitError(ctx, ExitUsage, nil, "usage: %s mount add [local path]", s.Name) + return exitError(ctx, ExitUsage, nil, "usage: %s mount add [local path]", s.Name) } if localPath == "" { @@ -83,11 +83,11 @@ func (s *Action) MountAdd(ctx context.Context, c *cli.Context) error { } if err := s.Store.AddMount(ctx, alias, localPath, keys...); err != nil { - return s.exitError(ctx, ExitMount, err, "failed to add mount '%s' to '%s': %s", alias, localPath, err) + return exitError(ctx, ExitMount, err, "failed to add mount '%s' to '%s': %s", alias, localPath, err) } if err := s.cfg.Save(); err != nil { - return s.exitError(ctx, ExitConfig, err, "failed to save config: %s", err) + return exitError(ctx, ExitConfig, err, "failed to save config: %s", err) } out.Green(ctx, "Mounted %s as %s", alias, localPath) diff --git a/action/move.go b/action/move.go index 7884d699a0..64321674e8 100644 --- a/action/move.go +++ b/action/move.go @@ -12,7 +12,7 @@ func (s *Action) Move(ctx context.Context, c *cli.Context) error { force := c.Bool("force") if len(c.Args()) != 2 { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s mv old-path new-path", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s mv old-path new-path", s.Name) } from := c.Args()[0] @@ -20,12 +20,12 @@ func (s *Action) Move(ctx context.Context, c *cli.Context) error { if !force { if s.Store.Exists(ctx, to) && !s.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) { - return s.exitError(ctx, ExitAborted, nil, "not overwriting your current secret") + return exitError(ctx, ExitAborted, nil, "not overwriting your current secret") } } if err := s.Store.Move(ctx, from, to); err != nil { - return s.exitError(ctx, ExitUnknown, err, "%s", err) + return exitError(ctx, ExitUnknown, err, "%s", err) } return nil diff --git a/action/otp.go b/action/otp.go index f398d6bda1..af2c2da6d2 100644 --- a/action/otp.go +++ b/action/otp.go @@ -23,12 +23,12 @@ const ( func (s *Action) OTP(ctx context.Context, c *cli.Context) error { name := c.Args().First() if name == "" { - return s.exitError(ctx, ExitUsage, nil, "usage: %s otp [name]", s.Name) + return exitError(ctx, ExitUsage, nil, "usage: %s otp [name]", s.Name) } sec, err := s.Store.Get(ctx, name) if err != nil { - return s.exitError(ctx, ExitDecrypt, err, "failed to get entry '%s': %s", name, err) + return exitError(ctx, ExitDecrypt, err, "failed to get entry '%s': %s", name, err) } otpURL := "" @@ -57,12 +57,12 @@ func (s *Action) OTP(ctx context.Context, c *cli.Context) error { } if err != nil { - return s.exitError(ctx, ExitUnknown, err, "No OTP entry found for %s", name) + return exitError(ctx, ExitUnknown, err, "No OTP entry found for %s", name) } } else { otp, label, err = twofactor.FromURL(otpURL) if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed get key from URL: %s", err) + return exitError(ctx, ExitUnknown, err, "failed get key from URL: %s", err) } } @@ -82,7 +82,7 @@ func (s *Action) OTP(ctx context.Context, c *cli.Context) error { if c.Bool("clip") { if err := s.copyToClipboard(ctx, fmt.Sprintf("token for %s", name), []byte(token)); err != nil { - return s.exitError(ctx, ExitIO, err, "failed to copy to clipboard: %s", err) + return exitError(ctx, ExitIO, err, "failed to copy to clipboard: %s", err) } } @@ -100,11 +100,11 @@ func (s *Action) OTP(ctx context.Context, c *cli.Context) error { err = errors.New("QR codes can only be generated for OATH OTPs") } if err != nil { - return s.exitError(ctx, ExitIO, err, "%s", err) + return exitError(ctx, ExitIO, err, "%s", err) } if err := ioutil.WriteFile(c.String("qr"), qr, 0600); err != nil { - return s.exitError(ctx, ExitIO, err, "failed to write QR code: %s", err) + return exitError(ctx, ExitIO, err, "failed to write QR code: %s", err) } } diff --git a/action/recipients.go b/action/recipients.go index 85b64977a2..447b281d71 100644 --- a/action/recipients.go +++ b/action/recipients.go @@ -36,7 +36,7 @@ func (s *Action) RecipientsPrint(ctx context.Context, c *cli.Context) error { tree, err := s.Store.RecipientsTree(ctx, true) if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list recipients: %s", err) + return exitError(ctx, ExitList, err, "failed to list recipients: %s", err) } fmt.Println(tree.Format(0)) @@ -93,7 +93,7 @@ func (s *Action) RecipientsAdd(ctx context.Context, c *cli.Context) error { case "show": recipients = []string{kl[sel].Fingerprint} default: - return s.exitError(ctx, ExitAborted, nil, "user aborted") + return exitError(ctx, ExitAborted, nil, "user aborted") } } } @@ -118,12 +118,12 @@ func (s *Action) RecipientsAdd(ctx context.Context, c *cli.Context) error { } if err := s.Store.AddRecipient(ctxutil.WithNoConfirm(ctx, true), store, keys[0].Fingerprint); err != nil { - return s.exitError(ctx, ExitRecipients, err, "failed to add recipient '%s': %s", r, err) + return exitError(ctx, ExitRecipients, err, "failed to add recipient '%s': %s", r, err) } added++ } if added < 1 { - return s.exitError(ctx, ExitUnknown, nil, "no key added") + return exitError(ctx, ExitUnknown, nil, "no key added") } out.Green(ctx, "\nAdded %d recipients", added) @@ -172,7 +172,7 @@ func (s *Action) RecipientsRemove(ctx context.Context, c *cli.Context) error { case "show": recipients = []string{ids[sel]} default: - return s.exitError(ctx, ExitAborted, nil, "user aborted") + return exitError(ctx, ExitAborted, nil, "user aborted") } } } @@ -188,7 +188,7 @@ func (s *Action) RecipientsRemove(ctx context.Context, c *cli.Context) error { } } if err := s.Store.RemoveRecipient(ctxutil.WithNoConfirm(ctx, true), store, strings.TrimPrefix(r, "0x")); err != nil { - return s.exitError(ctx, ExitRecipients, err, "failed to remove recipient '%s': %s", r, err) + return exitError(ctx, ExitRecipients, err, "failed to remove recipient '%s': %s", r, err) } fmt.Printf(removalWarning, r) removed++ diff --git a/action/show.go b/action/show.go index 817efa4b75..bfa712ffd1 100644 --- a/action/show.go +++ b/action/show.go @@ -26,14 +26,14 @@ func (s *Action) Show(ctx context.Context, c *cli.Context) error { ctx = WithPasswordOnly(ctx, c.Bool("password")) if err := s.show(ctx, c, name, key, true); err != nil { - return s.exitError(ctx, ExitDecrypt, err, "%s", err) + return exitError(ctx, ExitDecrypt, err, "%s", err) } return nil } func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, recurse bool) error { if name == "" { - return s.exitError(ctx, ExitUsage, nil, "Usage: %s show [name]", s.Name) + return exitError(ctx, ExitUsage, nil, "Usage: %s show [name]", s.Name) } if s.Store.IsDir(ctx, name) && !s.Store.Exists(ctx, name) { @@ -51,11 +51,11 @@ func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, rec sec, err := s.Store.Get(ctx, name) if err != nil { if err != store.ErrNotFound || !recurse || !ctxutil.IsTerminal(ctx) { - return s.exitError(ctx, ExitUnknown, err, "failed to retrieve secret '%s': %s", name, err) + return exitError(ctx, ExitUnknown, err, "failed to retrieve secret '%s': %s", name, err) } color.Yellow("Entry '%s' not found. Starting search...", name) if err := s.Find(ctx, c); err != nil { - return s.exitError(ctx, ExitNotFound, err, "%s", err) + return exitError(ctx, ExitNotFound, err, "%s", err) } os.Exit(ExitNotFound) } @@ -67,12 +67,12 @@ func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, rec val, err := sec.Value(key) if err != nil { if errors.Cause(err) == store.ErrYAMLValueUnsupported { - return s.exitError(ctx, ExitUnsupported, err, "Can not show nested key directly. Use 'gopass show %s'", name) + return exitError(ctx, ExitUnsupported, err, "Can not show nested key directly. Use 'gopass show %s'", name) } if errors.Cause(err) == store.ErrNotFound { - return s.exitError(ctx, ExitNotFound, err, "Secret '%s' not found", name) + return exitError(ctx, ExitNotFound, err, "Secret '%s' not found", name) } - return s.exitError(ctx, ExitUnknown, err, "failed to retrieve key '%s' from '%s': %s", key, name, err) + return exitError(ctx, ExitUnknown, err, "failed to retrieve key '%s' from '%s': %s", key, name, err) } if IsClip(ctx) { return s.copyToClipboard(ctx, name, []byte(val)) @@ -81,7 +81,7 @@ func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, rec case IsPrintQR(ctx): qr, err := qrcon.QRCode(sec.Password()) if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to encode '%s' as QR: %s", name, err) + return exitError(ctx, ExitUnknown, err, "failed to encode '%s' as QR: %s", name, err) } fmt.Println(qr) return nil @@ -94,12 +94,12 @@ func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, rec case ctxutil.IsShowSafeContent(ctx) && !IsForce(ctx): content = sec.Body() if content == "" { - return s.exitError(ctx, ExitNotFound, store.ErrNoBody, "no safe content to display, you can force display with show -f") + return exitError(ctx, ExitNotFound, store.ErrNoBody, "no safe content to display, you can force display with show -f") } default: buf, err := sec.Bytes() if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to encode secret: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to encode secret: %s", err) } content = string(buf) } diff --git a/action/templates.go b/action/templates.go index f851013cd6..c974a7c700 100644 --- a/action/templates.go +++ b/action/templates.go @@ -30,7 +30,7 @@ const ( func (s *Action) TemplatesPrint(ctx context.Context, c *cli.Context) error { tree, err := s.Store.TemplateTree() if err != nil { - return s.exitError(ctx, ExitList, err, "failed to list templates: %s", err) + return exitError(ctx, ExitList, err, "failed to list templates: %s", err) } fmt.Println(tree.Format(0)) return nil @@ -42,7 +42,7 @@ func (s *Action) TemplatePrint(ctx context.Context, c *cli.Context) error { content, err := s.Store.GetTemplate(ctx, name) if err != nil { - return s.exitError(ctx, ExitIO, err, "failed to retrieve template: %s", err) + return exitError(ctx, ExitIO, err, "failed to retrieve template: %s", err) } fmt.Println(string(content)) @@ -63,15 +63,16 @@ func (s *Action) TemplateEdit(ctx context.Context, c *cli.Context) error { var err error content, err = s.Store.GetTemplate(ctx, name) if err != nil { - return s.exitError(ctx, ExitIO, err, "failed to retrieve template: %s", err) + return exitError(ctx, ExitIO, err, "failed to retrieve template: %s", err) } } else { content = []byte(templateExample) } - nContent, err := s.editor(ctx, content) + editor := getEditor(c) + nContent, err := s.editor(ctx, editor, content) if err != nil { - return s.exitError(ctx, ExitUnknown, err, "failed to invoke editor: %s", err) + return exitError(ctx, ExitUnknown, err, "failed to invoke editor %s: %s", editor, err) } // If content is equal, nothing changed, exiting @@ -86,11 +87,11 @@ func (s *Action) TemplateEdit(ctx context.Context, c *cli.Context) error { func (s *Action) TemplateRemove(ctx context.Context, c *cli.Context) error { name := c.Args().First() if name == "" { - return s.exitError(ctx, ExitUsage, nil, "usage: %s templates remove [name]", s.Name) + return exitError(ctx, ExitUsage, nil, "usage: %s templates remove [name]", s.Name) } if !s.Store.HasTemplate(ctx, name) { - return s.exitError(ctx, ExitNotFound, nil, "template '%s' not found", name) + return exitError(ctx, ExitNotFound, nil, "template '%s' not found", name) } return s.Store.RemoveTemplate(ctx, name) diff --git a/action/unclip.go b/action/unclip.go index 8cc6ade835..aae7804016 100644 --- a/action/unclip.go +++ b/action/unclip.go @@ -22,7 +22,7 @@ func (s *Action) Unclip(ctx context.Context, c *cli.Context) error { cur, err := clipboard.ReadAll() if err != nil { - return s.exitError(ctx, ExitIO, err, "failed to read clipboard: %s", err) + return exitError(ctx, ExitIO, err, "failed to read clipboard: %s", err) } hash := fmt.Sprintf("%x", sha256.Sum256([]byte(cur))) @@ -33,16 +33,16 @@ func (s *Action) Unclip(ctx context.Context, c *cli.Context) error { if err := clipboard.WriteAll(""); err != nil { _ = notify.Notify("gopass - clipboard", "Failed to clear clipboard") - return s.exitError(ctx, ExitIO, err, "failed to write clipboard: %s", err) + return exitError(ctx, ExitIO, err, "failed to write clipboard: %s", err) } if err := s.clearClipboardHistory(ctx); err != nil { _ = notify.Notify("gopass - clipboard", "Failed to clear clipboard history") - return s.exitError(ctx, ExitIO, err, "failed to clear clipboard history: %s", err) + return exitError(ctx, ExitIO, err, "failed to clear clipboard history: %s", err) } if err := notify.Notify("gopass -clipboard", "Clipboard has been cleared"); err != nil { - return s.exitError(ctx, ExitIO, err, "failed to send unclip notification: %s", err) + return exitError(ctx, ExitIO, err, "failed to send unclip notification: %s", err) } return nil diff --git a/action/update.go b/action/update.go index 460406712e..f9c3c640a0 100644 --- a/action/update.go +++ b/action/update.go @@ -137,7 +137,7 @@ func (s *Action) tryDownload(ctx context.Context, dest, url string) error { return backoff.Retry(func() error { select { case <-ctx.Done(): - return backoff.Permanent(s.exitError(ctx, ExitAborted, nil, "user aborted")) + return backoff.Permanent(exitError(ctx, ExitAborted, nil, "user aborted")) default: } return s.download(ctx, dest, url) diff --git a/action/version.go b/action/version.go index 11c43f7af4..a9c68a4eaf 100644 --- a/action/version.go +++ b/action/version.go @@ -72,7 +72,7 @@ func (s *Action) Version(ctx context.Context, c *cli.Context) error { case <-time.After(2 * time.Second): out.Red(ctx, "Version check timed out") case <-ctx.Done(): - return s.exitError(ctx, ExitAborted, nil, "user aborted") + return exitError(ctx, ExitAborted, nil, "user aborted") } return nil diff --git a/backend/gpg/cli/gpg.go b/backend/gpg/cli/gpg.go index 9ff6be3c74..319e2abfed 100644 --- a/backend/gpg/cli/gpg.go +++ b/backend/gpg/cli/gpg.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/justwatchcom/gopass/backend/gpg" - "github.com/justwatchcom/gopass/utils/ctxutil" "github.com/justwatchcom/gopass/utils/out" "github.com/pkg/errors" ) @@ -46,7 +45,7 @@ type Config struct { } // New creates a new GPG wrapper -func New(cfg Config) (*GPG, error) { +func New(ctx context.Context, cfg Config) (*GPG, error) { // ensure created files don't have group or world perms set // this setting should be inherited by sub-processes umask(cfg.Umask) @@ -63,10 +62,6 @@ func New(cfg Config) (*GPG, error) { args: append(defaultArgs, cfg.Args...), } - ctx := context.Background() - if gdb := os.Getenv("GOPASS_DEBUG"); gdb == "true" { - ctx = ctxutil.WithDebug(ctx, true) - } if err := g.detectBinary(ctx, cfg.Binary); err != nil { return nil, err } diff --git a/main.go b/main.go index 5a910dec68..8ca67fb980 100644 --- a/main.go +++ b/main.go @@ -445,6 +445,12 @@ func main() { }, Aliases: []string{"set"}, BashComplete: action.Complete, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "editor, e", + Usage: "Use this editor binary", + }, + }, }, { Name: "find",