Compare commits
8 Commits
fdb655c671
...
23df75fd15
Author | SHA1 | Date | |
---|---|---|---|
23df75fd15 | |||
a1d9ffa6c6 | |||
47ffd8f160 | |||
8aacb243d2 | |||
ea9b4b3861 | |||
8611bfb5e0 | |||
46c76ed065 | |||
b0abb36e64 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,4 +2,4 @@ cmd/passgo/passgo
|
||||||
cmd/passgo-gui/passgo-gui
|
cmd/passgo-gui/passgo-gui
|
||||||
nohup.out
|
nohup.out
|
||||||
*.apk
|
*.apk
|
||||||
cpuprofile
|
PgpConnect.jar
|
||||||
|
|
296
PgpConnect.java
Normal file
296
PgpConnect.java
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -5,27 +5,41 @@ package main
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/image/font/gofont/goregular"
|
"git.wow.st/gmp/passgo"
|
||||||
"golang.org/x/image/font/sfnt"
|
"gioui.org/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
noidLabelText = "Enter a GPG key ID above"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log(Info, "Android start")
|
log(Info, "Android start")
|
||||||
var err error
|
// Use a larger font on Android
|
||||||
regular, err = sfnt.Parse(goregular.TTF)
|
fontSize = 24
|
||||||
if err != nil {
|
|
||||||
log(Fatal, "Cannot parse default font: ", err)
|
|
||||||
}
|
}
|
||||||
confDir = app.DataDir()
|
|
||||||
if _, err := os.Stat(confDir); os.IsNotExist(err) {
|
func initPgp(w *app.Window) {
|
||||||
err = os.MkdirAll(confDir, 0700)
|
passgo.InitPgp(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfDir() (string, error) {
|
||||||
|
ret, err := app.DataDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Info, "Cannot create configuration directory ", confDir)
|
log(Error, "Cannot get data directory:", err)
|
||||||
log(Fatal, err)
|
return "", err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(ret); os.IsNotExist(err) {
|
||||||
|
err = os.MkdirAll(ret, 0700)
|
||||||
|
if err != nil {
|
||||||
|
log(Error, "Cannot create configuration directory ", ret)
|
||||||
|
return "", 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,14 @@ import (
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"golang.org/x/image/font/gofont/goregular"
|
"gioui.org/app"
|
||||||
"golang.org/x/image/font/sfnt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setFont() error {
|
var (
|
||||||
|
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.")
|
||||||
|
@ -29,28 +32,38 @@ func setFont() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}*/
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
err := setFont()
|
fontSize = 16
|
||||||
if err != nil {
|
//err := setFont()
|
||||||
regular, err = sfnt.Parse(goregular.TTF)
|
//if err != nil {
|
||||||
if err != nil {
|
// regular, err = sfnt.Parse(goregular.TTF)
|
||||||
log(Fatal, "Cannot parse default font: ", err)
|
// if err != nil {
|
||||||
|
// log(Fatal, "Cannot parse default font: ", err)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initPgp(w *app.Window) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getConfDir() (string, error) {
|
||||||
usr, err := user.Current()
|
usr, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Fatal, "Cannot get current user: ", err)
|
log(Error, "Cannot get current user: ", err)
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
confDir = path.Join(usr.HomeDir, ".config/passgo")
|
ret := path.Join(usr.HomeDir, ".config/passgo")
|
||||||
if _, err := os.Stat(confDir); os.IsNotExist(err) {
|
if _, err := os.Stat(ret); os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(confDir, 0700)
|
err = os.MkdirAll(ret, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(Info, "Cannot create configuration directory ", confDir)
|
log(Info, "Cannot create configuration directory ", confDir)
|
||||||
log(Fatal, err)
|
return "", err
|
||||||
} else {
|
} else {
|
||||||
log(Info, "Configuration directory created")
|
log(Info, "Configuration directory created")
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,14 @@ 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"
|
||||||
|
|
||||||
"golang.org/x/image/font/sfnt"
|
"gioui.org/font/gofont"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
@ -36,7 +38,7 @@ type conf struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go func() {
|
if false { 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")
|
||||||
|
@ -53,8 +55,14 @@ 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)
|
||||||
|
@ -92,7 +100,10 @@ func main() {
|
||||||
chdir = make(chan struct{})
|
chdir = make(chan struct{})
|
||||||
passch = make(chan []byte)
|
passch = make(chan []byte)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log(Info,"passgo.Identities()")
|
||||||
passgo.Identities()
|
passgo.Identities()
|
||||||
|
}()
|
||||||
go Updater()
|
go Updater()
|
||||||
log(Info, "Staring event loop")
|
log(Info, "Staring event loop")
|
||||||
go eventLoop()
|
go eventLoop()
|
||||||
|
@ -101,8 +112,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
|
||||||
|
@ -111,9 +122,11 @@ 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()
|
||||||
|
@ -160,7 +173,8 @@ 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(Fatal, "Cannot open config file: ", err)
|
log(Error, "Config file = ", path.Join(confDir, "config.yml"))
|
||||||
|
log(Fatal, "Cannot open config file: ", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
@ -176,22 +190,25 @@ 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 := &text.Label{Size: unit.Sp(16), Text: "passgo"}
|
title := th.Body1("passgo")
|
||||||
dotsBtn := &Button{
|
dotsBtn := &Button{
|
||||||
Size: unit.Sp(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: "\xe2\x8b\xae",
|
Label: "\xe2\x8b\xae",
|
||||||
Alignment: text.Middle,
|
Alignment: text.Middle,
|
||||||
Color: black,
|
Color: black,
|
||||||
|
@ -204,12 +221,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(16), Text: "copied to clipboard",
|
copied := &Overlay{Size: unit.Sp(fontSize), Text: "copied to clipboard",
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: darkgray,
|
Background: darkgray,
|
||||||
Alignment: text.Middle,
|
Alignment: text.Middle,
|
||||||
}
|
}
|
||||||
cleared := &Overlay{Size: unit.Sp(16), Text: "clipboard cleared",
|
cleared := &Overlay{Size: unit.Sp(fontSize), Text: "clipboard cleared",
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: darkgray,
|
Background: darkgray,
|
||||||
Alignment: text.Middle,
|
Alignment: text.Middle,
|
||||||
|
@ -229,7 +246,7 @@ func eventLoop() {
|
||||||
z = "/"
|
z = "/"
|
||||||
}
|
}
|
||||||
passBtns = append(passBtns, &Button{
|
passBtns = append(passBtns, &Button{
|
||||||
Size: unit.Sp(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: strings.Join([]string{s, n, z}, ""),
|
Label: strings.Join([]string{s, n, z}, ""),
|
||||||
Background: gray,
|
Background: gray,
|
||||||
})
|
})
|
||||||
|
@ -237,11 +254,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)
|
||||||
|
@ -250,7 +267,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(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: n,
|
Label: n,
|
||||||
Alignment: text.End,
|
Alignment: text.End,
|
||||||
Color: black,
|
Color: black,
|
||||||
|
@ -264,64 +281,64 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
confBtn := &Button{
|
confBtn := &Button{
|
||||||
Size: unit.Sp(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: "configure",
|
Label: "configure",
|
||||||
Alignment: text.Middle,
|
Alignment: text.Middle,
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
|
|
||||||
storeDirLabel := &text.Label{Size: unit.Sp(16), Text: "Store directory"}
|
storeDirLabel := th.Label(unit.Sp(fontSize), "Store directory")
|
||||||
storeDirEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true}
|
storeDirEd := &widget.Editor{ SingleLine: true}
|
||||||
storeDirEd.SetText(store.Dir)
|
storeDirEd.SetText(store.Dir)
|
||||||
saveBtn := &Button{
|
saveBtn := &Button{
|
||||||
Size: unit.Sp(16),
|
Size: unit.Sp(fontSize),
|
||||||
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(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: "back",
|
Label: "back",
|
||||||
Alignment: text.End,
|
Alignment: text.End,
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
confirmLabel := &text.Label{Size: unit.Sp(16), Text: "Password exists. Overwrite?"}
|
confirmLabel := th.Label(unit.Sp(fontSize), "Password exists. Overwrite?")
|
||||||
yesBtn := &Button{
|
yesBtn := &Button{
|
||||||
Size: unit.Sp(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: "yes",
|
Label: "yes",
|
||||||
Alignment: text.End,
|
Alignment: text.End,
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
|
|
||||||
promptLabel := &text.Label{Size: unit.Sp(16), Text: "passphrase"}
|
promptLabel := th.Label(unit.Sp(fontSize), "passphrase")
|
||||||
promptEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Submit: true}
|
promptEd := &widget.Editor{ SingleLine: true, Submit: true }
|
||||||
okBtn := &Button{
|
okBtn := &Button{
|
||||||
Size: unit.Sp(16),
|
Size: unit.Sp(fontSize),
|
||||||
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(16),
|
Size: unit.Sp(fontSize),
|
||||||
Label: "+",
|
Label: "+",
|
||||||
Alignment: text.Middle,
|
Alignment: text.Middle,
|
||||||
Color: black,
|
Color: black,
|
||||||
Background: gray,
|
Background: gray,
|
||||||
}
|
}
|
||||||
|
|
||||||
insertLabel := &text.Label{Size: unit.Sp(16), Text: "Insert"}
|
insertLabel := th.Label(unit.Sp(fontSize), "Insert")
|
||||||
passnameLabel := &text.Label{Size: unit.Sp(16), Text: "password name:"}
|
passnameLabel := th.Label(unit.Sp(fontSize), "password name:")
|
||||||
passnameEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Submit: true}
|
passnameEd := &widget.Editor{ SingleLine: true }
|
||||||
passvalLabel := &text.Label{Size: unit.Sp(16), Text: "password value:"}
|
passvalLabel := th.Label(unit.Sp(fontSize), "password value:")
|
||||||
passvalEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Submit: true}
|
passvalEd := &widget.Editor{ SingleLine: true, Submit: true }
|
||||||
|
|
||||||
noidLabel := &text.Label{Size: unit.Sp(16), Text: "No GPG ids available. Please create a private key"}
|
noidLabel := th.Label(unit.Sp(fontSize), noidLabelText)
|
||||||
idLabel := &text.Label{Size: unit.Sp(16), Text: "Select ID"}
|
idLabel := th.Label(unit.Sp(fontSize), "Select ID")
|
||||||
|
|
||||||
anim := &time.Ticker{}
|
anim := &time.Ticker{}
|
||||||
animating := false
|
animating := false
|
||||||
|
@ -338,6 +355,7 @@ 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
|
||||||
|
@ -359,20 +377,22 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case store.Empty:
|
case store.Empty:
|
||||||
confBtn.Layout(gtx, family)
|
confBtn.Layout(gtx)
|
||||||
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, family)
|
idLabel.Layout(gtx)
|
||||||
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]
|
||||||
btn.Layout(gtx, family)
|
gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
|
||||||
|
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
|
||||||
|
@ -446,7 +466,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, family)
|
overlay.Layout(gtx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if x > start2 && x < fade2a {
|
if x > start2 && x < fade2a {
|
||||||
|
@ -457,23 +477,74 @@ 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, family)
|
idLabel.Layout(gtx)
|
||||||
})
|
})
|
||||||
//if len(idBtns) == 0 {
|
var c3 layout.FlexChild
|
||||||
|
var c4 layout.FlexChild
|
||||||
|
var c5 layout.FlexChild
|
||||||
|
var c6 layout.FlexChild
|
||||||
|
var c7 layout.FlexChild
|
||||||
|
if len(idBtns) == 0 {
|
||||||
|
c3 = flex.Rigid(gtx, func() {
|
||||||
|
updateBtn.Layout(gtx)
|
||||||
|
})
|
||||||
|
if updateBtn.Clicked() {
|
||||||
updateIdBtns()
|
updateIdBtns()
|
||||||
//}
|
w.Invalidate()
|
||||||
c3 := flex.Rigid(gtx, func() {
|
}
|
||||||
if len(idBtns) == 0 { // still zero after update
|
c4 = flex.Rigid(gtx, func() {
|
||||||
noidLabel.Layout(gtx, family)
|
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 {
|
} 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, family)
|
idBtns[i].Layout(gtx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, btn := range idBtns {
|
for _, btn := range idBtns {
|
||||||
|
@ -487,22 +558,22 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
flex.Layout(gtx, c1, c2, c3)
|
flex.Layout(gtx, c1, c2, c3, c4, c5, c6, c7)
|
||||||
}
|
}
|
||||||
|
|
||||||
var insName, insValue string
|
var insName, insValue string
|
||||||
genBtn := &SelButton{SelColor: gray}
|
genBtn := &SelButton{SelColor: gray}
|
||||||
genBtn.Button = Button{Size: unit.Sp(16), Label: "generate"}
|
genBtn.Button = Button{Size: unit.Sp(fontSize), Label: "generate"}
|
||||||
symBtn := &SelButton{SelColor: gray}
|
symBtn := &SelButton{SelColor: gray}
|
||||||
numBtn := &SelButton{SelColor: gray}
|
numBtn := &SelButton{SelColor: gray}
|
||||||
symBtn.Button = Button{Size: unit.Sp(16), Label: "@"}
|
symBtn.Button = Button{Size: unit.Sp(fontSize), Label: "@"}
|
||||||
numBtn.Button = Button{Size: unit.Sp(16), Label: "#"}
|
numBtn.Button = Button{Size: unit.Sp(fontSize), Label: "#"}
|
||||||
symBtn.Select()
|
symBtn.Select()
|
||||||
numBtn.Select()
|
numBtn.Select()
|
||||||
lenEd := &text.Editor{Size: unit.Sp(16), Family: family, SingleLine: true, Alignment: text.End}
|
lenEd := &widget.Editor{ SingleLine: true, Alignment: text.End }
|
||||||
lenEd.SetText("15")
|
lenEd.SetText("15")
|
||||||
lBtn := &Button{Size: unit.Sp(16), Label: "<", Background: gray}
|
lBtn := &Button{Size: unit.Sp(fontSize), Label: "<", Background: gray}
|
||||||
rBtn := &Button{Size: unit.Sp(16), Label: ">", Background: gray}
|
rBtn := &Button{Size: unit.Sp(fontSize), Label: ">", Background: gray}
|
||||||
|
|
||||||
updatePw := func() {
|
updatePw := func() {
|
||||||
if !genBtn.Selected {
|
if !genBtn.Selected {
|
||||||
|
@ -526,25 +597,25 @@ func eventLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
insertPage = func() {
|
insertPage = func() {
|
||||||
c2 := flex.Rigid(gtx, func() { insertLabel.Layout(gtx, family) })
|
c2 := flex.Rigid(gtx, func() { insertLabel.Layout(gtx) })
|
||||||
c3 := flex.Rigid(gtx, func() { passnameLabel.Layout(gtx, family) })
|
c3 := flex.Rigid(gtx, func() { passnameLabel.Layout(gtx) })
|
||||||
c4 := flex.Rigid(gtx, func() { passnameEd.Layout(gtx) })
|
c4 := flex.Rigid(gtx, func() { th.Editor("name").Layout(gtx, passnameEd) })
|
||||||
c5 := flex.Rigid(gtx, func() { passvalLabel.Layout(gtx, family) })
|
c5 := flex.Rigid(gtx, func() { passvalLabel.Layout(gtx) })
|
||||||
c6 := flex.Rigid(gtx, func() { passvalEd.Layout(gtx) })
|
c6 := flex.Rigid(gtx, func() { th.Editor("password").Layout(gtx, passvalEd) })
|
||||||
|
|
||||||
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, family) })
|
bc1 := btnflx.Rigid(gtx, func() { lBtn.Layout(gtx) })
|
||||||
bc2 := btnflx.Rigid(gtx, func() {
|
bc2 := btnflx.Rigid(gtx, func() {
|
||||||
gtx.Constraints.Width.Min = 60
|
gtx.Constraints.Width.Min = 60
|
||||||
lenEd.Layout(gtx)
|
th.Editor("len").Layout(gtx, lenEd)
|
||||||
})
|
})
|
||||||
|
|
||||||
bc3 := btnflx.Rigid(gtx, func() { rBtn.Layout(gtx, family) })
|
bc3 := btnflx.Rigid(gtx, func() { rBtn.Layout(gtx) })
|
||||||
bc4 := btnflx.Rigid(gtx, func() { symBtn.Layout(gtx, family) })
|
bc4 := btnflx.Rigid(gtx, func() { symBtn.Layout(gtx) })
|
||||||
bc5 := btnflx.Rigid(gtx, func() { numBtn.Layout(gtx, family) })
|
bc5 := btnflx.Rigid(gtx, func() { numBtn.Layout(gtx) })
|
||||||
bc6 := btnflx.Rigid(gtx, func() { genBtn.Layout(gtx, family) })
|
bc6 := btnflx.Rigid(gtx, func() { genBtn.Layout(gtx) })
|
||||||
|
|
||||||
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)
|
||||||
|
@ -552,8 +623,8 @@ func eventLoop() {
|
||||||
})
|
})
|
||||||
|
|
||||||
c8 := flex.Rigid(gtx, func() {
|
c8 := flex.Rigid(gtx, func() {
|
||||||
bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx, family) })
|
bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx) })
|
||||||
bc2 := btnflx.Rigid(gtx, func() { saveBtn.Layout(gtx, family) })
|
bc2 := btnflx.Rigid(gtx, func() { saveBtn.Layout(gtx) })
|
||||||
al.Layout(gtx, func() {
|
al.Layout(gtx, func() {
|
||||||
btnflx.Layout(gtx, bc1, bc2)
|
btnflx.Layout(gtx, bc1, bc2)
|
||||||
})
|
})
|
||||||
|
@ -605,19 +676,25 @@ func eventLoop() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
store.Insert(passnameEd.Text(), passvalEd.Text())
|
//Do not block the UI thread.
|
||||||
|
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, family)
|
confirmLabel.Layout(gtx)
|
||||||
})
|
})
|
||||||
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, family) })
|
bc1 := btnflx.Rigid(gtx, func() { backBtn.Layout(gtx) })
|
||||||
bc2 := btnflx.Rigid(gtx, func() { yesBtn.Layout(gtx, family) })
|
bc2 := btnflx.Rigid(gtx, func() { yesBtn.Layout(gtx) })
|
||||||
|
|
||||||
al.Layout(gtx, func() {
|
al.Layout(gtx, func() {
|
||||||
btnflx.Layout(gtx, bc1, bc2)
|
btnflx.Layout(gtx, bc1, bc2)
|
||||||
|
@ -632,22 +709,27 @@ func eventLoop() {
|
||||||
if yesBtn.Clicked() {
|
if yesBtn.Clicked() {
|
||||||
w.Invalidate()
|
w.Invalidate()
|
||||||
page = listPage
|
page = listPage
|
||||||
store.Insert(insName, insValue)
|
go func() {
|
||||||
|
err := store.Insert(insName, insValue)
|
||||||
|
if err != nil {
|
||||||
|
page = idPage
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
confPage = func() {
|
confPage = func() {
|
||||||
c2 := flex.Rigid(gtx, func() { storeDirLabel.Layout(gtx, family) })
|
c2 := flex.Rigid(gtx, func() { storeDirLabel.Layout(gtx) })
|
||||||
c3 := flex.Rigid(gtx, func() { storeDirEd.Layout(gtx) })
|
c3 := flex.Rigid(gtx, func() { th.Editor("directory").Layout(gtx, storeDirEd) })
|
||||||
|
|
||||||
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, family)
|
backBtn.Layout(gtx)
|
||||||
})
|
})
|
||||||
bc2 := btnflx.Rigid(gtx, func() {
|
bc2 := btnflx.Rigid(gtx, func() {
|
||||||
saveBtn.Layout(gtx, family)
|
saveBtn.Layout(gtx)
|
||||||
})
|
})
|
||||||
al.Layout(gtx, func() {
|
al.Layout(gtx, func() {
|
||||||
btnflx.Layout(gtx, bc1, bc2)
|
btnflx.Layout(gtx, bc1, bc2)
|
||||||
|
@ -679,23 +761,23 @@ func eventLoop() {
|
||||||
|
|
||||||
promptPage = func() {
|
promptPage = func() {
|
||||||
submit := false
|
submit := false
|
||||||
for e, ok := promptEd.Event(gtx); ok; e, ok = promptEd.Event(gtx) {
|
for _, e := range promptEd.Events(gtx) {
|
||||||
switch e.(type) {
|
switch e.(type) {
|
||||||
case text.SubmitEvent:
|
case widget.SubmitEvent:
|
||||||
log(Info, "Submit")
|
log(Info, "Submit")
|
||||||
submit = true
|
submit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c2 := flex.Rigid(gtx, func() { promptLabel.Layout(gtx, family) })
|
c2 := flex.Rigid(gtx, func() { promptLabel.Layout(gtx) })
|
||||||
c3 := flex.Rigid(gtx, func() { promptEd.Layout(gtx) })
|
c3 := flex.Rigid(gtx, func() { th.Editor("password").Layout(gtx, promptEd) })
|
||||||
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, family)
|
backBtn.Layout(gtx)
|
||||||
})
|
})
|
||||||
bc2 := btnflx.Rigid(gtx, func() {
|
bc2 := btnflx.Rigid(gtx, func() {
|
||||||
okBtn.Layout(gtx, family)
|
okBtn.Layout(gtx)
|
||||||
})
|
})
|
||||||
al.Layout(gtx, func() {
|
al.Layout(gtx, func() {
|
||||||
btnflx.Layout(gtx, bc1, bc2)
|
btnflx.Layout(gtx, bc1, bc2)
|
||||||
|
@ -741,11 +823,17 @@ func eventLoop() {
|
||||||
mallocs = ms.Mallocs
|
mallocs = ms.Mallocs
|
||||||
}
|
}
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case app.DestroyEvent:
|
case system.DestroyEvent:
|
||||||
return
|
return
|
||||||
case app.UpdateEvent:
|
case system.StageEvent:
|
||||||
fmt.Printf("UpdateEvent\n")
|
if e.Stage == system.StageRunning {
|
||||||
gtx.Reset(&e.Config, e.Size)
|
initPgp(w)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
@ -757,14 +845,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, family)
|
plusBtn.Layout(gtx)
|
||||||
})
|
})
|
||||||
ct3 := titleflex.Rigid(gtx, func() {
|
ct3 := titleflex.Rigid(gtx, func() {
|
||||||
dotsBtn.Layout(gtx, family)
|
dotsBtn.Layout(gtx)
|
||||||
})
|
})
|
||||||
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, family)
|
title.Layout(gtx)
|
||||||
})
|
})
|
||||||
titleflex.Layout(gtx, ct1, ct2, ct3)
|
titleflex.Layout(gtx, ct1, ct2, ct3)
|
||||||
})
|
})
|
||||||
|
@ -784,7 +872,7 @@ func eventLoop() {
|
||||||
insName, insValue = "", ""
|
insName, insValue = "", ""
|
||||||
page = insertPage
|
page = insertPage
|
||||||
}
|
}
|
||||||
w.Update(gtx.Ops)
|
e.Frame(gtx.Ops)
|
||||||
}
|
}
|
||||||
if x == 100 {
|
if x == 100 {
|
||||||
x = 0
|
x = 0
|
||||||
|
|
|
@ -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"
|
"gioui.org/op/clip"
|
||||||
"gioui.org/op/paint"
|
"gioui.org/op/paint"
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
|
@ -30,22 +30,16 @@ type Overlay struct {
|
||||||
Alignment text.Alignment
|
Alignment text.Alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Overlay) Layout(gtx *layout.Context, family text.Family) {
|
func (b *Overlay) Layout(gtx *layout.Context) {
|
||||||
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 := text.Label{
|
l := th.Label(b.Size, b.Text)
|
||||||
Size: b.Size,
|
|
||||||
Text: b.Text,
|
|
||||||
Alignment: b.Alignment,
|
|
||||||
}
|
|
||||||
ins := layout.UniformInset(unit.Dp(4))
|
ins := layout.UniformInset(unit.Dp(4))
|
||||||
l.Material.Record(gtx.Ops)
|
l.Color = b.Color
|
||||||
paint.ColorOp{Color: b.Color}.Add(gtx.Ops)
|
|
||||||
l.Material.Stop()
|
|
||||||
ins.Layout(gtx, func() {
|
ins.Layout(gtx, func() {
|
||||||
l.Layout(gtx, family)
|
l.Layout(gtx)
|
||||||
})
|
})
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
@ -76,30 +70,17 @@ 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)
|
||||||
rrect(gtx.Ops, w, h, r, r, r, r)
|
rect := f32.Rectangle{
|
||||||
|
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}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://pomax.github.io/bezierinfo/#circles_cubic.
|
func (b *Button) Layout(gtx *layout.Context) {
|
||||||
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 {
|
||||||
|
@ -110,15 +91,11 @@ func (b *Button) Layout(gtx *layout.Context, family text.Family) {
|
||||||
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 := text.Label{
|
l := th.Label(b.Size, b.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, family)
|
l.Layout(gtx)
|
||||||
})
|
})
|
||||||
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
14
go.mod
|
@ -1,14 +0,0 @@
|
||||||
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
40
go.sum
|
@ -1,40 +0,0 @@
|
||||||
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=
|
|
|
@ -1,24 +1,98 @@
|
||||||
//+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) {
|
||||||
fmt.Println("Clipboard not implemented for this platform")
|
pgp.Clip(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) nativeDecrypt(name string) (string, error) {
|
func (s *Store) nativeDecrypt(name string) (string, error) {
|
||||||
return "", fmt.Errorf("NOT IMPLEMENTED")
|
connect()
|
||||||
|
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) {
|
||||||
return nil, fmt.Errorf("NOT IMPLEMENTED")
|
connect()
|
||||||
|
ret, err := pgp.Encrypt(s.Id, pw)
|
||||||
|
return []byte(ret), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func nativeIdentities() ([]string, error) {
|
func nativeIdentities() ([]string, error) {
|
||||||
return nil, fmt.Errorf("NOT IMPLEMENTED")
|
//NOT IMPLEMENTED ON ANDROID
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
146
jni_android.c
Normal file
146
jni_android.c
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
195
jni_android.go
Normal file
195
jni_android.go
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
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()
|
||||||
|
}
|
19
jni_android.h
Normal file
19
jni_android.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#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
17
main.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
@ -53,10 +54,23 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +148,8 @@ 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)
|
||||||
|
@ -262,6 +278,7 @@ 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()
|
||||||
|
|
BIN
openpgp-api.jar
Normal file
BIN
openpgp-api.jar
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user