Compare commits

..

4 Commits

Author SHA1 Message Date
fdb655c671 Tweak SelButton. 2019-10-10 17:07:39 -04:00
0f984918ea Remove page timing debug information. 2019-10-10 11:36:27 -04:00
49ebf55b15 Fix performance bug in list.Layout(...) 2019-10-10 11:35:11 -04:00
455a8960e5 Update gio version. 2019-10-08 10:58:42 -04:00
14 changed files with 215 additions and 1000 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@ cmd/passgo/passgo
cmd/passgo-gui/passgo-gui cmd/passgo-gui/passgo-gui
nohup.out nohup.out
*.apk *.apk
PgpConnect.jar cpuprofile

View File

@ -1,296 +0,0 @@
package st.wow.git.passgo;
import java.lang.Runnable;
import java.lang.String;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import android.os.Handler;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
import android.content.Context;
import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.Intent;
import android.util.Log;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.PendingIntent;
import android.content.IntentSender;
import android.content.IntentSender.OnFinished;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.Manifest;
import android.content.pm.PackageManager;
public class PgpConnect extends Fragment {
Activity act;
Context ctx;
Handler handler;
OpenPgpServiceConnection mServiceConnection;
ClipboardManager cb;
final int PERMISSIONS_REQUEST = 1;
public PgpConnect(Activity act) {
Log.d("gio", "PgpConnect(Activity)");
act = act;
ctx = act.getApplicationContext();
this.handler = new Handler(ctx.getMainLooper());
final FragmentManager fm = act.getFragmentManager();
final Fragment frag = this;
handler.post(new Runnable() {
public void run() {
Log.d("gio", "PgpConnect(): adding fragment");
FragmentTransaction ft = fm.beginTransaction();
ft.add(frag, "PgpConnect");
ft.commitNow();
if (ctx.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ctx.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST);
}
}
});
}
@Override public void onAttach(Context ctx) {
super.onAttach(ctx);
Log.d("gio", "onAttach()");
this.ctx = ctx;
this.handler = new Handler(ctx.getMainLooper());
mServiceConnection = new OpenPgpServiceConnection(ctx, "org.sufficientlysecure.keychain");
mServiceConnection.bindToService();
cb = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
installComplete(this);
}
//For debugging:
/*private void printExtras(Intent data) {
Bundle bundle = data.getExtras();
if (bundle != null) {
for (String k : bundle.keySet()) {
Log.d("gio", "extra:" + k);
}
}
}*/
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d("gio", "onActivityResult(" + requestCode + "): " + resultCode);
super.onActivityResult(requestCode, resultCode, data);
//activityResult(requestCode, resultCode);
if (resultCode != Activity.RESULT_OK) {
Log.d("gio", "onActivityResult: not OK");
stringResult(requestCode, null);
return;
}
switch (data.getAction()) {
case OpenPgpApi.ACTION_DECRYPT_VERIFY: {
Log.d("gio", "action decrypt");
_decrypt(data, requestCode);
break;
}
case OpenPgpApi.ACTION_ENCRYPT: {
Log.d("gio", "action encrypt");
_encrypt(data, requestCode);
break;
}
case OpenPgpApi.ACTION_GET_KEY_IDS:
Log.d("gio", "action getid");
String[] ids = data.getStringArrayExtra("user_ids");
if (ids != null && ids.length > 0) {
Log.d("gio", "got some IDs");
stringResult(requestCode, ids[0]);
} else {
Log.d("gio", "no ids");
stringResult(requestCode, null);
}
break;
default: {
Log.d("gio", "some other action");
}
}
}
public void Clip(byte []dat) {
ClipData clip = ClipData.newPlainText("PgpConnect", new String(dat));
cb.setPrimaryClip(clip);
}
public void GetId(int chint) {
if (handler == null) {
stringResult(chint, null);
return;
}
Log.d("gio", "GetId()");
handler.post(new Runnable() {
public void run() {
Intent data = new Intent();
data.setAction(OpenPgpApi.ACTION_GET_KEY_IDS);
//data.setAction(OpenPgpApi.ACTION_ENCRYPT);
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, new String[]{" "});
_getid(data, chint);
}
});
}
public void Decrypt(byte []dat, int chint) {
if (handler == null) {
stringResult(chint, null);
return;
}
handler.post(new Runnable() {
public void run() {
Intent data = new Intent();
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
data.putExtra("DATA", dat);
_decrypt(data, chint);
}
});
}
private void _getid(Intent data, int chint) {
Log.d("gio","_getid");
InputStream is = null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
OpenPgpApi api = new OpenPgpApi(this.ctx, mServiceConnection.getService());
Intent result = api.executeApi(data, is, os);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: {
Log.d("gio","_getid: success");
try {
String ret = os.toString("UTF-8");
//Log.d(OpenPgpApi.TAG, "output: " + ret);
stringResult(chint, ret);
} catch (UnsupportedEncodingException e) {
Log.e("gio", "UnsupportedEncodingException", e);
stringResult(chint, null);
}
break;
}
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
Log.d("gio","_getid: interaction required");
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
try {
startIntentSenderForResult(pi.getIntentSender(), chint, null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) {
Log.e("gio", "SendIntentException", e);
stringResult(chint, null);
}
break;
}
case OpenPgpApi.RESULT_CODE_ERROR: {
Log.d("gio","_getid: error");
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
stringResult(chint, null);
}
}
}
private void _decrypt(Intent data, int chint) {
Log.d("gio","_decrypt");
byte []dat = data.getByteArrayExtra("DATA");
InputStream is = new ByteArrayInputStream(dat);
ByteArrayOutputStream os = new ByteArrayOutputStream();
OpenPgpApi api = new OpenPgpApi(this.ctx, mServiceConnection.getService());
Intent result = api.executeApi(data, is, os);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: {
try {
String ret = os.toString("UTF-8");
//Log.d(OpenPgpApi.TAG, "output: " + ret);
stringResult(chint, ret);
} catch (UnsupportedEncodingException e) {
Log.e("gio", "UnsupportedEncodingException", e);
stringResult(chint, null);
}
break;
}
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
try {
startIntentSenderForResult(pi.getIntentSender(), chint, null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) {
Log.e("gio", "SendIntentException", e);
stringResult(chint, null);
}
break;
}
case OpenPgpApi.RESULT_CODE_ERROR: {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
stringResult(chint, null);
}
}
}
public void Encrypt(byte[] id, byte[] dat, int chint) {
if (handler == null) {
stringResult(chint, null);
return;
}
handler.post(new Runnable() {
public void run() {
Intent data = new Intent();
data.setAction(OpenPgpApi.ACTION_ENCRYPT);
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, new String[]{new String(id)});
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
data.putExtra("DATA", dat);
_encrypt(data, chint);
}
});
}
private void _encrypt(Intent data, int chint) {
Log.d("gio","_encrypt");
byte []dat = data.getByteArrayExtra("DATA");
InputStream is = new ByteArrayInputStream(dat);
ByteArrayOutputStream os = new ByteArrayOutputStream();
OpenPgpApi api = new OpenPgpApi(this.ctx, mServiceConnection.getService());
Intent result = api.executeApi(data, is, os);
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: {
try {
String ret = os.toString("UTF-8");
//Log.d(OpenPgpApi.TAG, "output: " + ret);
stringResult(chint, ret);
} catch (UnsupportedEncodingException e) {
Log.e("gio", "UnsupportedEncodingException", e);
stringResult(chint, null);
}
if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) {
OpenPgpSignatureResult sigResult
= result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
}
break;
}
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
try {
IntentSender sender = pi.getIntentSender();
//Log.d("PgpConnect", "IntentSender:" + sender.toString());
startIntentSenderForResult(sender, chint, null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) {
Log.e("gio", "SendIntentException", e);
stringResult(chint, null);
}
break;
}
case OpenPgpApi.RESULT_CODE_ERROR: {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
stringResult(chint, null);
break;
}
}
}
static private native void installComplete(PgpConnect p);
static private native void activityResult(int requestCode, int resultCode);
static private native void stringResult(int requestCode, String result);
}

