~eliasnaur/gio

Mailing list for the Gio project for discussion and patches.

Send your message to ~eliasnaur/gio@lists.sr.ht. No account is required and you can post without being subscribed.

11 4

a Gio password manager

Gregory Pomerantz
Details
Message ID
<2dcaf2e4-bf77-7bb2-5610-470c93162839@wow.st>
DKIM signature
pass
Download raw message
I used Gio to build a very simple front-end to pass, the Unix password 
manager.

https://git.wow.st/gmp/passgo

I only have a MacOS desktop for now so that is what is fully supported. 
There is Darwin-specific code to set the GPG_AGENT_INFO environment 
variable, so you can use gpg-agent for passphrase prompts. This would be 
easy to adapt to Linux but a few things would need to be tweaked (see 
impl_darwin.go in the top level directory). If gpg-agent is not 
available, passgo provides a passphrase entry page, though the 
passphrase currently is visible as you type (should there be a password 
entry mode for layout.Editor?).

There is also Darwin code for copying passwords to the clipboard which 
would need to be implemented for other platforms. Other than the 
clipboard and lack of access to gpg-agent, the GUI should work on LInux 
too. Ultimately I would like to support Android but have not started 
looking into that, it should certainly be possible but I have no idea at 
this point how to access the keychain.

I decided to use the base Gio API for this instead of a functional 
wrapper, so I can get a better feel for how it is supposed to work. The 
GUI includes multiple pages under a standard title bar as well as a 
little bit of animation. The animation code is ugly and I'm sure there 
is a better way to do it.
Details
Message ID
<BWTVRWDF6B08.1VDC7R9WO235P@testmac>
In-Reply-To
<2dcaf2e4-bf77-7bb2-5610-470c93162839@wow.st> (view parent)
DKIM signature
pass
Download raw message
On Fri Sep 6, 2019 at 1:44 PM Gregory Pomerantz wrote:
> I used Gio to build a very simple front-end to pass, the Unix password 
> manager.
> 
> https://git.wow.st/gmp/passgo
> 

Nice!

FWIW, I couldn't get it to work on my macOS 10.14. I ran

	$ brew install gpg
	$ gpg --full-generate-key
	$ ll ~/.gnupg/
	total 24
	drwx------  11 elias  staff   352 Sep  7 17:19 .
	drwxr-xr-x+ 61 elias  staff  1952 Sep  7 17:29 ..
	srwx------   1 elias  staff     0 Sep  7 17:18 S.gpg-agent
	srwx------   1 elias  staff     0 Sep  7 17:18 S.gpg-agent.browser
	srwx------   1 elias  staff     0 Sep  7 17:18 S.gpg-agent.extra
	srwx------   1 elias  staff     0 Sep  7 17:18 S.gpg-agent.ssh
	drwx------   3 elias  staff    96 Sep  7 17:18 openpgp-revocs.d
	drwx------   4 elias  staff   128 Sep  7 17:18 private-keys-v1.d
	-rw-r--r--   1 elias  staff  1444 Sep  7 17:18 pubring.kbx
	-rw-------   1 elias  staff    32 Sep  7 17:16 pubring.kbx~
	-rw-------   1 elias  staff  1280 Sep  7 17:19 trustdb.gpg
	$ git clone https://git.wow.st/gmp/passgo.git
	$ cd passgo
	$ go mod init git.wow.st/gmp/passgo

