diff --git a/.gitignore b/.gitignore index 1dce362..a723c84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -cmd/gpass/gpass -cmd/gpass-gui/gpass-gui +cmd/gpass/passgo +cmd/gpass-gui/passgo-gui diff --git a/README.md b/README.md index 923cd0c..94808e1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# gpass +# passgo This repository includes Go code to interact with pass, the Unix password manager. Library code is provided to open a password store, list @@ -7,7 +7,7 @@ a simple passphrase input function, or, if gpg-agent is running (on MacOS), it will connect automatically to request your GPG passphrase. ```go -store, err := gpass.GetStore() +store, err := passgo.GetStore() if err != nil { ... } passlist := store.List() //storeDir := store.Dir @@ -15,7 +15,7 @@ for _,x := range passlist { if x.Pathname = "myPass" { p, err := store.Decrypt(x.Pathname) if err == nil { - gpass.Clip(p) // put on the clipboard + passgo.Clip(p) // put on the clipboard } else { log.Fatal("Cannot decrypt ", x.Pathname) } diff --git a/cmd/gpass-gui/log.go b/cmd/gpass-gui/log.go deleted file mode 100644 index 83d8756..0000000 --- a/cmd/gpass-gui/log.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - golog "log" - "os" -) - -type logLevelT int -const ( - Fatal logLevelT = 1 << iota - Error - Warn - Info - Debug - DebugGfx -) - -var loglevel = Fatal | Error | Warn | Info - -func log(level logLevelT, msg ...interface{}) { - if level & loglevel != 0 { - golog.Print(msg...) - } - if level & Fatal != 0 { - os.Exit(-1) - } -} - diff --git a/cmd/passgo-gui/log.go b/cmd/passgo-gui/log.go new file mode 100644 index 0000000..a728732 --- /dev/null +++ b/cmd/passgo-gui/log.go @@ -0,0 +1,28 @@ +package main + +import ( + golog "log" + "os" +) + +type logLevelT int + +const ( + Fatal logLevelT = 1 << iota + Error + Warn + Info + Debug + DebugGfx +) + +var loglevel = Fatal | Error | Warn | Info + +func log(level logLevelT, msg ...interface{}) { + if level&loglevel != 0 { + golog.Print(msg...) + } + if level&Fatal != 0 { + os.Exit(-1) + } +} diff --git a/cmd/gpass-gui/main.go b/cmd/passgo-gui/main.go similarity index 82% rename from cmd/gpass-gui/main.go rename to cmd/passgo-gui/main.go index ac1fee3..b16d13b 100644 --- a/cmd/gpass-gui/main.go +++ b/cmd/passgo-gui/main.go @@ -4,56 +4,55 @@ package main import ( //"fmt" - "path" - "strings" "image" "image/color" + "path" + "strings" "sync" "gioui.org/ui" "gioui.org/ui/app" + "gioui.org/ui/f32" + "gioui.org/ui/gesture" "gioui.org/ui/input" "gioui.org/ui/key" "gioui.org/ui/layout" - "gioui.org/ui/text" "gioui.org/ui/measure" - "gioui.org/ui/f32" "gioui.org/ui/paint" - "gioui.org/ui/gesture" "gioui.org/ui/pointer" + "gioui.org/ui/text" "github.com/fsnotify/fsnotify" "golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/sfnt" - - "git.wow.st/gmp/gpass" + "git.wow.st/gmp/passgo" ) func main() { var err error - store,err = gpass.GetStore() + store, err = passgo.GetStore() if err != nil { log(Fatal, err) } updated = make(chan struct{}) go Updater() - log(Info,"Staring event loop") + log(Info, "Staring event loop") go eventLoop() app.Main() - log(Info,"Event loop returned") + log(Info, "Event loop returned") } var ( - l []gpass.Pass - mux sync.Mutex - store *gpass.Store + l []passgo.Pass + mux sync.Mutex + store *passgo.Store updated chan struct{} ) func Updater() { update := func() { - ltmp,err := store.List() + ltmp, err := store.List() if err != nil { log(Fatal, err) } @@ -74,16 +73,16 @@ func Updater() { case <-watcher.Events: update() case e := <-watcher.Errors: - log(Info, "Watcher error: ",e) + log(Info, "Watcher error: ", e) } } } type Button struct { - Face text.Face - Label string - Click gesture.Click - Color color.RGBA + Face text.Face + Label string + Click gesture.Click + Color color.RGBA clicked bool } @@ -114,7 +113,6 @@ func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) { b.End() } - func (b *Button) Layout(c ui.Config, ops *ui.Ops, q input.Queue, cs layout.Constraints) layout.Dimensions { b.clicked = false for ev, ok := b.Click.Next(q); ok; ev, ok = b.Click.Next(q) { @@ -142,7 +140,7 @@ func (b *Button) Layout(c ui.Config, ops *ui.Ops, q input.Queue, cs layout.Const c1 := st.End(layoutRRect(b.Color, c, ops, st.Expand())) dims = st.Layout(c1, c2) return ins.End(dims) -} +} func (b *Button) Clicked() bool { return b.clicked @@ -165,23 +163,25 @@ func eventLoop() { passInput.SetText("passphrase") passSubmit := &Button{Face: face, Label: "submit"} lst := &layout.List{Axis: layout.Vertical} - passBtns := make([]*Button,0) + passBtns := make([]*Button, 0) _ = passInput _ = passSubmit - pathnames := make([]string,0) + pathnames := make([]string, 0) updateBtns := func() { passBtns = passBtns[:0] pathnames = pathnames[:0] mux.Lock() - for _,x := range l { + for _, x := range l { _, n := path.Split(x.Pathname) - s := strings.Repeat(" /",x.Level) + s := strings.Repeat(" /", x.Level) z := "" - if x.Dir { z = "/" } - passBtns = append(passBtns,&Button{ - Face:face, - Label:strings.Join([]string{s,n,z},""), + if x.Dir { + z = "/" + } + passBtns = append(passBtns, &Button{ + Face: face, + Label: strings.Join([]string{s, n, z}, ""), Color: color.RGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0}, }) pathnames = append(pathnames, x.Pathname) @@ -190,21 +190,20 @@ func eventLoop() { } updateBtns() - - for { - select { - case <-updated: - log(Info,"UPDATE") + for { + select { + case <-updated: + log(Info, "UPDATE") updateBtns() - w.Invalidate() - case e := <-w.Events(): + w.Invalidate() + case e := <-w.Events(): switch e := e.(type) { case app.DestroyEvent: return case app.UpdateEvent: c := &e.Config ops.Reset() - faces.Reset(c) + faces.Reset(c) var dims layout.Dimensions cs := layout.RigidConstraints(e.Size) cs = margin.Begin(c, ops, cs) @@ -219,15 +218,15 @@ func eventLoop() { lst.End(dims) if btn.Clicked() { // don't block UI thread on decryption attempt - log(Info,"Clicked ", btn.Label) + log(Info, "Clicked ", btn.Label) go func(name string) { //p,err := store.Decrypt(name, prompt) - p,err := store.Decrypt(name) + p, err := store.Decrypt(name) if err == nil { - gpass.Clip(p) + passgo.Clip(p) } else { - log(Info,"Can't decrypt ", name) - log(Info,err) + log(Info, "Can't decrypt ", name) + log(Info, err) } }(pathnames[lst.Index()]) } @@ -235,9 +234,8 @@ func eventLoop() { mux.Unlock() dims = lst.Layout() dims = margin.End(dims) - w.Update(ops) + w.Update(ops) } - } - } + } + } } - diff --git a/cmd/gpass/main.go b/cmd/passgo/main.go similarity index 55% rename from cmd/gpass/main.go rename to cmd/passgo/main.go index 7d08057..066e003 100644 --- a/cmd/gpass/main.go +++ b/cmd/passgo/main.go @@ -6,11 +6,11 @@ import ( "path" "strings" - "git.wow.st/gmp/gpass" + "git.wow.st/gmp/passgo" ) func usage() { - fmt.Println("gpass [-c|--clip] [name]") + fmt.Println("passgo [-c|--clip] [name]") os.Exit(-1) } @@ -18,53 +18,55 @@ type options struct { clip bool } -func parse(args []string) ([]string,options) { - ret := make([]string,0) +func parse(args []string) ([]string, options) { + ret := make([]string, 0) opts := options{} - for _,a := range args { + for _, a := range args { switch { - case a == "-c",a == "--clip": + case a == "-c", a == "--clip": opts.clip = true case a[0] == '-': usage() default: - ret = append(ret,a) + ret = append(ret, a) } } - return ret,opts + return ret, opts } func main() { - store,err := gpass.GetStore() + store, err := passgo.GetStore() if err != nil { fmt.Println(err) os.Exit(-1) } args := os.Args[1:] - args,opts := parse(args) + args, opts := parse(args) switch len(args) { case 0: - l,err := store.List() + l, err := store.List() if err != nil { fmt.Println(err) os.Exit(-1) } for _, x := range l { _, n := path.Split(x.Pathname) - s := strings.Repeat(" /",x.Level) + s := strings.Repeat(" /", x.Level) z := "" - if x.Dir { z = "/" } - fmt.Println(strings.Join([]string{s,n,z},"")) + if x.Dir { + z = "/" + } + fmt.Println(strings.Join([]string{s, n, z}, "")) } case 1: - p,err := store.Decrypt(args[0]) + p, err := store.Decrypt(args[0]) if err != nil { fmt.Println(err) os.Exit(-1) } if opts.clip { - gpass.Clip(p) + passgo.Clip(p) } else { fmt.Print(p) } @@ -72,4 +74,3 @@ func main() { usage() } } - diff --git a/impl_darwin.go b/impl_darwin.go index f730377..0570cba 100644 --- a/impl_darwin.go +++ b/impl_darwin.go @@ -1,11 +1,11 @@ //+build !android !linux -package gpass +package passgo import ( - "fmt" "bufio" "bytes" + "fmt" "os" "os/exec" "os/user" @@ -73,7 +73,7 @@ func setAgentInfo() { } s := fmt.Sprintf("%s:%d:1", filename, pid) fmt.Printf("GPG_AGENT_INFO = %s\n", s) - os.Setenv("GPG_AGENT_INFO",s) + os.Setenv("GPG_AGENT_INFO", s) // gpg-agent is running, so use GPGPrompt as password prompt ask = GPGPrompt @@ -90,4 +90,3 @@ func Clip(x string) { cmd.Stdin = b cmd.Run() } - diff --git a/main.go b/main.go index 7643750..660c266 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -package gpass +package passgo import ( "bufio" @@ -22,7 +22,7 @@ import ( var ( basename *regexp.Regexp - ask openpgp.PromptFunction + ask openpgp.PromptFunction ) func init() { @@ -30,19 +30,19 @@ func init() { } type Store struct { - Dir string + Dir string keyring openpgp.KeyRing } func GetStore() (*Store, error) { ret := &Store{} - u, err := user.Current() - if err != nil { - return ret, fmt.Errorf("Can't get current user.") - } - ret.Dir = path.Join(u.HomeDir,".password-store") + u, err := user.Current() + if err != nil { + return ret, fmt.Errorf("Can't get current user.") + } + ret.Dir = path.Join(u.HomeDir, ".password-store") - fd, err := os.Open(path.Join(u.HomeDir,".gnupg/secring.gpg")) + fd, err := os.Open(path.Join(u.HomeDir, ".gnupg/secring.gpg")) defer fd.Close() if err != nil { return ret, fmt.Errorf("Can't open keyring file") @@ -52,30 +52,30 @@ func GetStore() (*Store, error) { return ret, fmt.Errorf("Can't open gnupg keyring.") } ret.keyring = kr - return ret, nil + return ret, nil } type caseInsensitive []os.FileInfo -func (fs caseInsensitive) Len() int { return len(fs) } +func (fs caseInsensitive) Len() int { return len(fs) } func (fs caseInsensitive) Swap(i, j int) { fs[i], fs[j] = fs[j], fs[i] } func (fs caseInsensitive) Less(i, j int) bool { - return strings.ToLower(fs[i].Name()) < strings.ToLower(fs[j].Name()) + return strings.ToLower(fs[i].Name()) < strings.ToLower(fs[j].Name()) } type Pass struct { Pathname string - Level int - Dir bool + Level int + Dir bool } -func (s *Store) List() ([]Pass,error) { - return s.list(0,"") +func (s *Store) List() ([]Pass, error) { + return s.list(0, "") } -func (s *Store) list(level int, p string) ([]Pass,error) { - ret := make([]Pass,0) - dir := path.Join(s.Dir,p) +func (s *Store) list(level int, p string) ([]Pass, error) { + ret := make([]Pass, 0) + dir := path.Join(s.Dir, p) fd, err := os.Open(dir) defer fd.Close() if err != nil { @@ -87,25 +87,25 @@ func (s *Store) list(level int, p string) ([]Pass,error) { } sort.Sort(caseInsensitive(files)) for _, x := range files { - n := basename.ReplaceAllLiteralString(x.Name(),"") - entry := Pass{Pathname: path.Join(p,n),Level: level} + n := basename.ReplaceAllLiteralString(x.Name(), "") + entry := Pass{Pathname: path.Join(p, n), Level: level} if n[0] == '.' { continue } if x.IsDir() { entry.Dir = true - ret = append(ret,entry) - l,err := s.list(level+1,path.Join(p,x.Name())) + ret = append(ret, entry) + l, err := s.list(level+1, path.Join(p, x.Name())) if err != nil { - return nil,err + return nil, err } - ret = append(ret,l...) + ret = append(ret, l...) } else { - ret = append(ret,entry) + ret = append(ret, entry) } } - return ret,nil + return ret, nil } func AskPass(prompts ...func() []byte) openpgp.PromptFunction { @@ -124,7 +124,7 @@ func AskPass(prompts ...func() []byte) openpgp.PromptFunction { var err error var passphrase []byte dec := func(p *packet.PrivateKey) { - for i := 0; i<3; i++ { + for i := 0; i < 3; i++ { if err = p.Decrypt(passphrase); err == nil { break } @@ -135,7 +135,7 @@ func AskPass(prompts ...func() []byte) openpgp.PromptFunction { var ret openpgp.PromptFunction ret = func(keys []openpgp.Key, symmetric bool) ([]byte, error) { if !symmetric { - for _,k := range keys { + for _, k := range keys { if p := k.PrivateKey; p != nil && p.Encrypted { dec(p) } @@ -175,18 +175,18 @@ func GPGPrompt(keys []openpgp.Key, symmetric bool) ([]byte, error) { return nil, fmt.Errorf("Unable to find key") } -func (s *Store) Decrypt(name string, prompts ...func() []byte) (string,error) { +func (s *Store) Decrypt(name string, prompts ...func() []byte) (string, error) { if ask == nil { ask = AskPass(prompts...) } - file := path.Join(s.Dir, strings.Join([]string{name,".gpg"},"")) + file := path.Join(s.Dir, strings.Join([]string{name, ".gpg"}, "")) if _, err := os.Stat(file); os.IsNotExist(err) { - return "",fmt.Errorf("Not in password store.") + return "", fmt.Errorf("Not in password store.") } fd, err := os.Open(file) defer fd.Close() if err != nil { - return "",err + return "", err } var reader io.Reader unarmor, err := armor.Decode(fd) @@ -197,19 +197,18 @@ func (s *Store) Decrypt(name string, prompts ...func() []byte) (string,error) { fd, err = os.Open(file) defer fd.Close() if err != nil { - return "",err + return "", err } reader = fd } - md, err := openpgp.ReadMessage(reader,s.keyring,ask,nil) + md, err := openpgp.ReadMessage(reader, s.keyring, ask, nil) if err != nil { fmt.Println("Error reading message") - return "",err + return "", err } ra, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { - return "",err + return "", err } return string(ra), nil } -