View File

@ -5,41 +5,27 @@ package main
import ( import (
"os" "os"
"git.wow.st/gmp/passgo" "golang.org/x/image/font/gofont/goregular"
"gioui.org/app" "golang.org/x/image/font/sfnt"
)
var (
noidLabelText = "Enter a GPG key ID above"
) )
func init() { func init() {
log(Info, "Android start") log(Info, "Android start")
// Use a larger font on Android var err error
fontSize = 24 regular, err = sfnt.Parse(goregular.TTF)
}
func initPgp(w *app.Window) {
passgo.InitPgp(w)
}
func getConfDir() (string, error) {
ret, err := app.DataDir()
if err != nil { if err != nil {
log(Error, "Cannot get data directory:", err) log(Fatal, "Cannot parse default font: ", err)
return "", err
} }
if _, err := os.Stat(ret); os.IsNotExist(err) { confDir = app.DataDir()
err = os.MkdirAll(ret, 0700) if _, err := os.Stat(confDir); os.IsNotExist(err) {
err = os.MkdirAll(confDir, 0700)
if err != nil { if err != nil {
log(Error, "Cannot create configuration directory ", ret) log(Info, "Cannot create configuration directory ", confDir)
return "", err log(Fatal, err)
} else { } else {
log(Info, "Configuration directory created") log(Info, "Configuration directory created")
return ret, nil
} }
} else { } else {
log(Info, "Configuration directory found") log(Info, "Configuration directory found")
return ret, nil
} }
} }

View File

@ -7,14 +7,11 @@ import (
"os/user" "os/user"
"path" "path"
"gioui.org/app" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/font/sfnt"
) )
var ( func setFont() error {
noidLabelText = "No GPG ids available. Please create a private key or enter a key ID above"
)
/*func setFont() error {
f, err := os.Open("/System/Library/Fonts/AppleSDGothicNeo.ttc") f, err := os.Open("/System/Library/Fonts/AppleSDGothicNeo.ttc")
if err != nil { if err != nil {
log(Info, "Cannot open system font.") log(Info, "Cannot open system font.")
@ -32,38 +29,28 @@ var (
return err return err
} }
return nil return nil
}*/ }
func init() { func init() {
fontSize = 16 err := setFont()
//err := setFont()
//if err != nil {
// regular, err = sfnt.Parse(goregular.TTF)
// if err != nil {
// log(Fatal, "Cannot parse default font: ", err)
// }
//}
}
func initPgp(w *app.Window) {
}
func getConfDir() (string, error) {
usr, err := user.Current()
if err != nil { if err != nil {
log(Error, "Cannot get current user: ", err) regular, err = sfnt.Parse(goregular.TTF)
return "", err
}
ret := path.Join(usr.HomeDir, ".config/passgo")
if _, err := os.Stat(ret); os.IsNotExist(err) {
err = os.MkdirAll(ret, 0700)
if err != nil { if err != nil {
log(Info, "Cannot create configuration directory ", confDir) log(Fatal, "Cannot parse default font: ", err)
return "", err }
} else { }
log(Info, "Configuration directory created") usr, err := user.Current()
return ret, nil 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")
} }
} }
return ret, nil
} }

View File