Then I applied this change:

	diff --git a/main.go b/main.go
	index e19d440..7975b6c 100644
	--- a/main.go
	+++ b/main.go
	@@ -46,7 +46,7 @@ func GetStore(store *Store) error {
		fd, err := os.Open(path.Join(u.HomeDir, ".gnupg/secring.gpg"))
		defer fd.Close()
		if err != nil {
	-		return fmt.Errorf("Can't open keyring file")
	+		return fmt.Errorf("Can't open keyring file %w", err)
		}
		kr, err := openpgp.ReadKeyRing(fd)
		if err != nil {

And finally ran passgo-gui with

	$ go run ./cmd/passgo-gui
	GPG_AGENT_INFO = /Users/elias/.gnupg/S.gpg-agent:28956:1
	2019/09/07 17:34:10  StoreDir =
	2019/09/07 17:34:10 Can't open keyring file open /Users/elias/.gnupg/secring.gpg: no such file or directory
	exit status 255

If you intend to maintain passgo for more than a short while, I strongly
suggest you add a go.mod file as I did above. That way, passgo-gui won't
break each time I tweak the Gio API.

> The GUI includes multiple pages under a standard title bar as well as a 
> little bit of animation. The animation code is ugly and I'm sure there 
> is a better way to do it.

If you haven't seen it already the code for
[scatter.im](https://scatter.im) includes a system for "pages" and for
animating transitions between them.

-- elias
Gregory Pomerantz
Details
Message ID
<4279f372-1b78-6c65-cea9-09228771dc82@wow.st>
In-Reply-To
<BWTVRWDF6B08.1VDC7R9WO235P@testmac> (view parent)
DKIM signature
pass
Download raw message
On 9/7/19 11:38 AM, Elias Naur wrote:
> And finally ran passgo-gui with
>
> 	$ go run ./cmd/passgo-gui
> 	GPG_AGENT_INFO = /Users/elias/.gnupg/S.gpg-agent:28956:1
> 	2019/09/07 17:34:10  StoreDir =
> 	2019/09/07 17:34:10 Can't open keyring file open /Users/elias/.gnupg/secring.gpg: no such file or directory
> 	exit status 255

Thanks for the report. Reportedly gpg no longer uses the secring.gpg 
file. My original code was built with earlier versions of GPG so this is 
not an issue on my system (I have an old secring.gpg file lying around 
and was not aware that this has been deprecated). I will take a look at 
making this work with current versions of GPG.

> If you intend to maintain passgo for more than a short while, I strongly
> suggest you add a go.mod file as I did above. That way, passgo-gui won't
> break each time I tweak the Gio API.
Eventually but at the moment this is just as much a way for me to get 
comfortable with GIo, so I'll be trying to track the Gio API as it 
changes. I don't feel it is ready for a real release yet in any event.
> If you haven't seen it already the code for
> [scatter.im](https://scatter.im) includes a system for "pages" and for
> animating transitions between them.

I have looked at that but not yet tried to replicate any of those 
approaches. I will look again in more detail, I was wondering how to 
manage the main event loop in order to draw as infrequently as possible 
-- i.e. only doing an FPS-based redraw when animations are in progress.
Details
Message ID
<BWVGVBQZNU55.3H5Y516RYY14B@toolbox>
In-Reply-To
<4279f372-1b78-6c65-cea9-09228771dc82@wow.st> (view parent)
DKIM signature
pass
Download raw message
On Sun Sep 8, 2019 at 9:26 PM Gregory Pomerantz wrote:
> > If you haven't seen it already the code for
> > [scatter.im](https://scatter.im) includes a system for "pages" and for
> > animating transitions between them.
> 
> I have looked at that but not yet tried to replicate any of those 
> approaches. I will look again in more detail, I was wondering how to 
> manage the main event loop in order to draw as infrequently as possible 
> -- i.e. only doing an FPS-based redraw when animations are in progress.
> 

For external updates such as network requests, use Window.Invalidate.  Because
Window.Events is a channel, it integrates well with a Go select statement
(untested):

	select {
	case e := <-externalNetwork:
		// Update state
		w.Invalidate() // Schedule UpdateEvent
	case e := w.Events():
		if e, ok := e.(app.UpdateEvent); ok {
			...
		}
	}

For internal updates such as a blinking cursor or animation, use ui.InvalidateOp:

	ui.InvalidateOp{At: ...}.Add(ops)

leave the At field zeroed will request an immediate redraw, use a specific time
the scxhedule a redraw in the future.

Finally, make sure you handle input events before layout. In the case of a button,
update the button state with events before drawing it:

	var q input.Queue
	...
	for e, ok := q.Next(button); ok; e, ok = q.Next(button) {
		// Update button state
		...
	}

	// Draw button

If you draw before handling events, your UI might have a newer state than what
is drawn to the window.

-- elias
Gregory Pomerantz
Details
Message ID
<7fe79495-3933-5c34-5bcd-54cb718ca6fa@wow.st>
In-Reply-To
<BWVGVBQZNU55.3H5Y516RYY14B@toolbox> (view parent)
DKIM signature
pass
Download raw message
> For internal updates such as a blinking cursor or animation, use ui.InvalidateOp:
>
> 	ui.InvalidateOp{At: ...}.Add(ops)
>
> leave the At field zeroed will request an immediate redraw, use a specific time
> the scxhedule a redraw in the future.

I was experimenting with InvalidateOp but it was unclear to me if it 
causes a future window UpdateEvent to be triggered if you are not 
otherwise set to receive one.

WIth Window.Invalidate() it looks like the next redraw is immediate 
regardless of how long it took to draw the current window -- i.e. it 
would spin your CPU doing redraws at an unreasonably high FPS. It would 
be nice to have a way to set a max re-draw rate so that inputs, 
invalidations, etc do not trigger UpdateEvents more frequently than, 
say, 90 or 120 FPS.

> Finally, make sure you handle input events before layout. In the case of a button,
> update the button state with events before drawing it:
Makes sense, thanks.
Details
Message ID
<BWVHCJ7F32TC.OLCBS8TL2GRI@testmac>
In-Reply-To
<7fe79495-3933-5c34-5bcd-54cb718ca6fa@wow.st> (view parent)
DKIM signature
pass
Download raw message
On Mon Sep 9, 2019 at 8:28 AM Gregory Pomerantz wrote:
> 
> > For internal updates such as a blinking cursor or animation, use ui.InvalidateOp:
> >
> > 	ui.InvalidateOp{At: ...}.Add(ops)
> >
> > leave the At field zeroed will request an immediate redraw, use a specific time
> > the scxhedule a redraw in the future.
> 
> I was experimenting with InvalidateOp but it was unclear to me if it 
> causes a future window UpdateEvent to be triggered if you are not 
> otherwise set to receive one.
> 
> WIth Window.Invalidate() it looks like the next redraw is immediate 
> regardless of how long it took to draw the current window -- i.e. it 
> would spin your CPU doing redraws at an unreasonably high FPS. It would 
> be nice to have a way to set a max re-draw rate so that inputs, 
> invalidations, etc do not trigger UpdateEvents more frequently than, 
> say, 90 or 120 FPS.
> 

Window.Invalidate is only intended for externally triggered events, not
for continuous animation. For animation, each widget or UI element that
need animation add their required InvalidateOp to ops. Window.Update
then use the InvalidateOp with the earliest At time to schedule a
redraw.

A feature that limits FPS might still be useful for some use-cases, but
shouldn't be for plain UI programs. You can emulate limiting by having a
goroutine throttled to say 30 FPS and calling Window.Invalidate (it is
goroutine-safe).

-- elias
Details
Message ID
<20190909132209.GA20755@larrymbp14>
In-Reply-To
<BWVGVBQZNU55.3H5Y516RYY14B@toolbox> (view parent)
DKIM signature
missing
Download raw message
On Mon, Sep 09, 2019 at 02:22:43PM +0200, Elias Naur wrote:
> For external updates such as network requests, use
> Window.Invalidate.  Because Window.Events is a channel, it
> integrates well with a Go select statement (untested):
> 
> 	select {
> 	case e := <-externalNetwork:
> 		// Update state
> 		w.Invalidate() // Schedule UpdateEvent
> 	case e := w.Events():
> 		if e, ok := e.(app.UpdateEvent); ok {
> 			...
> 		}
> 	}

Just to clarify: Since w.Events() returns a channel, that second case
would need to be

> 	case e := <-w.Events():

-- L
Gregory Pomerantz
Details
Message ID
<40c5312b-ecb8-5ac7-7ba3-1c730b81a265@wow.st>
In-Reply-To
<4279f372-1b78-6c65-cea9-09228771dc82@wow.st> (view parent)
DKIM signature
pass
Download raw message
On 9/7/19 11:38 AM, Elias Naur wrote:
> Thanks for the report. Reportedly gpg no longer uses the secring.gpg 
> file. My original code was built with earlier versions of GPG so this 
> is not an issue on my system (I have an old secring.gpg file lying 
> around and was not aware that this has been deprecated). I will take a 
> look at making this work with current versions of GPG.

As I expected, gpg 2.0 ruins everything, I made some updates to call out 
to gpg commands as necessary so this should now be working on a fresh 
MacOS gpg install. Let me know if you give it a try.

There is still some code cleanup to do and I would love to get an 
Android version working via OpenKeychain -- this will require calling 
out to java classes on Android, and suggestions for the best way to make 
that happen?
Details
Message ID
<BWWFHBYVYZB0.BCP5JYJAFQVG@toolbox>
In-Reply-To
<40c5312b-ecb8-5ac7-7ba3-1c730b81a265@wow.st> (view parent)
DKIM signature
pass
Download raw message
On Tue Sep 10, 2019 at 9:00 AM Gregory Pomerantz wrote:
> On 9/7/19 11:38 AM, Elias Naur wrote:
> > Thanks for the report. Reportedly gpg no longer uses the secring.gpg 
> > file. My original code was built with earlier versions of GPG so this 
> > is not an issue on my system (I have an old secring.gpg file lying 
> > around and was not aware that this has been deprecated). I will take a 
> > look at making this work with current versions of GPG.
> 
> As I expected, gpg 2.0 ruins everything, I made some updates to call out 
> to gpg commands as necessary so this should now be working on a fresh 
> MacOS gpg install. Let me know if you give it a try.
> 

I tried (on Linux this time):

	$ go run git.wow.st/gmp/passgo/cmd/passgo-gui
	go: finding git.wow.st/gmp/passgo latest
	go: finding gioui.org latest
	go: finding gioui.org/ui latest
	go: finding gopkg.in/yaml.v2 v2.2.2
	go: finding golang.org/x/crypto latest
	go: downloading gopkg.in/yaml.v2 v2.2.2
	go: downloading gioui.org v0.0.0-20190909155013-86b231ca287a
	go: extracting gopkg.in/yaml.v2 v2.2.2
	go: extracting gioui.org v0.0.0-20190909155013-86b231ca287a
	go: finding github.com/jcmdev0/gpgagent latest
	go: downloading github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c
	go: extracting github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c
	# git.wow.st/gmp/passgo
	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:253:3: not enough arguments to return
	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:253:11: s.gpgDecrypt undefined (type *Store has no field or method gpgDecrypt)
	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:299:10: undefined: gpgIdentities
	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:315:15: s.gpgEncrypt undefined (type *Store has no field or method gpgEncrypt)

> There is still some code cleanup to do and I would love to get an 
> Android version working via OpenKeychain -- this will require calling 
> out to java classes on Android, and suggestions for the best way to make 
> that happen?
> 

Your best bet at this time is to use the gio -buildmode archive flag to get
an .aar file you can include in an Android project. See

	https://man.sr.ht/~eliasnaur/gio/integrate.md#android

-- elias
Gregory Pomerantz
Details
Message ID
<1f100908-9341-8d42-7fe5-c5760483d28b@wow.st>
In-Reply-To
<BWWFHBYVYZB0.BCP5JYJAFQVG@toolbox> (view parent)
DKIM signature
pass
Download raw message
> 	# git.wow.st/gmp/passgo
> 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:253:3: not enough arguments to return
> 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:253:11: s.gpgDecrypt undefined (type *Store has no field or method gpgDecrypt)
> 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:299:10: undefined: gpgIdentities
> 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:315:15: s.gpgEncrypt undefined (type *Store has no field or method gpgEncrypt)
A couple of functions were not yet implemented for Linux, I don't have a 
Linux desktop at the moment for testing. I cloned the Darwin code that I 
expect should work fine under Linux and eliminated the code that I know 
will fail. The current version compiles on Linux and the command-line 
tool works if you would like to try the gui tool again.
Details
Message ID
<BWWIKTR5NK6W.2XN4QW3FNF588@testmac>
In-Reply-To
<1f100908-9341-8d42-7fe5-c5760483d28b@wow.st> (view parent)
DKIM signature
pass
Download raw message
On Tue Sep 10, 2019 at 1:00 PM Gregory Pomerantz wrote:
> 
> > 	# git.wow.st/gmp/passgo
> > 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:253:3: not enough arguments to return
> > 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:253:11: s.gpgDecrypt undefined (type *Store has no field or method gpgDecrypt)
> > 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:299:10: undefined: gpgIdentities
> > 	go/pkg/mod/git.wow.st/gmp/passgo@v0.0.0-20190910124559-09859ca0a9c1/main.go:315:15: s.gpgEncrypt undefined (type *Store has no field or method gpgEncrypt)
> A couple of functions were not yet implemented for Linux, I don't have a 
> Linux desktop at the moment for testing. I cloned the Darwin code that I 
> expect should work fine under Linux and eliminated the code that I know 
> will fail. The current version compiles on Linux and the command-line 
> tool works if you would like to try the gui tool again.

I just switched to my macbook instead :) It runs great now, thanks. It's
a neat little program!

-- elias
Gregory Pomerantz
Details
Message ID
<9b946891-f46c-e5a0-3fa5-65e83f88deee@wow.st>
In-Reply-To
<BWWIKTR5NK6W.2XN4QW3FNF588@testmac> (view parent)
DKIM signature
pass
Download raw message
On 9/10/19 1:55 PM, Elias Naur wrote:

>> A couple of functions were not yet implemented for Linux, I don't have a
>> Linux desktop at the moment for testing. I cloned the Darwin code that I
>> expect should work fine under Linux and eliminated the code that I know
>> will fail. The current version compiles on Linux and the command-line
>> tool works if you would like to try the gui tool again.
> I just switched to my macbook instead :) It runs great now, thanks. It's
> a neat little program!
thanks, glad it works! I'll be adding in a few missing features like 
nesting passwords into sub-directories (these can be decrypted but not 
created) and random password generation, and will look at putting in a 
go.mod file when things settle down. If you install the command line 
version of pass (brew install pass) you should find your password 
database is compatible.