Compare commits
No commits in common. "7b3d59a581684d33637366ddde1fe74ecd127694" and "28ccd6d107fbb2224694bf2837c839fe0b89087f" have entirely different histories.
7b3d59a581
...
28ccd6d107
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
||||||
cmd/passgo/passgo
|
cmd/gpass/passgo
|
||||||
cmd/passgo-gui/passgo-gui
|
cmd/gpass-gui/passgo-gui
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
//+build !android !linux
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"golang.org/x/image/font/gofont/goregular"
|
|
||||||
"golang.org/x/image/font/sfnt"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
regular *sfnt.Font
|
|
||||||
confDir string
|
|
||||||
)
|
|
||||||
|
|
||||||
func setFont() error {
|
|
||||||
f, err := os.Open("/System/Library/Fonts/AppleSDGothicNeo.ttc")
|
|
||||||
if err != nil {
|
|
||||||
log(Info, "Cannot open system font.")
|
|
||||||
return err
|
|
||||||
|
|
||||||
}
|
|
||||||
collection, err := sfnt.ParseCollectionReaderAt(f)
|
|
||||||
if err != nil {
|
|
||||||
log(Info, "Cannot parse system font.")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
regular, err = collection.Font(0)
|
|
||||||
if err != nil {
|
|
||||||
log(Info, "Cannot access first font in collection.")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
err := setFont()
|
|
||||||
if err != nil {
|
|
||||||
regular, err = sfnt.Parse(goregular.TTF)
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot parse default font: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usr, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot get current user: ", err)
|
|
||||||
}
|
|
||||||
confDir = path.Join(usr.HomeDir, ".config/passgo")
|
|
||||||
if _, err := os.Stat(confDir); os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(confDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
log(Info, "Cannot create configuration directory ", confDir)
|
|
||||||
log(Fatal, err)
|
|
||||||
} else {
|
|
||||||
log(Info, "Configuration directory created")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,14 +3,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
//"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/app"
|
"gioui.org/ui/app"
|
||||||
|
@ -25,47 +23,19 @@ import (
|
||||||
"gioui.org/ui/text"
|
"gioui.org/ui/text"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"gopkg.in/yaml.v2"
|
"golang.org/x/image/font/gofont/goregular"
|
||||||
|
"golang.org/x/image/font/sfnt"
|
||||||
|
|
||||||
"git.wow.st/gmp/passgo"
|
"git.wow.st/gmp/passgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type conf struct {
|
|
||||||
StoreDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var fd *os.File
|
var err error
|
||||||
confFile := path.Join(confDir, "config.yml")
|
store, err = passgo.GetStore()
|
||||||
if _, err := os.Stat(confFile); os.IsNotExist(err) {
|
|
||||||
fd, err = os.Create(confFile)
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot create configuration file: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
confbytes, err := ioutil.ReadFile(confFile)
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot read configuration file: ", err)
|
|
||||||
}
|
|
||||||
if err = yaml.UnmarshalStrict(confbytes, &Config); err != nil {
|
|
||||||
log(Fatal, "Cannot parse configuration file: ", err)
|
|
||||||
}
|
|
||||||
store.Dir = Config.StoreDir
|
|
||||||
log(Info, " StoreDir = ", store.Dir)
|
|
||||||
err = passgo.GetStore(&store)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Fatal, err)
|
log(Fatal, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fd != nil { // we still have an empty conf file open:
|
|
||||||
Config.StoreDir = store.Dir
|
|
||||||
saveConf(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
reload = make(chan struct{})
|
|
||||||
updated = make(chan struct{})
|
updated = make(chan struct{})
|
||||||
|
|
||||||
go Updater()
|
go Updater()
|
||||||
log(Info, "Staring event loop")
|
log(Info, "Staring event loop")
|
||||||
go eventLoop()
|
go eventLoop()
|
||||||
|
@ -74,23 +44,17 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Config conf
|
l []passgo.Pass
|
||||||
l []passgo.Pass
|
mux sync.Mutex
|
||||||
mux sync.Mutex
|
store *passgo.Store
|
||||||
store passgo.Store
|
updated chan struct{}
|
||||||
reload chan struct{}
|
|
||||||
updated chan struct{}
|
|
||||||
black = color.RGBA{A: 0xff, R: 0, G: 0, B: 0}
|
|
||||||
white = color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}
|
|
||||||
gray = color.RGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0}
|
|
||||||
darkgray = color.RGBA{A: 0xff, R: 0xa0, G: 0xa0, B: 0xa0}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Updater() {
|
func Updater() {
|
||||||
update := func() {
|
update := func() {
|
||||||
ltmp, err := store.List()
|
ltmp, err := store.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Info, err)
|
log(Fatal, err)
|
||||||
}
|
}
|
||||||
mux.Lock()
|
mux.Lock()
|
||||||
l = ltmp
|
l = ltmp
|
||||||
|
@ -106,8 +70,6 @@ func Updater() {
|
||||||
watcher.Add(store.Dir)
|
watcher.Add(store.Dir)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-reload:
|
|
||||||
update()
|
|
||||||
case <-watcher.Events:
|
case <-watcher.Events:
|
||||||
update()
|
update()
|
||||||
case e := <-watcher.Errors:
|
case e := <-watcher.Errors:
|
||||||
|
@ -116,71 +78,12 @@ func Updater() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveConf(fds ...*os.File) {
|
|
||||||
var fd *os.File
|
|
||||||
var err error
|
|
||||||
if len(fds) > 0 && fds[0] != nil {
|
|
||||||
fd = fds[0]
|
|
||||||
} else {
|
|
||||||
fd, err = os.Create(path.Join(confDir, "config.yml"))
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot open config file: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
confbytes, err := yaml.Marshal(Config)
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot save configuration: ", err)
|
|
||||||
}
|
|
||||||
_, err = fd.Write(confbytes)
|
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot write to configuration: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Overlay struct {
|
|
||||||
Face text.Face
|
|
||||||
Text string
|
|
||||||
Click gesture.Click
|
|
||||||
Color color.RGBA
|
|
||||||
Background color.RGBA
|
|
||||||
Alignment text.Alignment
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Overlay) Layout(c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
|
||||||
ins := layout.UniformInset(ui.Dp(1))
|
|
||||||
cs = ins.Begin(c, ops, cs)
|
|
||||||
var dims layout.Dimensions
|
|
||||||
st := layout.Stack{}
|
|
||||||
st.Init(ops, cs)
|
|
||||||
{
|
|
||||||
cs = st.Rigid()
|
|
||||||
l := text.Label{
|
|
||||||
Face: b.Face,
|
|
||||||
Text: b.Text,
|
|
||||||
Alignment: b.Alignment,
|
|
||||||
}
|
|
||||||
ins := layout.UniformInset(ui.Dp(4))
|
|
||||||
l.Material.Record(ops)
|
|
||||||
paint.ColorOp{Color: b.Color}.Add(ops)
|
|
||||||
l.Material.Stop()
|
|
||||||
dims = ins.End(l.Layout(ops, ins.Begin(c, ops, cs)))
|
|
||||||
pointer.RectAreaOp{image.Rect(0, 0, dims.Size.X, dims.Size.Y)}.Add(ops)
|
|
||||||
}
|
|
||||||
c2 := st.End(dims)
|
|
||||||
c1 := st.End(layoutRRect(b.Background, c, ops, st.Expand()))
|
|
||||||
dims = st.Layout(c1, c2)
|
|
||||||
return ins.End(dims)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Button struct {
|
type Button struct {
|
||||||
Face text.Face
|
Face text.Face
|
||||||
Label string
|
Label string
|
||||||
Click gesture.Click
|
Click gesture.Click
|
||||||
Color color.RGBA
|
Color color.RGBA
|
||||||
Background color.RGBA
|
clicked bool
|
||||||
Alignment text.Alignment
|
|
||||||
clicked bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func layoutRRect(col color.RGBA, c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
func layoutRRect(col color.RGBA, c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
||||||
|
@ -210,7 +113,7 @@ func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) {
|
||||||
b.End()
|
b.End()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
func (b *Button) Layout(c ui.Config, ops *ui.Ops, q input.Queue, cs layout.Constraints) layout.Dimensions {
|
||||||
b.clicked = false
|
b.clicked = false
|
||||||
for ev, ok := b.Click.Next(q); ok; ev, ok = b.Click.Next(q) {
|
for ev, ok := b.Click.Next(q); ok; ev, ok = b.Click.Next(q) {
|
||||||
if ev.Type == gesture.TypeClick {
|
if ev.Type == gesture.TypeClick {
|
||||||
|
@ -225,18 +128,16 @@ func (b *Button) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.Const
|
||||||
{
|
{
|
||||||
cs = st.Rigid()
|
cs = st.Rigid()
|
||||||
l := text.Label{
|
l := text.Label{
|
||||||
Face: b.Face,
|
Face: b.Face,
|
||||||
Text: b.Label,
|
Text: b.Label,
|
||||||
Alignment: b.Alignment,
|
|
||||||
}
|
}
|
||||||
ins := layout.UniformInset(ui.Dp(4))
|
ins := layout.UniformInset(ui.Dp(4))
|
||||||
paint.ColorOp{Color: b.Color}.Add(ops)
|
|
||||||
dims = ins.End(l.Layout(ops, ins.Begin(c, ops, cs)))
|
dims = ins.End(l.Layout(ops, ins.Begin(c, ops, cs)))
|
||||||
pointer.RectAreaOp{image.Rect(0, 0, dims.Size.X, dims.Size.Y)}.Add(ops)
|
pointer.RectAreaOp{image.Rect(0, 0, dims.Size.X, dims.Size.Y)}.Add(ops)
|
||||||
b.Click.Add(ops)
|
b.Click.Add(ops)
|
||||||
}
|
}
|
||||||
c2 := st.End(dims)
|
c2 := st.End(dims)
|
||||||
c1 := st.End(layoutRRect(b.Background, c, ops, st.Expand()))
|
c1 := st.End(layoutRRect(b.Color, c, ops, st.Expand()))
|
||||||
dims = st.Layout(c1, c2)
|
dims = st.Layout(c1, c2)
|
||||||
return ins.End(dims)
|
return ins.End(dims)
|
||||||
}
|
}
|
||||||
|
@ -248,43 +149,24 @@ func (b *Button) Clicked() bool {
|
||||||
func eventLoop() {
|
func eventLoop() {
|
||||||
w := app.NewWindow(app.WithWidth(ui.Dp(250)))
|
w := app.NewWindow(app.WithWidth(ui.Dp(250)))
|
||||||
q := w.Queue()
|
q := w.Queue()
|
||||||
var c ui.Config
|
_ = q
|
||||||
ops := new(ui.Ops)
|
ops := new(ui.Ops)
|
||||||
var dims layout.Dimensions
|
|
||||||
var cs layout.Constraints
|
|
||||||
var margincs layout.Constraints
|
|
||||||
var faces measure.Faces
|
var faces measure.Faces
|
||||||
var c1 layout.FlexChild // flex child for title bar
|
regular, err := sfnt.Parse(goregular.TTF)
|
||||||
|
if err != nil {
|
||||||
|
log(Fatal, "Cannot parse font.")
|
||||||
|
}
|
||||||
face := faces.For(regular, ui.Sp(16))
|
face := faces.For(regular, ui.Sp(16))
|
||||||
|
|
||||||
margin := layout.UniformInset(ui.Dp(10))
|
margin := layout.UniformInset(ui.Dp(10))
|
||||||
|
|
||||||
title := &text.Label{Face: face, Text: "passgo"}
|
|
||||||
dotsbtn := &Button{
|
|
||||||
Face: face,
|
|
||||||
Label: "\xe2\x8b\xae",
|
|
||||||
Alignment: text.End,
|
|
||||||
Color: black,
|
|
||||||
Background: gray,
|
|
||||||
}
|
|
||||||
|
|
||||||
titleflex := &layout.Flex{Axis: layout.Horizontal}
|
|
||||||
|
|
||||||
flex := &layout.Flex{Axis: layout.Vertical}
|
|
||||||
passInput := &text.Editor{Face: face, SingleLine: true}
|
passInput := &text.Editor{Face: face, SingleLine: true}
|
||||||
passInput.SetText("passphrase")
|
passInput.SetText("passphrase")
|
||||||
passSubmit := &Button{Face: face, Label: "submit", Color: black}
|
passSubmit := &Button{Face: face, Label: "submit"}
|
||||||
lst := &layout.List{Axis: layout.Vertical}
|
lst := &layout.List{Axis: layout.Vertical}
|
||||||
passBtns := make([]*Button, 0)
|
passBtns := make([]*Button, 0)
|
||||||
_ = passInput
|
_ = passInput
|
||||||
_ = passSubmit
|
_ = passSubmit
|
||||||
pathnames := make([]string, 0)
|
pathnames := make([]string, 0)
|
||||||
copied := &Overlay{Face: face, Text: "copied to clipboard",
|
|
||||||
Color: black,
|
|
||||||
Background: darkgray,
|
|
||||||
Alignment: text.Middle,
|
|
||||||
}
|
|
||||||
var copiedWhen time.Time
|
|
||||||
|
|
||||||
updateBtns := func() {
|
updateBtns := func() {
|
||||||
passBtns = passBtns[:0]
|
passBtns = passBtns[:0]
|
||||||
|
@ -298,9 +180,9 @@ func eventLoop() {
|
||||||
z = "/"
|
z = "/"
|
||||||
}
|
}
|
||||||
passBtns = append(passBtns, &Button{
|
passBtns = append(passBtns, &Button{
|
||||||
Face: face,
|
Face: face,
|
||||||
Label: strings.Join([]string{s, n, z}, ""),
|
Label: strings.Join([]string{s, n, z}, ""),
|
||||||
Background: gray,
|
Color: color.RGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0},
|
||||||
})
|
})
|
||||||
pathnames = append(pathnames, x.Pathname)
|
pathnames = append(pathnames, x.Pathname)
|
||||||
}
|
}
|
||||||
|
@ -308,190 +190,50 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
updateBtns()
|
updateBtns()
|
||||||
|
|
||||||
confBtn := &Button{
|
|
||||||
Face: face,
|
|
||||||
Label: "configure",
|
|
||||||
Alignment: text.Middle,
|
|
||||||
Color: black,
|
|
||||||
Background: gray,
|
|
||||||
}
|
|
||||||
|
|
||||||
storeDirLabel := &text.Label{Face: face, Text: "Store directory"}
|
|
||||||
storeDirEd := &text.Editor{Face: face, SingleLine: true}
|
|
||||||
storeDirEd.SetText(store.Dir)
|
|
||||||
saveBtn := &Button{
|
|
||||||
Face: face,
|
|
||||||
Label: "save",
|
|
||||||
Alignment: text.End,
|
|
||||||
Color: black,
|
|
||||||
Background: gray,
|
|
||||||
}
|
|
||||||
backBtn := &Button{
|
|
||||||
Face: face,
|
|
||||||
Label: "back",
|
|
||||||
Alignment: text.End,
|
|
||||||
Color: black,
|
|
||||||
Background: gray,
|
|
||||||
}
|
|
||||||
|
|
||||||
anim := &time.Ticker{}
|
|
||||||
animating := false
|
|
||||||
animOn := func() {
|
|
||||||
anim = time.NewTicker(time.Second / 120)
|
|
||||||
animating = true
|
|
||||||
w.Invalidate()
|
|
||||||
}
|
|
||||||
animOff := func() {
|
|
||||||
anim.Stop()
|
|
||||||
animating = false
|
|
||||||
}
|
|
||||||
|
|
||||||
var listPage, confPage, page func()
|
|
||||||
_ = confPage
|
|
||||||
|
|
||||||
listPage = func() {
|
|
||||||
cs = flex.Flexible(1.0)
|
|
||||||
mux.Lock()
|
|
||||||
if lst.Dragging() {
|
|
||||||
key.HideInputOp{}.Add(ops)
|
|
||||||
}
|
|
||||||
var c2 layout.FlexChild
|
|
||||||
if len(passBtns) == 0 {
|
|
||||||
c2 = flex.End(confBtn.Layout(c, q, ops, cs))
|
|
||||||
if confBtn.Clicked() {
|
|
||||||
log(Info, "Configure")
|
|
||||||
w.Invalidate()
|
|
||||||
page = confPage
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for lst.Init(c, q, ops, cs, len(passBtns)); lst.More(); lst.Next() {
|
|
||||||
btn := passBtns[lst.Index()]
|
|
||||||
dims = btn.Layout(c, q, ops, lst.Constraints())
|
|
||||||
lst.End(dims)
|
|
||||||
if btn.Clicked() {
|
|
||||||
log(Info, "Clicked ", btn.Label)
|
|
||||||
// don't block UI thread on decryption attempt
|
|
||||||
go func(name string) {
|
|
||||||
//p,err := store.Decrypt(name, prompt)
|
|
||||||
p, err := store.Decrypt(name)
|
|
||||||
if err == nil {
|
|
||||||
passgo.Clip(p)
|
|
||||||
copiedWhen = time.Now()
|
|
||||||
animOn()
|
|
||||||
} else {
|
|
||||||
log(Info, "Can't decrypt ", name)
|
|
||||||
log(Info, err)
|
|
||||||
}
|
|
||||||
}(pathnames[lst.Index()])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c2 = flex.End(lst.Layout())
|
|
||||||
}
|
|
||||||
mux.Unlock()
|
|
||||||
if dotsbtn.Clicked() {
|
|
||||||
log(Info, "Configure")
|
|
||||||
w.Invalidate()
|
|
||||||
page = confPage
|
|
||||||
}
|
|
||||||
flex.Layout(c1, c2)
|
|
||||||
x := time.Since(copiedWhen).Seconds()
|
|
||||||
start, end := 1.5, 1.75
|
|
||||||
switch {
|
|
||||||
case x > start && x < end:
|
|
||||||
fade := (end - x) / (end - start)
|
|
||||||
copied.Color.R = uint8(float64(black.R) * fade)
|
|
||||||
copied.Color.G = uint8(float64(black.G) * fade)
|
|
||||||
copied.Color.B = uint8(float64(black.B) * fade)
|
|
||||||
copied.Color.A = uint8(float64(black.A) * fade)
|
|
||||||
copied.Background.R = uint8(float64(darkgray.R) * fade)
|
|
||||||
copied.Background.G = uint8(float64(darkgray.G) * fade)
|
|
||||||
copied.Background.B = uint8(float64(darkgray.B) * fade)
|
|
||||||
copied.Background.A = uint8(float64(darkgray.A) * fade)
|
|
||||||
fallthrough
|
|
||||||
case x <= start:
|
|
||||||
cs = margincs
|
|
||||||
al := layout.Align{Alignment: layout.SE}
|
|
||||||
cs = al.Begin(ops, cs)
|
|
||||||
cs.Width.Min = cs.Width.Max
|
|
||||||
dims = al.End(copied.Layout(c, ops, cs))
|
|
||||||
case animating:
|
|
||||||
copied.Color = black
|
|
||||||
copied.Background = darkgray
|
|
||||||
animOff()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
confPage = func() {
|
|
||||||
cs = flex.Rigid()
|
|
||||||
c2 := flex.End(storeDirLabel.Layout(ops, cs))
|
|
||||||
cs = flex.Rigid()
|
|
||||||
c3 := flex.End(storeDirEd.Layout(c, q, ops, cs))
|
|
||||||
cs = flex.Rigid()
|
|
||||||
al := &layout.Align{Alignment: layout.E}
|
|
||||||
cs = al.Begin(ops, cs)
|
|
||||||
btnflx := &layout.Flex{Axis: layout.Horizontal}
|
|
||||||
btnflx.Init(ops, cs)
|
|
||||||
cs = btnflx.Rigid()
|
|
||||||
bc1 := btnflx.End(backBtn.Layout(c, q, ops, cs))
|
|
||||||
cs = btnflx.Rigid()
|
|
||||||
bc2 := btnflx.End(saveBtn.Layout(c, q, ops, cs))
|
|
||||||
dims = btnflx.Layout(bc1, bc2)
|
|
||||||
c4 := flex.End(al.End(dims))
|
|
||||||
flex.Layout(c1, c2, c3, c4)
|
|
||||||
if backBtn.Clicked() {
|
|
||||||
log(Info, "Back")
|
|
||||||
storeDirEd.SetText(store.Dir)
|
|
||||||
w.Invalidate()
|
|
||||||
page = listPage
|
|
||||||
}
|
|
||||||
if saveBtn.Clicked() {
|
|
||||||
log(Info, "Save")
|
|
||||||
go func() { // do not block UI thread
|
|
||||||
store.Dir = storeDirEd.Text()
|
|
||||||
passgo.GetStore(&store)
|
|
||||||
Config.StoreDir = store.Dir
|
|
||||||
saveConf()
|
|
||||||
reload <- struct{}{}
|
|
||||||
}()
|
|
||||||
w.Invalidate()
|
|
||||||
page = listPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
page = listPage
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-updated:
|
case <-updated:
|
||||||
log(Info, "UPDATE")
|
log(Info, "UPDATE")
|
||||||
updateBtns()
|
updateBtns()
|
||||||
w.Invalidate()
|
w.Invalidate()
|
||||||
case <-anim.C:
|
|
||||||
w.Invalidate()
|
|
||||||
case e := <-w.Events():
|
case e := <-w.Events():
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case app.DestroyEvent:
|
case app.DestroyEvent:
|
||||||
return
|
return
|
||||||
case app.UpdateEvent:
|
case app.UpdateEvent:
|
||||||
c = &e.Config
|
c := &e.Config
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
faces.Reset(c)
|
faces.Reset(c)
|
||||||
cs = layout.RigidConstraints(e.Size)
|
var dims layout.Dimensions
|
||||||
|
cs := layout.RigidConstraints(e.Size)
|
||||||
cs = margin.Begin(c, ops, cs)
|
cs = margin.Begin(c, ops, cs)
|
||||||
margincs = cs
|
|
||||||
flex.Init(ops, cs)
|
|
||||||
cs = flex.Rigid()
|
|
||||||
titleflex.Init(ops, cs)
|
|
||||||
cs = titleflex.Rigid()
|
|
||||||
ct2 := titleflex.End(dotsbtn.Layout(c, q, ops, cs))
|
|
||||||
cs = titleflex.Flexible(1.0)
|
|
||||||
cs.Width.Min = cs.Width.Max
|
|
||||||
ct1 := titleflex.End(title.Layout(ops, cs))
|
|
||||||
c1 = flex.End(titleflex.Layout(ct1, ct2))
|
|
||||||
|
|
||||||
page()
|
mux.Lock()
|
||||||
|
if lst.Dragging() {
|
||||||
margin.End(dims)
|
key.HideInputOp{}.Add(ops)
|
||||||
|
}
|
||||||
|
for lst.Init(c, q, ops, cs, len(passBtns)); lst.More(); lst.Next() {
|
||||||
|
btn := passBtns[lst.Index()]
|
||||||
|
dims = btn.Layout(c, ops, q, lst.Constraints())
|
||||||
|
lst.End(dims)
|
||||||
|
if btn.Clicked() {
|
||||||
|
// don't block UI thread on decryption attempt
|
||||||
|
log(Info, "Clicked ", btn.Label)
|
||||||
|
go func(name string) {
|
||||||
|
//p,err := store.Decrypt(name, prompt)
|
||||||
|
p, err := store.Decrypt(name)
|
||||||
|
if err == nil {
|
||||||
|
passgo.Clip(p)
|
||||||
|
} else {
|
||||||
|
log(Info, "Can't decrypt ", name)
|
||||||
|
log(Info, err)
|
||||||
|
}
|
||||||
|
}(pathnames[lst.Index()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mux.Unlock()
|
||||||
|
dims = lst.Layout()
|
||||||
|
dims = margin.End(dims)
|
||||||
w.Update(ops)
|
w.Update(ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,7 @@ func parse(args []string) ([]string, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var store passgo.Store
|
store, err := passgo.GetStore()
|
||||||
err := passgo.GetStore(&store)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
|
|
17
main.go
17
main.go
|
@ -34,26 +34,25 @@ type Store struct {
|
||||||
keyring openpgp.KeyRing
|
keyring openpgp.KeyRing
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStore(store *Store) error {
|
func GetStore() (*Store, error) {
|
||||||
|
ret := &Store{}
|
||||||
u, err := user.Current()
|
u, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't get current user.")
|
return ret, fmt.Errorf("Can't get current user.")
|
||||||
}
|
|
||||||
if store.Dir == "" {
|
|
||||||
store.Dir = path.Join(u.HomeDir, ".password-store")
|
|
||||||
}
|
}
|
||||||
|
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()
|
defer fd.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't open keyring file")
|
return ret, fmt.Errorf("Can't open keyring file")
|
||||||
}
|
}
|
||||||
kr, err := openpgp.ReadKeyRing(fd)
|
kr, err := openpgp.ReadKeyRing(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't open gnupg keyring.")
|
return ret, fmt.Errorf("Can't open gnupg keyring.")
|
||||||
}
|
}
|
||||||
store.keyring = kr
|
ret.keyring = kr
|
||||||
return nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type caseInsensitive []os.FileInfo
|
type caseInsensitive []os.FileInfo
|
||||||
|
|
Loading…
Reference in New Issue
Block a user