@ -16,14 +16,12 @@ import (
"gioui.org/app" "gioui.org/app"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/system"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/text" "gioui.org/text"
"gioui.org/text/shape"
"gioui.org/unit" "gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
"gioui.org/font/gofont" "golang.org/x/image/font/sfnt"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -38,7 +36,7 @@ type conf struct {
} }
func main() { func main() {
if false { go func() { go func() {
f, err := os.Create("cpuprofile") f, err := os.Create("cpuprofile")
if err != nil { if err != nil {
fmt.Printf("Can't create CPU profile\n") fmt.Printf("Can't create CPU profile\n")
@ -55,14 +53,8 @@ func main() {
pprof.StopCPUProfile() pprof.StopCPUProfile()
f.Close() f.Close()
fmt.Printf("CPU profile written\n") fmt.Printf("CPU profile written\n")
}() } }()
var fd *os.File var fd *os.File
var err error
confDir, err = getConfDir()
if err != nil {
fmt.Printf("Can't get config directory")
os.Exit(-1)
}
confFile := path.Join(confDir, "config.yml") confFile := path.Join(confDir, "config.yml")
if _, err := os.Stat(confFile); os.IsNotExist(err) { if _, err := os.Stat(confFile); os.IsNotExist(err) {
fd, err = os.Create(confFile) fd, err = os.Create(confFile)
@ -100,10 +92,7 @@ func main() {
chdir = make(chan struct{}) chdir = make(chan struct{})
passch = make(chan []byte) passch = make(chan []byte)
go func() { passgo.Identities()
log(Info,"passgo.Identities()")
passgo.Identities()
}()
go Updater() go Updater()
log(Info, "Staring event loop") log(Info, "Staring event loop")
go eventLoop() go eventLoop()
@ -112,8 +101,8 @@ func main() {
} }
var ( var (
fontSize float32
confDir string confDir string
regular *sfnt.Font
Config conf Config conf
l []passgo.Pass l []passgo.Pass
mux sync.Mutex mux sync.Mutex
@ -122,11 +111,9 @@ var (
updated chan struct{} updated chan struct{}
chdir chan struct{} chdir chan struct{}
passch chan []byte passch chan []byte
th *material.Theme
) )
func Updater() { func Updater() {
//time.Sleep(time.Second * 2)
update := func() { update := func() {
fmt.Printf("update()\n") fmt.Printf("update()\n")
ltmp, err := store.List() ltmp, err := store.List()
@ -173,8 +160,7 @@ func saveConf(fds ...*os.File) {
} else { } else {
fd, err = os.Create(path.Join(confDir, "config.yml")) fd, err = os.Create(path.Join(confDir, "config.yml"))
if err != nil { if err != nil {
log(Error, "Config file = ", path.Join(confDir, "config.yml")) log(Fatal, "Cannot open config file: ", err)
log(Fatal, "Cannot open config file: ", err.Error())
} }
} }
defer fd.Close() defer fd.Close()
@ -190,25 +176,22 @@ func saveConf(fds ...*os.File) {
} }
func eventLoop() { func eventLoop() {
gofont.Register()
th = material.NewTheme()
th.TextSize = unit.Sp(fontSize)
w := app.NewWindow( w := app.NewWindow(
app.Size(unit.Dp(250), unit.Dp(500)), app.Size(unit.Dp(250), unit.Dp(500)),
app.Title("passgo")) app.Title("passgo"))
gtx := &layout.Context{Queue: w.Queue()} gtx := &layout.Context{Queue: w.Queue()}
//time.Sleep(time.Second/5)
var margincs layout.Constraints var margincs layout.Constraints
family := &shape.Family{Regular: regular}
var c1 layout.FlexChild // flex child for title bar var c1 layout.FlexChild // flex child for title bar
sysinset := &layout.Inset{} sysinset := &layout.Inset{}
margin := layout.UniformInset(unit.Dp(10)) margin := layout.UniformInset(unit.Dp(10))
title := th.Body1("passgo") title := &text.Label{Size: unit.Sp(16), Text: "passgo"}
dotsBtn := &Button{ dotsBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "\xe2\x8b\xae", Label: "\xe2\x8b\xae",
Alignment: text.Middle, Alignment: text.Middle,
Color: black, Color: black,
@ -221,12 +204,12 @@ func eventLoop() {
lst := &layout.List{Axis: layout.Vertical} lst := &layout.List{Axis: layout.Vertical}
passBtns := make([]*Button, 0) passBtns := make([]*Button, 0)
pathnames := make([]string, 0) pathnames := make([]string, 0)
copied := &Overlay{Size: unit.Sp(fontSize), Text: "copied to clipboard", copied := &Overlay{Size: unit.Sp(16), Text: "copied to clipboard",
Color: black, Color: black,
Background: darkgray, Background: darkgray,
Alignment: text.Middle, Alignment: text.Middle,
} }
cleared := &Overlay{Size: unit.Sp(fontSize), Text: "clipboard cleared", cleared := &Overlay{Size: unit.Sp(16), Text: "clipboard cleared",
Color: black, Color: black,
Background: darkgray, Background: darkgray,
Alignment: text.Middle, Alignment: text.Middle,
@ -246,7 +229,7 @@ func eventLoop() {
z = "/" z = "/"
} }
passBtns = append(passBtns, &Button{ passBtns = append(passBtns, &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: strings.Join([]string{s, n, z}, ""), Label: strings.Join([]string{s, n, z}, ""),
Background: gray, Background: gray,
}) })
@ -254,11 +237,11 @@ func eventLoop() {
} }
mux.Unlock() mux.Unlock()
} }
updateBtns()
idBtns := make([]*Button, 0) idBtns := make([]*Button, 0)
updateIdBtns := func() { updateIdBtns := func() {
log(Info,"passgo.Identities()")
ids, err := passgo.Identities() ids, err := passgo.Identities()
if err != nil { if err != nil {
log(Info, err) log(Info, err)
@ -267,7 +250,7 @@ func eventLoop() {
for i, n := range ids { for i, n := range ids {
if i >= len(idBtns) { if i >= len(idBtns) {
idBtns = append(idBtns, &Button{ idBtns = append(idBtns, &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: n, Label: n,
Alignment: text.End, Alignment: text.End,
Color: black, Color: black,
@ -281,64 +264,64 @@ func eventLoop() {
} }
confBtn := &Button{ confBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "configure", Label: "configure",
Alignment: text.Middle, Alignment: text.Middle,
Color: black, Color: black,
Background: gray, Background: gray,
} }
storeDirLabel := th.Label(unit.Sp(fontSize), "Store directory") storeDirLabel := &text.Label{Size: unit.Sp(16), Text: "Store directory"}
storeDirEd := &widget.Editor{ SingleLine: true} storeDirEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true}
storeDirEd.SetText(store.Dir) storeDirEd.SetText(store.Dir)
saveBtn := &Button{ saveBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "save", Label: "save",
Alignment: text.End, Alignment: text.End,
Color: black, Color: black,
Background: gray, Background: gray,
} }
backBtn := &Button{ backBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "back", Label: "back",
Alignment: text.End, Alignment: text.End,
Color: black, Color: black,
Background: gray, Background: gray,
} }
confirmLabel := th.Label(unit.Sp(fontSize), "Password exists. Overwrite?") confirmLabel := &text.Label{Size: unit.Sp(16), Text: "Password exists. Overwrite?"}
yesBtn := &Button{ yesBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "yes", Label: "yes",
Alignment: text.End, Alignment: text.End,
Color: black, Color: black,
Background: gray, Background: gray,
} }
promptLabel := th.Label(unit.Sp(fontSize), "passphrase") promptLabel := &text.Label{Size: unit.Sp(16), Text: "passphrase"}
promptEd := &widget.Editor{ SingleLine: true, Submit: true } promptEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Submit: true}
okBtn := &Button{ okBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "ok", Label: "ok",
Alignment: text.End, Alignment: text.End,
Color: black, Color: black,
Background: gray, Background: gray,
} }
plusBtn := &Button{ plusBtn := &Button{
Size: unit.Sp(fontSize), Size: unit.Sp(16),
Label: "+", Label: "+",
Alignment: text.Middle, Alignment: text.Middle,
Color: black, Color: black,
Background: gray, Background: gray,
} }
insertLabel := th.Label(unit.Sp(fontSize), "Insert") insertLabel := &text.Label{Size: unit.Sp(16), Text: "Insert"}
passnameLabel := th.Label(unit.Sp(fontSize), "password name:") passnameLabel := &text.Label{Size: unit.Sp(16), Text: "password name:"}
passnameEd := &widget.Editor{ SingleLine: true } passnameEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Submit: true}
passvalLabel := th.Label(unit.Sp(fontSize), "password value:") passvalLabel := &text.Label{Size: unit.Sp(16), Text: "password value:"}
passvalEd := &widget.Editor{ SingleLine: true, Submit: true } passvalEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Submit: true}
noidLabel := th.Label(unit.Sp(fontSize), noidLabelText) noidLabel := &text.Label{Size: unit.Sp(16), Text: "No GPG ids available. Please create a private key"}
idLabel := th.Label(unit.Sp(fontSize), "Select ID") idLabel := &text.Label{Size: unit.Sp(16), Text: "Select ID"}
anim := &time.Ticker{} anim := &time.Ticker{}
animating := false animating := false
@ -355,7 +338,6 @@ func eventLoop() {
} }
var listPage, idPage, insertPage, confirmPage, confPage, promptPage, page func() var listPage, idPage, insertPage, confirmPage, confPage, promptPage, page func()
_ = idPage
prompt := func() []byte { prompt := func() []byte {
page = promptPage page = promptPage
@ -377,22 +359,20 @@ func eventLoop() {
} }
switch { switch {
case store.Empty: case store.Empty:
confBtn.Layout(gtx) confBtn.Layout(gtx, family)
if confBtn.Clicked() { if confBtn.Clicked() {
log(Info, "Configure") log(Info, "Configure")
w.Invalidate() w.Invalidate()
page = confPage page = confPage
} }
case store.Id == "": case store.Id == "":
idLabel.Layout(gtx) idLabel.Layout(gtx, family)
w.Invalidate() w.Invalidate()
page = idPage page = idPage
default: default:
gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
lst.Layout(gtx, len(passBtns), func(i int) { lst.Layout(gtx, len(passBtns), func(i int) {
btn := passBtns[i] btn := passBtns[i]
gtx.Constraints.Width.Min = gtx.Constraints.Width.Max btn.Layout(gtx, family)
btn.Layout(gtx)
if btn.Clicked() { if btn.Clicked() {
log(Info, "Clicked ", btn.Label) log(Info, "Clicked ", btn.Label)
// don't block UI thread on decryption attempt // don't block UI thread on decryption attempt
@ -466,7 +446,7 @@ func eventLoop() {
al := layout.Align(layout.SE) al := layout.Align(layout.SE)
al.Layout(gtx, func() { al.Layout(gtx, func() {
gtx.Constraints.Width.Min = gtx.Constraints.Width.Max gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
overlay.Layout(gtx) overlay.Layout(gtx, family)
}) })
} }
if x > start2 && x < fade2a { if x > start2 && x < fade2a {
@ -477,74 +457,23 @@ func eventLoop() {
} }
} }
updateBtn := &Button{
Size: unit.Sp(fontSize),
Label: "Update",
Alignment: text.Middle,
Color: black,
Background: gray,
}
idEd := &widget.Editor{SingleLine: true, Submit: true}
idSubmitBtn := &Button{
Size: unit.Sp(fontSize),
Label: "Submit",
Alignment: text.Middle,
Color: black,
Background: gray,
}
idPage = func() { idPage = func() {
if !animating { if !animating {
animOn() animOn()
} }
c2 := flex.Rigid(gtx, func() { c2 := flex.Rigid(gtx, func() {
idLabel.Layout(gtx) idLabel.Layout(gtx, family)
}) })
var c3 layout.FlexChild //if len(idBtns) == 0 {
var c4 layout.FlexChild updateIdBtns()
var c5 layout.FlexChild //}
var c6 layout.FlexChild c3 := flex.Rigid(gtx, func() {
var c7 layout.FlexChild if len(idBtns) == 0 { // still zero after update
if len(idBtns) == 0 { noidLabel.Layout(gtx, family)
c3 = flex.Rigid(gtx, func() { } else {
updateBtn.Layout(gtx)
})
if updateBtn.Clicked() {
updateIdBtns()
w.Invalidate()
}
c4 = flex.Rigid(gtx, func() {
th.Editor("id").Layout(gtx, idEd)
})
for _, e := range idEd.Events(gtx) {
switch e.(type) {
case widget.SubmitEvent:
log(Info, "Submit")
store.Id = idEd.Text()
page = listPage
}
}
c5 = flex.Rigid(gtx, func() {
idSubmitBtn.Layout(gtx)
})
if idSubmitBtn.Clicked() {
store.Id = idEd.Text()
page = listPage
}
c6 = flex.Rigid(gtx, func() {
noidLabel.Layout(gtx)
})
} else {
c3 = flex.Rigid(gtx, func() { })
c4 = flex.Rigid(gtx, func() { })
c5 = flex.Rigid(gtx, func() { })
c6 = flex.Rigid(gtx, func() { })
}
c7 = flex.Rigid(gtx, func() {
if len(idBtns) > 0 { // still zero after update
for i := 0; i < len(idBtns); i++ { for i := 0; i < len(idBtns); i++ {
lst.Layout(gtx, len(idBtns), func(i int) { lst.Layout(gtx, len(idBtns), func(i int) {
idBtns[i].Layout(gtx) idBtns[i].Layout(gtx, family)
}) })
} }
for _, btn := range idBtns { for _, btn := range idBtns {
@ -558,22 +487,22 @@ func eventLoop() {
} }
} }
}) })
flex.Layout(gtx, c1, c2, c3, c4, c5, c6, c7) flex.Layout(gtx, c1, c2, c3)
} }
var insName, insValue string var insName, insValue string
genBtn := &SelButton{SelColor: gray} genBtn := &SelButton{SelColor: gray}
genBtn.Button = Button{Size: unit.Sp(fontSize), Label: "generate"} genBtn.Button = Button{Size: unit.Sp(16), Label: "generate"}
symBtn := &SelButton{SelColor: gray} symBtn := &SelButton{SelColor: gray}
numBtn := &SelButton{SelColor: gray} numBtn := &SelButton{SelColor: gray}
symBtn.Button = Button{Size: unit.Sp(fontSize), Label: "@"} symBtn.Button = Button{Size: unit.Sp(16), Label: "@"}
numBtn.Button = Button{Size: unit.Sp(fontSize), Label: "#"} numBtn.Button = Button{Size: unit.Sp(16), Label: "#"}
symBtn.Select() symBtn.Select()
numBtn.Select() numBtn.Select()
lenEd := &widget.Editor{ SingleLine: true, Alignment: text.End } lenEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Alignment: text.End}
lenEd.SetText("15") lenEd.SetText("15")
lBtn := &Button{Size: unit.Sp(fontSize), Label: "<", Background: gray} lBtn := &Button{Size: unit.Sp(16), Label: "<", Background: gray}
rBtn := &Button{Size: unit.Sp(fontSize), Label: ">", Background: gray} rBtn := &Button{Size: unit.Sp(16), Label: ">", Background: gray}
updatePw := func() { updatePw := func() {
if !genBtn.Selected { if !genBtn.Selected {
@ -597,25 +526,25 @@ func eventLoop() {
} }
insertPage = func() { insertPage = func() {
c2 := flex.Rigid(gtx, func() { insertLabel.Layout(gtx) }) c2 := flex.Rigid(gtx, func() { insertLabel.Layout(gtx, family) })
c3 := flex.Rigid(gtx, func() { passnameLabel.Layout(gtx) }) c3 := flex.Rigid(gtx, func() { passnameLabel.Layout(gtx, family) })
c4 := flex.Rigid(gtx, func() { th.Editor("name").Layout(gtx, passnameEd) }) c4 := flex.Rigid(gtx, func() { passnameEd.Layout(gtx) })
c5 := flex.Rigid(gtx, func() { passvalLabel.Layout(gtx) }) c5 := flex.Rigid(gtx, func() { passvalLabel.Layout(gtx, family) })
c6 := flex.Rigid(gtx, func() { th.Editor("password").Layout(gtx, passvalEd) }) c6 := flex.Rigid(gtx, func() { passvalEd.Layout(gtx) })
btnflx := &layout.Flex{Axis: layout.Horizontal} btnflx := &layout.Flex{Axis: layout.Horizontal}
al := layout.Align(layout.E) al := layout.Align(layout.E)
c7 := flex.Rigid(gtx, func() { c7 := flex.Rigid(gtx, func() {
bc1 := btnflx.Rigid(gtx, func() { lBtn.Layout(gtx) }) bc1 := btnflx.Rigid(gtx, func() { lBtn.Layout(gtx, family) })
bc2 := btnflx.Rigid(gtx, func() { bc2 := btnflx.Rigid(gtx, func() {
gtx.Constraints.Width.Min = 60 gtx.Constraints.Width.Min = 60
th.Editor("len").Layout(gtx, lenEd) lenEd.Layout(gtx)
}) })
bc3 := btnflx.Rigid(gtx, func() { rBtn.Layout(gtx) }) bc3 := btnflx.Rigid(gtx, func() { rBtn.Layout(gtx, family) })
bc4 := btnflx.Rigid(gtx, func() { symBtn.Layout(gtx) }) bc4 := btnflx.Rigid(gtx, func() { symBtn.Layout(gtx, family) })
bc5 := btnflx.Rigid(gtx, func() { numBtn.Layout(gtx) }) bc5 := btnflx.Rigid(gtx, func() { numBtn.Layout(gtx, family) })
bc6 := btnflx.Rigid(gtx, func() { genBtn.Layout(gtx) }) bc6 := btnflx.Rigid(gtx, func() { genBtn.Layout(gtx, family) })
al.Layout(gtx, func() { al.Layout(gtx, func() {
btnflx.Layout(gtx, bc1, bc2, bc3, bc4, bc5, bc6) btnflx.Layout(gtx, bc1, bc2, bc3, bc4, bc5, bc6)
@ -623,8 +552,8 @@ func eventLoop() {
}) })
c8 := flex.Rigid(gtx, func() { c8 := flex.Rigid(gtx, func() {
bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx) }) bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx, family) })
bc2 := btnflx.Rigid(gtx, func() { saveBtn.Layout(gtx) }) bc2 := btnflx.Rigid(gtx, func() { saveBtn.Layout(gtx, family) })
al.Layout(gtx, func() { al.Layout(gtx, func() {
btnflx.Layout(gtx, bc1, bc2) btnflx.Layout(gtx, bc1, bc2)
}) })
@ -676,25 +605,19 @@ func eventLoop() {
return return
} }
} }
//Do not block the UI thread. store.Insert(passnameEd.Text(), passvalEd.Text())
go func() {
err := store.Insert(passnameEd.Text(), passvalEd.Text())
if err != nil {
page = idPage
}
}()
} }
} }
confirmPage = func() { confirmPage = func() {
c2 := flex.Rigid(gtx, func() { c2 := flex.Rigid(gtx, func() {
confirmLabel.Layout(gtx) confirmLabel.Layout(gtx, family)
}) })
al := layout.Align(layout.E) al := layout.Align(layout.E)
btnflx := &layout.Flex{Axis: layout.Horizontal} btnflx := &layout.Flex{Axis: layout.Horizontal}
c3 := flex.Rigid(gtx, func() { c3 := flex.Rigid(gtx, func() {
bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx) }) bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx, family) })
bc2 := btnflx.Rigid(gtx, func() { yesBtn.Layout(gtx) }) bc2 := btnflx.Rigid(gtx, func() { yesBtn.Layout(gtx, family) })
al.Layout(gtx, func() { al.Layout(gtx, func() {
btnflx.Layout(gtx, bc1, bc2) btnflx.Layout(gtx, bc1, bc2)
@ -709,27 +632,22 @@ func eventLoop() {
if yesBtn.Clicked() { if yesBtn.Clicked() {
w.Invalidate() w.Invalidate()
page = listPage page = listPage
go func() { store.Insert(insName, insValue)
err := store.Insert(insName, insValue)
if err != nil {
page = idPage
}
}()
} }
} }
confPage = func() { confPage = func() {
c2 := flex.Rigid(gtx, func() { storeDirLabel.Layout(gtx) }) c2 := flex.Rigid(gtx, func() { storeDirLabel.Layout(gtx, family) })
c3 := flex.Rigid(gtx, func() { th.Editor("directory").Layout(gtx, storeDirEd) }) c3 := flex.Rigid(gtx, func() { storeDirEd.Layout(gtx) })
al := layout.Align(layout.E) al := layout.Align(layout.E)
c4 := flex.Rigid(gtx, func() { c4 := flex.Rigid(gtx, func() {
btnflx := &layout.Flex{Axis: layout.Horizontal} btnflx := &layout.Flex{Axis: layout.Horizontal}
bc1 := btnflx.Rigid(gtx, func() { bc1 := btnflx.Rigid(gtx, func() {
backBtn.Layout(gtx) backBtn.Layout(gtx, family)
}) })
bc2 := btnflx.Rigid(gtx, func() { bc2 := btnflx.Rigid(gtx, func() {
saveBtn.Layout(gtx) saveBtn.Layout(gtx, family)
}) })
al.Layout(gtx, func() { al.Layout(gtx, func() {
btnflx.Layout(gtx, bc1, bc2) btnflx.Layout(gtx, bc1, bc2)
@ -761,23 +679,23 @@ func eventLoop() {
promptPage = func() { promptPage = func() {
submit := false submit := false
for _, e := range promptEd.Events(gtx) { for e, ok := promptEd.Event(gtx); ok; e, ok = promptEd.Event(gtx) {
switch e.(type) { switch e.(type) {
case widget.SubmitEvent: case text.SubmitEvent:
log(Info, "Submit") log(Info, "Submit")
submit = true submit = true
} }
} }
c2 := flex.Rigid(gtx, func() { promptLabel.Layout(gtx) }) c2 := flex.Rigid(gtx, func() { promptLabel.Layout(gtx, family) })
c3 := flex.Rigid(gtx, func() { th.Editor("password").Layout(gtx, promptEd) }) c3 := flex.Rigid(gtx, func() { promptEd.Layout(gtx) })
c4 := flex.Rigid(gtx, func() { c4 := flex.Rigid(gtx, func() {
al := layout.Align(layout.E) al := layout.Align(layout.E)
btnflx := &layout.Flex{Axis: layout.Horizontal} btnflx := &layout.Flex{Axis: layout.Horizontal}
bc1 := btnflx.Rigid(gtx, func() { bc1 := btnflx.Rigid(gtx, func() {
backBtn.Layout(gtx) backBtn.Layout(gtx, family)
}) })
bc2 := btnflx.Rigid(gtx, func() { bc2 := btnflx.Rigid(gtx, func() {
okBtn.Layout(gtx) okBtn.Layout(gtx, family)
}) })
al.Layout(gtx, func() { al.Layout(gtx, func() {
btnflx.Layout(gtx, bc1, bc2) btnflx.Layout(gtx, bc1, bc2)
@ -823,17 +741,11 @@ func eventLoop() {
mallocs = ms.Mallocs mallocs = ms.Mallocs
} }
switch e := e.(type) { switch e := e.(type) {
case system.DestroyEvent: case app.DestroyEvent:
return return
case system.StageEvent: case app.UpdateEvent:
if e.Stage == system.StageRunning { fmt.Printf("UpdateEvent\n")
initPgp(w) gtx.Reset(&e.Config, e.Size)
}
go func() {
updateIdBtns()
}()
case system.FrameEvent:
gtx.Reset(e.Config, e.Size)
sysinset.Top = e.Insets.Top sysinset.Top = e.Insets.Top
sysinset.Bottom = e.Insets.Bottom sysinset.Bottom = e.Insets.Bottom
@ -845,14 +757,14 @@ func eventLoop() {
margincs = gtx.Constraints margincs = gtx.Constraints
c1 = flex.Rigid(gtx, func() { c1 = flex.Rigid(gtx, func() {
ct2 := titleflex.Rigid(gtx, func() { ct2 := titleflex.Rigid(gtx, func() {
plusBtn.Layout(gtx) plusBtn.Layout(gtx, family)
}) })
ct3 := titleflex.Rigid(gtx, func() { ct3 := titleflex.Rigid(gtx, func() {
dotsBtn.Layout(gtx) dotsBtn.Layout(gtx, family)
}) })
ct1 := titleflex.Flex(gtx, 1.0, func() { ct1 := titleflex.Flex(gtx, 1.0, func() {
gtx.Constraints.Width.Min = gtx.Constraints.Width.Max gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
title.Layout(gtx) title.Layout(gtx, family)
}) })
titleflex.Layout(gtx, ct1, ct2, ct3) titleflex.Layout(gtx, ct1, ct2, ct3)
}) })
@ -872,7 +784,7 @@ func eventLoop() {
insName, insValue = "", "" insName, insValue = "", ""
page = insertPage page = insertPage
} }
e.Frame(gtx.Ops) w.Update(gtx.Ops)
} }
if x == 100 { if x == 100 {
x = 0 x = 0

View File

@ -8,7 +8,7 @@ import (
"gioui.org/gesture" "gioui.org/gesture"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op/clip" "gioui.org/op"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/text" "gioui.org/text"
"gioui.org/unit" "gioui.org/unit"
@ -30,16 +30,22 @@ type Overlay struct {
Alignment text.Alignment Alignment text.Alignment
} }
func (b *Overlay) Layout(gtx *layout.Context) { func (b *Overlay) Layout(gtx *layout.Context, family text.Family) {
ins := layout.UniformInset(unit.Dp(1)) ins := layout.UniformInset(unit.Dp(1))
ins.Layout(gtx, func() { ins.Layout(gtx, func() {
st := layout.Stack{} st := layout.Stack{}
c2 := st.Rigid(gtx, func() { c2 := st.Rigid(gtx, func() {
l := th.Label(b.Size, b.Text) l := text.Label{
Size: b.Size,
Text: b.Text,
Alignment: b.Alignment,
}
ins := layout.UniformInset(unit.Dp(4)) ins := layout.UniformInset(unit.Dp(4))
l.Color = b.Color l.Material.Record(gtx.Ops)
paint.ColorOp{Color: b.Color}.Add(gtx.Ops)
l.Material.Stop()
ins.Layout(gtx, func() { ins.Layout(gtx, func() {
l.Layout(gtx) l.Layout(gtx, family)
}) })
pointer.RectAreaOp{image.Rect(0, 0, gtx.Dimensions.Size.X, gtx.Dimensions.Size.Y)}.Add(gtx.Ops) pointer.RectAreaOp{image.Rect(0, 0, gtx.Dimensions.Size.X, gtx.Dimensions.Size.Y)}.Add(gtx.Ops)
}) })
@ -70,17 +76,30 @@ func layoutRRect(col color.RGBA, gtx *layout.Context) {
r := float32(gtx.Config.Px(unit.Dp(4))) r := float32(gtx.Config.Px(unit.Dp(4)))
sz := image.Point{X: gtx.Constraints.Width.Min, Y: gtx.Constraints.Height.Min} sz := image.Point{X: gtx.Constraints.Width.Min, Y: gtx.Constraints.Height.Min}
w, h := float32(sz.X), float32(sz.Y) w, h := float32(sz.X), float32(sz.Y)
rect := f32.Rectangle{ rrect(gtx.Ops, w, h, r, r, r, r)
f32.Point{0, 0},
f32.Point{w, h},
}
clip.RoundRect(gtx.Ops, rect, r, r, r, r)
paint.ColorOp{Color: col}.Add(gtx.Ops) paint.ColorOp{Color: col}.Add(gtx.Ops)
paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: w, Y: h}}}.Add(gtx.Ops) paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: w, Y: h}}}.Add(gtx.Ops)
gtx.Dimensions = layout.Dimensions{Size: sz} gtx.Dimensions = layout.Dimensions{Size: sz}
} }
func (b *Button) Layout(gtx *layout.Context) { // https://pomax.github.io/bezierinfo/#circles_cubic.
func rrect(ops *op.Ops, width, height, se, sw, nw, ne float32) {
w, h := float32(width), float32(height)
const c = 0.55228475 // 4*(sqrt(2)-1)/3
var b paint.Path
b.Begin(ops)
b.Move(f32.Point{X: w, Y: h - se})
b.Cube(f32.Point{X: 0, Y: se * c}, f32.Point{X: -se + se*c, Y: se}, f32.Point{X: -se, Y: se}) // SE
b.Line(f32.Point{X: sw - w + se, Y: 0})
b.Cube(f32.Point{X: -sw * c, Y: 0}, f32.Point{X: -sw, Y: -sw + sw*c}, f32.Point{X: -sw, Y: -sw}) // SW
b.Line(f32.Point{X: 0, Y: nw - h + sw})
b.Cube(f32.Point{X: 0, Y: -nw * c}, f32.Point{X: nw - nw*c, Y: -nw}, f32.Point{X: nw, Y: -nw}) // NW
b.Line(f32.Point{X: w - ne - nw, Y: 0})
b.Cube(f32.Point{X: ne * c, Y: 0}, f32.Point{X: ne, Y: ne - ne*c}, f32.Point{X: ne, Y: ne}) // NE
b.End()
}
func (b *Button) Layout(gtx *layout.Context, family text.Family) {
b.clicked = false b.clicked = false
for _, ev := range b.Click.Events(gtx) { for _, ev := range b.Click.Events(gtx) {
if ev.Type == gesture.TypeClick { if ev.Type == gesture.TypeClick {
@ -91,11 +110,15 @@ func (b *Button) Layout(gtx *layout.Context) {
ins.Layout(gtx, func() { ins.Layout(gtx, func() {
st := layout.Stack{} st := layout.Stack{}
c2 := st.Rigid(gtx, func() { c2 := st.Rigid(gtx, func() {
l := th.Label(b.Size, b.Label) l := text.Label{
Size: b.Size,
Text: b.Label,
Alignment: b.Alignment,
}
ins := layout.UniformInset(unit.Dp(4)) ins := layout.UniformInset(unit.Dp(4))
//paint.ColorOp{Color: b.Color}.Add(ops) //paint.ColorOp{Color: b.Color}.Add(ops)
ins.Layout(gtx, func() { ins.Layout(gtx, func() {
l.Layout(gtx) l.Layout(gtx, family)
}) })
pointer.RectAreaOp{image.Rect(0, 0, gtx.Dimensions.Size.X, gtx.Dimensions.Size.Y)}.Add(gtx.Ops) pointer.RectAreaOp{image.Rect(0, 0, gtx.Dimensions.Size.X, gtx.Dimensions.Size.Y)}.Add(gtx.Ops)
b.Click.Add(gtx.Ops) b.Click.Add(gtx.Ops)

14
go.mod Normal file
View File

@ -0,0 +1,14 @@
module git.wow.st/gmp/passgo
go 1.12
require (
gioui.org v0.0.0-20191008122509-6bc55d66c2fe
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5
git.wow.st/gmp/rand v0.0.0-20191001220155-a81bebfaf8b0
github.com/fsnotify/fsnotify v1.4.7
github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a
gopkg.in/yaml.v2 v2.2.2
)

40
go.sum Normal file
View File

@ -0,0 +1,40 @@
gioui.org v0.0.0-20191002081212-2a1c203863c3 h1:4ROVjhDrWa/J/O4KbtbCItYHhWnFWbmTyHPrfBeeG0w=
gioui.org v0.0.0-20191002081212-2a1c203863c3/go.mod h1:+CEjc9B//HrBfWsQOVxjCyih7HGIj3Pww1xFHVDZyyk=
gioui.org v0.0.0-20191002205528-32bda106e7ff h1:pLleCPnIUzrPrjM+kad+jVhU31lzoEfINETZkLcTMRQ=
gioui.org v0.0.0-20191002205528-32bda106e7ff/go.mod h1:+CEjc9B//HrBfWsQOVxjCyih7HGIj3Pww1xFHVDZyyk=
gioui.org v0.0.0-20191002212946-96d3d74cb33a h1:+VTwpWGLLyLXICY3wn4LQlSLXpwX06PenIutOxYqe+M=
gioui.org v0.0.0-20191002212946-96d3d74cb33a/go.mod h1:+CEjc9B//HrBfWsQOVxjCyih7HGIj3Pww1xFHVDZyyk=
gioui.org v0.0.0-20191008122509-6bc55d66c2fe h1:N29sMDb1EoJNXSTxGWWb1i+3GymQXbbwLiRc7bEW450=
gioui.org v0.0.0-20191008122509-6bc55d66c2fe/go.mod h1:+CEjc9B//HrBfWsQOVxjCyih7HGIj3Pww1xFHVDZyyk=
gioui.org/ui v0.0.0-20190918172808-816f0e901fc6 h1:LvHEYxyOW7g+PhOiAm8Delc3AUv9EH219oDvaUMeKBw=
gioui.org/ui v0.0.0-20190918172808-816f0e901fc6/go.mod h1:PssKPKlqVIeyaed+0w492Xc2NgX5M3n6oZKOAj5rxoE=
gioui.org/ui v0.0.0-20190926171558-ce74bc0cbaea h1:rv21Wx1Inf27NY453rrrP6tuEG65PyWPhnP2Jx+Qb8k=
gioui.org/ui v0.0.0-20190926171558-ce74bc0cbaea/go.mod h1:PssKPKlqVIeyaed+0w492Xc2NgX5M3n6oZKOAj5rxoE=
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5 h1:OKeTjZST+/TKvtdA258NXJH+/gIx/xwyZxKrAezNFvk=
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5/go.mod h1:NLdpaBoMQNFqncwP8OVRNWUDw1Kt9XWm3snfT7cXu24=
git.wow.st/gmp/rand v0.0.0-20191001220006-66bfa936ad6b h1:yu3SnygEUgkNJ3xBXLkWHws7tki5qveZ6Kk7ybWj1GQ=
git.wow.st/gmp/rand v0.0.0-20191001220006-66bfa936ad6b/go.mod h1:8+2Gwnrpc5yuk8Wp6cBhxvGcNLumYiPbQ7n0SQ8h29A=
git.wow.st/gmp/rand v0.0.0-20191001220155-a81bebfaf8b0 h1:08wP00wvbDINsct1fzKV1xGGLvvtNsSb2X4CtIdpBzM=
git.wow.st/gmp/rand v0.0.0-20191001220155-a81bebfaf8b0/go.mod h1:8+2Gwnrpc5yuk8Wp6cBhxvGcNLumYiPbQ7n0SQ8h29A=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c h1:DCnjNrPDSEslcqqBgcZBxlLUIhk2elQVyf2V+HkyxJI=
github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c/go.mod h1:vdJ2op9pzpbH8CbpYKYBD6zjURqDY13PmnVn2I/uYBs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,98 +1,24 @@
//+build android //+build android
//go:generate mkdir -p classes
//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar -classpath openpgp-api.jar -d classes PgpConnect.java
//go:generate jar cf PgpConnect.jar -C classes .
//go:generate rm -rf classes
package passgo package passgo
/*
#import <jni.h>
#import "jni_android.h"
*/
import "C"
import ( import (
"fmt" "fmt"
"io/ioutil"
"path"
"log"
"strings"
"sync"
"gioui.org/app"
_ "gioui.org/app/permission/storage"
) )
var (
h *app.Handle
waitch chan struct{}
w *app.Window
pgp PGP
InitPgpOnce sync.Once
)
func init() {
waitch = make(chan struct{})
}
//export installComplete
func installComplete(env *C.JNIEnv, class C.jclass, p C.jobject) {
log.Printf("InstallComplete()")
pgp = PGP(C.NewGlobalRef(env, p))
close(waitch)
}
func InitPgp(x *app.Window) {
InitPgpOnce.Do(func() {
log.Printf("InitPgp()")
w = x
h = app.PlatformHandle()
SetJVM(h.JVM, h.Context)
RunInJVM(func(env *JNIEnv) {
C.InitPgpConnect(env)
})
w.RegisterDelegate("st/wow/git/passgo/PgpConnect")
})
}
func stopPgp() {
waitch = make(chan struct{})
w = nil
}
func connect() {
<-waitch
}
//Clip copies a string to the clipboard //Clip copies a string to the clipboard
func Clip(x string) { func Clip(x string) {
pgp.Clip(x) fmt.Println("Clipboard not implemented for this platform")
} }
func (s *Store) nativeDecrypt(name string) (string, error) { func (s *Store) nativeDecrypt(name string) (string, error) {
connect() return "", fmt.Errorf("NOT IMPLEMENTED")
data, err := ioutil.ReadFile(path.Join(s.Dir, strings.Join([]string{name, ".gpg"}, "")))
if err != nil {
return "", fmt.Errorf("Error reading file: %s", err)
}
ret, err := pgp.Decrypt(string(data))
return ret, err
} }
func (s *Store) nativeEncrypt(pw string) ([]byte, error) { func (s *Store) nativeEncrypt(pw string) ([]byte, error) {
connect() return nil, fmt.Errorf("NOT IMPLEMENTED")
ret, err := pgp.Encrypt(s.Id, pw)
return []byte(ret), err
} }
func nativeIdentities() ([]string, error) { func nativeIdentities() ([]string, error) {
//NOT IMPLEMENTED ON ANDROID return nil, fmt.Errorf("NOT IMPLEMENTED")
return []string{}, nil
log.Printf("nativeIdentities()")
connect()
log.Printf("calling pgp.GetId()")
id, err := pgp.GetId()
log.Printf("pgp.GetId() returned")
return []string{id}, err
} }

View File

@ -1,146 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <jni.h>
#include <android/log.h>
#include "jni_android.h"
#include "_cgo_export.h"
static jobject gClassLoader;
static jmethodID gFindClassMethod;
void
SetLoader(JNIEnv* env, jobject context) {
jclass cclass = (*env)->GetObjectClass(env, context);
jmethodID gcl_id = (*env)->GetMethodID(env, cclass, "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = (*env)->CallObjectMethod(env, context, gcl_id);
gClassLoader = (*env)->NewGlobalRef(env, loader);
jclass lclass = (*env)->GetObjectClass(env, loader);
gFindClassMethod = (*env)->GetMethodID(env, lclass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
}
jclass
FindClass(JNIEnv* env, char* name) {
jstring strClassName = (*env)->NewStringUTF(env, name);
return (*env)->CallObjectMethod(env, gClassLoader, gFindClassMethod, strClassName);
}
jobject
CreateObject(JNIEnv* env, jclass cls) {
jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "()V");
return (*env)->NewObject(env, cls, init);
}
void
InitPgpConnect(JNIEnv* env) {
jclass cls = FindClass(env, "st/wow/git/passgo/PgpConnect");
printf("class = %p", cls);
static const JNINativeMethod actMethods[] = {
{
.name = "activityResult",
.signature = "(II)V",
.fnPtr = activityResult
},
{
.name = "stringResult",
.signature = "(ILjava/lang/String;)V",
.fnPtr = stringResult
},
{
.name = "installComplete",
.signature = "(Lst/wow/git/passgo/PgpConnect;)V",
.fnPtr = installComplete
},
};
(*env)->RegisterNatives(env, cls, actMethods, sizeof(actMethods)/sizeof(actMethods[0]));
}
void
GetId(JNIEnv* env, jobject p, int chint) {
jclass cls = (*env)->GetObjectClass(env, p);
printf("GetId(): cls = %p", cls);
jmethodID mid = (*env)->GetMethodID(env, cls, "GetId", "(I)V");
printf("GetId(): mid = %p", mid);
(*env)->CallObjectMethod(env, p, mid, chint);
}
void
Decrypt(JNIEnv* env, jobject p, char* cdata, int datalen, int chint) {
jbyteArray data = (*env)->NewByteArray(env, datalen);
if (datalen > 0) {
(*env)->SetByteArrayRegion(env, data, 0, datalen, cdata);
}
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "Decrypt", "([BI)V");
(*env)->CallVoidMethod(env, p, mid, data, chint);
}
void stringResult(JNIEnv* env, jclass cls, jint requestCode, jobject response) {
if (response == 0) {
goStringResult(env, cls, requestCode, 0);
} else {
char* str = (*env)->GetStringUTFChars(env, response, NULL);
goStringResult(env, cls, requestCode, str);
}
}
void
Encrypt(JNIEnv* env, jobject p, char* cid, int idlen, char* cdata, int datalen, int chint) {
jbyteArray id = (*env)->NewByteArray(env, idlen);
if (idlen > 0) {
(*env)->SetByteArrayRegion(env, id, 0, idlen, cid);
}
jbyteArray data = (*env)->NewByteArray(env, datalen);
if (datalen > 0) {
(*env)->SetByteArrayRegion(env, data, 0, datalen, cdata);
}
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "Encrypt", "([B[BI)V");
(*env)->CallVoidMethod(env, p, mid, id, data, chint);
}
void
Clip(JNIEnv* env, jobject p, char* cdata, int datalen) {
jbyteArray data = (*env)->NewByteArray(env, datalen);
if (datalen > 0) {
(*env)->SetByteArrayRegion(env, data, 0, datalen, cdata);
}
jclass cls = (*env)->GetObjectClass(env, p);
jmethodID mid = (*env)->GetMethodID(env, cls, "Clip", "([B)V");
(*env)->CallVoidMethod(env, p, mid, data);
}
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
(*env)->CallVoidMethod(env, obj, methodID);
}
void CallVoidMethod1(JNIEnv *env, jobject obj, jmethodID methodID, jobject arg) {
(*env)->CallVoidMethod(env, obj, methodID, arg);
}
jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
return (*env)->CallIntMethod(env, obj, methodID);
}
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
return (*env)->GetMethodID(env, clazz, name, sig);
}
jint GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
return (*vm)->GetEnv(vm, (void **)env, version);
}
jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
}
jint DetachCurrentThread(JavaVM *vm) {
return (*vm)->DetachCurrentThread(vm);
}
jobject
NewGlobalRef(JNIEnv *env, jobject o) {
return (*env)->NewGlobalRef(env, o);
}

View File

@ -1,195 +0,0 @@
package passgo
/*
#cgo LDFLAGS: -landroid -llog
#include <stdlib.h>
#include "jni_android.h"
*/
import "C"
import (
"errors"
"fmt"
"log"
"runtime"
"unsafe"
"sync"
)
var theJVM *C.JavaVM
type JNIEnv = C.JNIEnv
func SetJVM(jvm, context uintptr) {
log.Print("set theJVM")
theJVM = (*C.JavaVM)(unsafe.Pointer(jvm))
RunInJVM(func(env *C.JNIEnv) {
C.SetLoader(env, (C.jobject)(context))
})
}
func FindClass(env *C.JNIEnv, name string) C.jclass {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return C.FindClass((*C.JNIEnv)(env), cname)
}
func JniCallVoidMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) {
C.CallVoidMethod(env, obj, methodID)
}
func JniCallVoidMethod1(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID, arg uintptr) {
C.CallVoidMethod1(env, obj, methodID, C.jobject(unsafe.Pointer(arg)))
}
func JniCallIntMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) int {
return (int)(C.CallIntMethod(env, obj, methodID))
}
func JniGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jmethodID {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
csig := C.CString(sig)
defer C.free(unsafe.Pointer(csig))
return C.GetMethodID(env, cls, cname, csig)
}
func CreateObject(env *C.JNIEnv, cls C.jclass) C.jobject {
return C.CreateObject(env, (C.jclass)(cls))
}
type PGP C.jobject
func InitPgpConnect(env uintptr) {
C.InitPgpConnect((*C.JNIEnv)(unsafe.Pointer(env)))
}
//func NewPgpConnect(env uintptr, act uintptr) PGP {
// return (PGP)(C.NewPgpConnect((*C.JNIEnv)(unsafe.Pointer(env)), (C.jobject)(unsafe.Pointer(act))))
//}
var (
fragChans map[int]chan string
chmux sync.Mutex
chint int
)
func (p PGP) GetId() (string, error) {
ch := make(chan string)
chmux.Lock()
if fragChans == nil {
fragChans = make(map[int]chan string)
}
fragChans[chint] = ch
curint := chint
chint++
chmux.Unlock()
RunInJVM(func(env *JNIEnv) {
C.GetId(env, (C.jobject)(p), C.int(curint))
})
log.Printf("GetId(): waiting for response")
switch x := <-ch; {
case x == "":
log.Printf("GetId(): failed")
return "", fmt.Errorf("GetId failed")
default:
log.Printf("GetId(): got %s", x)
return x, nil
}
}
func (p PGP) Decrypt(data string) (string, error) {
ch := make(chan string)
chmux.Lock()
if fragChans == nil {
fragChans = make(map[int]chan string)
}
fragChans[chint] = ch
curint := chint
chint++
chmux.Unlock()
cdata := C.CString(data)
defer C.free(unsafe.Pointer(cdata))
RunInJVM(func(env *JNIEnv) {
C.Decrypt(env, (C.jobject)(p), (*C.char)(unsafe.Pointer(cdata)), (C.int)(len(data)), C.int(curint))
})
switch x := <-ch; {
case x == "":
return "", fmt.Errorf("Decryption failed")
default:
return x, nil
}
}
func (p PGP) Encrypt(id, data string) (string, error) {
ch := make(chan string)
chmux.Lock()
if fragChans == nil {
fragChans = make(map[int]chan string)
}
fragChans[chint] = ch
curint := chint
chint++
chmux.Unlock()
cid := C.CString(id)
defer C.free(unsafe.Pointer(cid))
cdata := C.CString(data)
defer C.free(unsafe.Pointer(cdata))
RunInJVM(func(env *JNIEnv) {
C.Encrypt(env, (C.jobject)(p), (*C.char)(unsafe.Pointer(cid)), (C.int)(len(id)), (*C.char)(unsafe.Pointer(cdata)), (C.int)(len(data)), C.int(curint))
})
switch x := <-ch; {
case x == "":
return "", fmt.Errorf("Encryption failed")
default:
return x, nil
}
}
func (p PGP) Clip(data string) {
cdata := C.CString(data)
defer C.free(unsafe.Pointer(cdata))
RunInJVM(func(env *JNIEnv) {
C.Clip(env, (C.jobject)(p), (*C.char)(unsafe.Pointer(cdata)), (C.int)(len(data)))
})
}
func RunInJVM(f func(env *C.JNIEnv)) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var env *C.JNIEnv
var detach bool
if res := C.GetEnv(theJVM, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
if res != C.JNI_EDETACHED {
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
}
if C.AttachCurrentThread(theJVM, &env, nil) != C.JNI_OK {
panic(errors.New("runInJVM: AttachCurrentThread failed"))
}
detach = true
}
if detach {
defer func() {
C.DetachCurrentThread(theJVM)
}()
}
f(env)
}
//export activityResult
func activityResult(env *C.JNIEnv, class C.jclass, request, result C.jint) {
log.Printf("activityResult (%d): %d", request, result)
}
//export goStringResult
func goStringResult(env *C.JNIEnv, class C.jclass, request C.jint, str *C.char) {
fragChans[int(request)] <- C.GoString(str)
C.free(unsafe.Pointer(str))
chmux.Lock()
delete(fragChans, int(request))
chmux.Unlock()
}

View File

@ -1,19 +0,0 @@
#include <jni.h>
void SetLoader(JNIEnv* env, jobject context);
jclass FindClass(JNIEnv* env, char* name);
jobject CreateObject(JNIEnv* env, jclass cls);
void InitPgpConnect(JNIEnv* env);
void GetId(JNIEnv* env, jobject p, int chint);
void Decrypt(JNIEnv* env, jobject p, char* cdata, int datalen, int chint);
void Encrypt(JNIEnv* env, jobject p, char* cid, int idlen, char* cdata, int datalen, int chint);
void Clip(JNIEnv* env, jobject p, char* cdata, int datalen);
void stringResult(JNIEnv* env, jclass cls, jint requestCode, jobject response);
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID);
void CallVoidMethod1(JNIEnv *env, jobject obj, jmethodID methodID, jobject arg);
jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID);
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jint GetEnv(JavaVM *vm, JNIEnv **env, jint version);
jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);
jint DetachCurrentThread(JavaVM *vm);
jobject NewGlobalRef(JNIEnv *env, jobject o);

17
main.go
View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"os/user" "os/user"
"path" "path"
@ -54,23 +53,10 @@ func GetStore(store *Store) error {
if err != nil { if err != nil {
return fmt.Errorf("Can't read .gpg-id: %s", err) return fmt.Errorf("Can't read .gpg-id: %s", err)
} }
// extract only the email address portion of the ID (required
// for Android)
if id[len(id)-1] == '\n' { if id[len(id)-1] == '\n' {
id = id[:len(id)-1] id = id[:len(id)-1]
} }
for i := len(id) - 1; i > 0; i-- {
if id[i] == '>' {
for j := i-1; j > 0; j-- {
if id[j] == '<' {
id = id[j+1:i]
}
}
break
}
}
store.Id = string(id) store.Id = string(id)
log.Printf("ID = %s", store.Id)
return getKeyring() return getKeyring()
} }
@ -148,8 +134,6 @@ func (s *Store) list(level int, p string) ([]Pass, error) {
fd, err := os.Open(dir) fd, err := os.Open(dir)
defer fd.Close() defer fd.Close()
if err != nil { if err != nil {
log.Printf("password store directory = %s", dir)
log.Printf("error is %s", err)
return nil, fmt.Errorf("Cannot open password store") return nil, fmt.Errorf("Cannot open password store")
} }
files, err := fd.Readdir(0) files, err := fd.Readdir(0)
@ -278,7 +262,6 @@ func (s *Store) Decrypt(name string, prompts ...func() []byte) (string, error) {
} }
func Identities() ([]string, error) { func Identities() ([]string, error) {
log.Printf("Identities()")
getKeyring() getKeyring()
if useNative { if useNative {
return nativeIdentities() return nativeIdentities()

Binary file not shown.