commit e6b79cc1e100fb2029d40041bbdf6a97b45afc90
Author: lash <dev@holbrook.no>
Date: Wed, 28 Feb 2024 02:46:59 +0000
Initial commit
Diffstat:
43 files changed, 2111 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+src/*.ui
+testdata_mdb
+*\~
diff --git a/Makefile b/Makefile
@@ -0,0 +1,17 @@
+all: subs
+
+subs: glade
+ make -C src
+
+glade:
+ gtk4-builder-tool simplify --3to4 glade.ui > src/main.ui
+
+clean:
+ rm -vf src/*.o
+ rm -vf src/a.out
+
+run: all
+ G_MESSAGES_DEBUG=all ./src/a.out
+
+debug: all
+ G_DEBUG=3 G_MESSAGES_DEBUG=all ./src/a.out
diff --git a/glade.ui b/glade.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.40.0 -->
+<interface>
+ <requires lib="gtk+" version="3.24"/>
+ <object class="GtkWindow" id="keechoose">
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkListBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="keescan">
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">gtk-missing-image</property>
+ <property name="use-fallback">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="keeunlock">
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkBox" id="keeunlockbox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkEntry" id="keeunlockpassphrase">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="keeunlocksubmit">
+ <property name="label" translatable="yes">button</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="keeview">
+ <property name="can-focus">False</property>
+ <child>
+ <!-- n-columns=3 n-rows=3 -->
+ <object class="GtkGrid" id="view">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="use-fallback">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ <property name="width">2</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/src/Makefile b/src/Makefile
@@ -0,0 +1,19 @@
+OBJS := $(patsubst %.c,%.o,$(filter-out main.c,$(wildcard *.c)))
+INCLUDES := `pkg-config --cflags gtk4 libgcrypt lmdb libxdg-basedir`
+CFLAGS += $(INCLUDES) -g3 -Wall -Iaux/varint
+LIBS := `pkg-config --libs gtk4 libgcrypt zlib lmdb libxdg-basedir` -lb64
+LDFLAGS += $(LIBS)
+
+all: aux resource $(OBJS)
+ $(CC) $(CFLAGS) main.c -o a.out $(OBJS) $(LDFLAGS) aux/varint/varint.o
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
+
+resource:
+ glib-compile-resources kee.gresource.xml --target=resources.c --generate-source
+
+aux:
+ make -C aux
+
+.PHONY: clean aux
diff --git a/src/aux/Makefile b/src/aux/Makefile
@@ -0,0 +1,2 @@
+all:
+ make -C varint
diff --git a/src/aux/README.md b/src/aux/README.md
@@ -0,0 +1,5 @@
+add auxiliary source files here.
+
+e.g. implementation of varint
+
+TODO: varint header file
diff --git a/src/aux/varint/.package b/src/aux/varint/.package
@@ -0,0 +1,2 @@
+file varint.h
+file varint.c
diff --git a/src/aux/varint/CMakeLists.txt b/src/aux/varint/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_library(varint varint.c)
+set_property(TARGET varint PROPERTY C_VISIBILITY_PRESET default)
+set_property(TARGET varint PROPERTY VISIBILITY_INLINES_HIDDEN OFF)
+include_directories(.)
diff --git a/src/aux/varint/Makefile b/src/aux/varint/Makefile
@@ -0,0 +1,2 @@
+all:
+ $(CC) -c varint.c -o varint.o
diff --git a/src/aux/varint/README.md b/src/aux/varint/README.md
@@ -0,0 +1,59 @@
+# varint.c
+
+Read and write variable sized integers in C.
+Compatible with Protobuf and Go varints.
+
+## API
+```c
+// read uint64 or int64 from data. returns the number of bytes read or returns
+// the zero if there is not enough data or returns -1 if the data does not
+// represent a valid varint.
+int varint_read_u(const void *data, size_t len, uint64_t *x);
+int varint_read_i(const void *data, size_t len, int64_t *x);
+
+// write a uint64 or int64 to data buffer. The data buffer must be at least
+// 10 bytes in order to hold the maximum sized varint. Returns the number of
+// bytes written.
+int varint_write_u(void *data, uint64_t x);
+int varint_write_i(void *data, int64_t x);
+```
+
+## Example
+
+```c
+#include <stdio.h>
+#include "varint.h"
+
+int main() {
+ char buf[100];
+ int n;
+ int64_t i;
+ uint64_t u;
+
+ // write and read a signed integer
+ i = -305;
+ n = varint_write_i(buf, i);
+ printf("wrote the value %lld (%d bytes)\n", (long long)i, n);
+ n = varint_read_i(buf, sizeof(buf), &i);
+ printf("read the value %lld (%d bytes)\n", (long long)i, n);
+
+
+ // write and read an unsigned integer
+ u = 102993;
+ n = varint_write_u(buf, u);
+ printf("wrote the value %llu (%d bytes)\n", (unsigned long long)u, n);
+ n = varint_read_u(buf, sizeof(buf), &u);
+ printf("read the value %lld (%d bytes)\n", (unsigned long long)u, n);
+}
+// output:
+// wrote the value -305 (2 bytes)
+// read the value -305 (2 bytes)
+// wrote the value 102993 (3 bytes)
+// read the value 102993 (3 bytes)
+```
+
+## Running tests
+
+```sh
+$ cc -DVARINT_TEST varint.c && ./a.out
+```
diff --git a/src/aux/varint/varint.c b/src/aux/varint/varint.c
@@ -0,0 +1,168 @@
+// Copyright 2020 Joshua J Baker. All rights reserved.
+
+#include "varint.h"
+
+// varint_write_u writes a uint64 varint to data, which could be to 10 bytes.
+// Make sure that you provide a data buffer that can take 10 bytes!
+// Returns the number of bytes written.
+int varint_write_u(void *data, uint64_t x) {
+ char *str = data;
+ uint64_t n = 0;
+ n+=x>=0x80; str[0]=x|0x80; x>>=7;
+ n+=x>=0x80; str[1]=x|0x80; x>>=7;
+ n+=x>=0x80; str[2]=x|0x80; x>>=7;
+ n+=x>=0x80; str[3]=x|0x80; x>>=7;
+ n+=x>=0x80; str[4]=x|0x80; x>>=7;
+ n+=x>=0x80; str[5]=x|0x80; x>>=7;
+ n+=x>=0x80; str[6]=x|0x80; x>>=7;
+ n+=x>=0x80; str[7]=x|0x80; x>>=7;
+ n+=x>=0x80; str[8]=x|0x80; x>>=7;
+ n+=x>=0x80; str[9]=x|0x80; x>>=7;
+ str[n] ^= 0x80;
+ return n+1;
+}
+
+// varint_write_i writes a int64 varint to data, which could be to 10 bytes.
+// Make sure that you provide a data buffer that can take 10 bytes!
+// Returns the number of bytes written.
+int varint_write_i(void *buf, int64_t x) {
+ uint64_t ux = (uint64_t)x << 1;
+ if (x < 0) {
+ ux = ~ux;
+ }
+ return varint_write_u(buf, ux);
+}
+
+// varint_read_u reads a uint64 varint from data.
+// Returns the number of bytes reads, or returns 0 if there's not enough data
+// to complete the read, or returns -1 if the data buffer does not represent
+// a valid uint64 varint.
+int varint_read_u(const void *data, size_t len, uint64_t *x) {
+ const char *str = data;
+ uint64_t b;
+ *x = 0;
+ if (len==0) return 0; b=str[0]; *x|=(b&0x7f)<<(7*0); if (b<0x80) return 0+1;
+ if (len==1) return 0; b=str[1]; *x|=(b&0x7f)<<(7*1); if (b<0x80) return 1+1;
+ if (len==2) return 0; b=str[2]; *x|=(b&0x7f)<<(7*2); if (b<0x80) return 2+1;
+ if (len==3) return 0; b=str[3]; *x|=(b&0x7f)<<(7*3); if (b<0x80) return 3+1;
+ if (len==4) return 0; b=str[4]; *x|=(b&0x7f)<<(7*4); if (b<0x80) return 4+1;
+ if (len==5) return 0; b=str[5]; *x|=(b&0x7f)<<(7*5); if (b<0x80) return 5+1;
+ if (len==6) return 0; b=str[6]; *x|=(b&0x7f)<<(7*6); if (b<0x80) return 6+1;
+ if (len==7) return 0; b=str[7]; *x|=(b&0x7f)<<(7*7); if (b<0x80) return 7+1;
+ if (len==8) return 0; b=str[8]; *x|=(b&0x7f)<<(7*8); if (b<0x80) return 8+1;
+ if (len==9) return 0; b=str[9]; *x|=(b&0x7f)<<(7*9); if (b<0x80) return 9+1;
+ return -1;
+}
+
+// varint_read_i reads a int64 varint from data.
+// Returns the number of bytes reads, or returns 0 if there's not enough data
+// to complete the read, or returns -1 if the data buffer does not represent
+// a valid int64 varint.
+int varint_read_i(const void *data, size_t len, int64_t *x) {
+ uint64_t ux;
+ int n = varint_read_u(data, len, &ux);
+ *x = (int64_t)(ux >> 1);
+ if ((ux&1) != 0) {
+ *x = ~*x;
+ }
+ return n;
+}
+
+//==============================================================================
+// TESTS
+// $ cc -DVARINT_TEST buf.c && ./a.out # run tests
+//==============================================================================
+#ifdef VARINT_TEST
+#include <math.h>
+#include <time.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static uint64_t rand_uint() {
+ return (((uint64_t)rand()<<33) |
+ ((uint64_t)rand()<<1) |
+ ((uint64_t)rand()<<0)) >> (rand()&63);
+}
+
+static int64_t rand_int() {
+ return (int64_t)(rand_uint()) * ((rand()&1)?1:-1);
+}
+
+static void rand_fill(char *data, int len) {
+ int i = 0;
+ for (; i < len-3; i += 3) {
+ uint32_t r = rand();
+ data[i+0] = r;
+ data[i+1] = r >> 8;
+ data[i+2] = r >> 16;
+ }
+ for (; i < len; i++) {
+ data[i] = rand();
+ }
+}
+
+int main() {
+ printf("Running varint.c tests...\n");
+ int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
+ printf("seed=%d\n", seed);
+ srand(seed);
+ int N = 500000;
+ uint64_t *uints = malloc(sizeof(uint64_t)*N);
+ char *data = malloc(10*N);
+ assert(uints && data);
+ printf("[RANDOMS] ");
+ for (int h = 0; h < 50; h++) {
+ for (int i = 0, n = 0; i < N; i++) {
+ uints[i] = rand_uint();
+ int nn = varint_write_u(data+n, uints[i]);
+ assert(nn > 0);
+ n += nn;
+ }
+ for (int i = 0, n = 0; i < N; i++) {
+ uint64_t x;
+ int nn = varint_read_u(data+n, 10*N-n, &x);
+ assert(nn > 0);
+ assert(x == uints[i]);
+ n += nn;
+ }
+ int64_t *ints = (int64_t*)uints;
+ for (int i = 0, n = 0; i < N; i++) {
+ ints[i] = rand_int();
+ int nn = varint_write_i(data+n, ints[i]);
+ assert(nn > 0);
+ n += nn;
+ }
+ for (int i = 0, n = 0; i < N; i++) {
+ int64_t x;
+ int nn = varint_read_i(data+n, 10*N-n, &x);
+ assert(nn > 0);
+ assert(x == ints[i]);
+ n += nn;
+ }
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+ printf("[FUZZING] ");
+ int sz = 10*N/2;
+ for (int h = 0; h < 50; h++) {
+ rand_fill(data, sz);
+ for (int i = 0; i < sz; i++) {
+ int64_t x;
+ varint_read_i(data+i, sz-i, &x);
+ }
+ for (int i = 0; i < sz; i++) {
+ uint64_t x;
+ varint_read_u(data+i, sz-i, &x);
+ }
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+ free(uints);
+ free(data);
+ printf("PASSED\n");
+}
+#endif
diff --git a/src/aux/varint/varint.h b/src/aux/varint/varint.h
@@ -0,0 +1,15 @@
+// Copyright 2020 Joshua J Baker. All rights reserved.
+
+#ifndef VARINT_H
+#define VARINT_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+int varint_read_u(const void *data, size_t len, uint64_t *x);
+int varint_read_i(const void *data, size_t len, int64_t *x);
+
+int varint_write_u(void *data, uint64_t x);
+int varint_write_i(void *data, int64_t x);
+
+#endif
diff --git a/src/aux/varint/varint.o b/src/aux/varint/varint.o
Binary files differ.
diff --git a/src/command.h b/src/command.h
@@ -0,0 +1,7 @@
+#ifndef _KEE_CMD_H
+#define _KEE_CMD_H
+
+int process_rpc_command(void *backend, char *buf, size_t buf_len, bool preview);
+int preview_command(char *in, size_t in_len, char *out, size_t *out_len);
+
+#endif // _KEE_CMD_H
diff --git a/src/common.h b/src/common.h
@@ -0,0 +1,6 @@
+#ifndef _COMMON_H
+#define _COMMON_H
+
+typedef unsigned char kee_boolean;
+
+#endif // _EXPORT_H
diff --git a/src/context.h b/src/context.h
@@ -0,0 +1,8 @@
+#ifndef _KEE_CONTEXT
+#define _KEE_CONTEXT
+
+struct kee_context {
+
+};
+
+#endif // _KEE_CONTEXT
diff --git a/src/db.c b/src/db.c
@@ -0,0 +1,184 @@
+#include <string.h>
+#include <fcntl.h>
+#include <lmdb.h>
+#include <gcrypt.h>
+#include <time.h>
+#include "db.h"
+#include "err.h"
+#include "endian.h"
+
+int db_connect(struct db_ctx *ctx, char *conn) {
+ int r;
+
+ db_reset(ctx);
+ ctx->connstr = conn;
+ r = mdb_env_create(&ctx->env);
+ if (r) {
+ return ERR_FAIL;
+ }
+ r = mdb_env_open(ctx->env, ctx->connstr, MDB_NOLOCK, S_IRWXU);
+ if (r) {
+ return ERR_FAIL;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * \todo split up and optimize
+ */
+int db_put(struct db_ctx *ctx, enum DbKey pfx, char *data, size_t data_len) {
+ int r;
+ char *buf;
+ char buf_reverse[33];
+ unsigned char *rv;
+ char kv;
+ struct timespec ts;
+ char rts[sizeof(struct timespec)];
+ gcry_error_t e;
+ gcry_md_hd_t h;
+ MDB_txn *tx;
+ MDB_dbi dbi;
+ MDB_val k;
+ MDB_val v;
+
+ buf = (char*)malloc(1 + 32 + sizeof(struct timespec)); // length should be lookup in sizes array for each key
+
+ r = clock_gettime(CLOCK_REALTIME, &ts);
+ if (r) {
+ return ERR_FAIL;
+ }
+ memcpy(rts, &ts.tv_sec, sizeof(ts.tv_sec));
+ memcpy(rts + sizeof(ts.tv_sec), &ts.tv_nsec, sizeof(ts.tv_nsec));
+ to_endian(0, sizeof(ts.tv_sec), rts);
+ to_endian(0, sizeof(ts.tv_nsec), rts + sizeof(ts.tv_sec));
+
+ e = gcry_md_open(&h, GCRY_MD_SHA256, 0);
+ if (e) {
+ return ERR_DIGESTFAIL;
+ }
+ gcry_md_write(h, data, data_len);
+ rv = gcry_md_read(h, 0);
+ kv = (char)pfx;
+ memcpy(buf, &kv, 1);
+ memcpy(buf + 1, rts, sizeof(struct timespec));
+ memcpy(buf + 1 + sizeof(struct timespec), rv, 32);
+
+ r = mdb_txn_begin(ctx->env, NULL, 0, &tx);
+ if (r) {
+ return ERR_FAIL;
+ }
+
+ r = mdb_dbi_open(tx, NULL, MDB_CREATE, &dbi);
+ if (r) {
+ return ERR_FAIL;
+ }
+
+ k.mv_data = buf;
+ k.mv_size = 1 + 32 + sizeof(struct timespec);
+ v.mv_data = data;
+ v.mv_size = data_len;
+
+ r = mdb_put(tx, dbi, &k, &v, 0);
+ if (r) {
+ return ERR_FAIL;
+ }
+
+ // put reverse lookup
+ buf_reverse[0] = (char)DbKeyReverse;
+ memcpy(buf_reverse+1, rv, 32);
+ k.mv_data = buf_reverse;
+ k.mv_size = 33;
+ v.mv_data = buf;
+ v.mv_size = 1 + 32 + sizeof(struct timespec);
+ gcry_md_close(h); // keep the handle open until here because we use its digest value again for the key
+
+ r = mdb_put(tx, dbi, &k, &v, 0);
+ if (r) {
+ return ERR_FAIL;
+ }
+
+ r = mdb_txn_commit(tx);
+ if (r) {
+ return ERR_FAIL;
+ }
+
+ return ERR_OK;
+
+}
+
+/**
+ *
+ * \todo change cursor to jump to new search match when current (last) prefix does not match lookup prefix.
+ *
+ */
+int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, char **value, size_t *value_len) {
+ int r;
+ unsigned char start[DB_KEY_SIZE_LIMIT];
+
+ memset(start, 0, DB_KEY_SIZE_LIMIT);;
+ if ((char)pfx == 0) {
+ return ERR_DB_INVALID;
+ }
+
+ if (ctx->current_key == DbNoKey) {
+ if (ctx->started) {
+ mdb_cursor_close(ctx->crsr);
+ mdb_dbi_close(ctx->env, ctx->dbi);
+ mdb_txn_abort(ctx->tx);
+ }
+
+ r = mdb_txn_begin(ctx->env, NULL, MDB_RDONLY, &ctx->tx);
+ if (r) {
+ return ERR_DB_FAIL;
+ }
+ r = mdb_dbi_open(ctx->tx, NULL, 0, &ctx->dbi);
+ if (r) {
+ return ERR_DB_FAIL;
+ }
+
+ r = mdb_cursor_open(ctx->tx, ctx->dbi, &ctx->crsr);
+ if (r) {
+ return ERR_DB_FAIL;
+ }
+ ctx->current_key = pfx;
+
+ start[0] = (char)pfx;
+ ctx->k.mv_size = 1;
+ if (!ctx->browsing) {
+ if (*key != 0) {
+ memcpy(start+1, *key, *key_len);
+ ctx->k.mv_size += *key_len;
+ }
+ }
+ ctx->k.mv_data = start;
+ }
+
+ if (!ctx->browsing) {
+ r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_SET_RANGE);
+ ctx->browsing = 1;
+ } else {
+ r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_NEXT_NODUP);
+ }
+ if (r) {
+ return ERR_DB_FAIL;
+ }
+ start[0] = (char)*((char*)ctx->k.mv_data);
+ if (start[0] != ctx->current_key) {
+ db_reset(ctx);
+ return ERR_DB_NOMATCH;
+ }
+
+ *key = (char*)ctx->k.mv_data;
+ *key_len = ctx->k.mv_size;
+ *value = (char*)ctx->v.mv_data;
+ *value_len = ctx->v.mv_size;
+
+ return ERR_OK;
+
+}
+
+
+void db_reset(struct db_ctx *ctx) {
+ memset(ctx, 0, sizeof(struct db_ctx));
+}
diff --git a/src/db.h b/src/db.h
@@ -0,0 +1,50 @@
+#ifndef _DB_H
+#define _DB_H
+
+#include <lmdb.h>
+
+#ifndef DB_KEY_SIZE_LIMIT
+#define DB_KEY_SIZE_LIMIT 64
+#endif
+
+#ifndef DB_VALUE_SIZE_LIMIT
+#define DB_VALUE_SIZE_LIMIT 1048576
+#endif
+
+/**
+ * \brief Key prefixes used for database storage.
+ *
+ */
+enum DbKey {
+ /// Noop value, used for default value of a DbKey variable
+ DbNoKey = 0x00,
+ /// A credit item record
+ DbKeyCreditItem = 0x01,
+ /// A reverse lookup record; resolves the content hash to the content entry in the database.
+ DbKeyReverse = 0xff,
+};
+
+/**
+ *
+ * \brief Interface to persistent storage of data items used in application.
+ *
+ */
+struct db_ctx {
+ char *connstr;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_txn *tx;
+ MDB_cursor *crsr;
+ MDB_val k;
+ MDB_val v;
+ enum DbKey current_key;
+ int started;
+ int browsing;
+};
+
+int db_connect(struct db_ctx *ctx, char *conn);
+int db_put(struct db_ctx *ctx, enum DbKey pfx, char *data, size_t data_len);
+int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, char **value, size_t *value_len);
+void db_reset(struct db_ctx *ctx);
+
+#endif // _DB_H
diff --git a/src/debug.c b/src/debug.c
@@ -0,0 +1,20 @@
+#include <gtk/gtk.h>
+
+#include "debug.h"
+
+
+void debug_log(enum debugLevel level, const char *s) {
+ int loglevel;
+
+ switch(level) {
+ case DEBUG_CRITICAL:
+ loglevel = G_LOG_LEVEL_ERROR;
+ break;
+ case DEBUG_ERROR:
+ loglevel = G_LOG_LEVEL_ERROR;
+ break;
+ default:
+ loglevel = G_LOG_LEVEL_DEBUG;
+ }
+ g_log(G_LOG_DOMAIN, loglevel, s);
+}
diff --git a/src/debug.h b/src/debug.h
@@ -0,0 +1,39 @@
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+/**
+ * \brief Debug levels for simple log output.
+ */
+enum debugLevel {
+ /// Critical error, should terminate application.
+ DEBUG_CRITICAL,
+ /// Error, anomalous condition that should not occur.
+ DEBUG_ERROR,
+ /// Warning, condition that may contribute to affecting the state of the program adversely.
+ DEBUG_WARNING,
+ /// Info, events that an end-user may be interested in during normal operation.
+ DEBUG_INFO,
+ /// Debug, events that a developer may be intereted in during normal operation.
+ DEBUG_DEBUG,
+ /// Trace, mostly temporary loglines used for debugging concrete issues during development.
+ DEBUG_TRACE,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *
+ * \brief Implementer logs constant string according to given log level.
+ *
+ * \param level Debug level
+ * \param s String to log
+ */
+void debug_log(enum debugLevel level, const char *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/digest.c b/src/digest.c
@@ -0,0 +1,27 @@
+#include <gcrypt.h>
+
+#include "err.h"
+#include "digest.h"
+
+
+int calculate_digest(const char *in, size_t in_len, char *out) {
+ gcry_error_t e;
+ gcry_md_hd_t h;
+ unsigned char *v;
+ static unsigned int digest_len;
+
+ if (digest_len == 0) {
+ digest_len = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
+ }
+
+ e = gcry_md_open(&h, GCRY_MD_SHA256, GCRY_MD_FLAG_SECURE);
+ if (e) {
+ return ERR_DIGESTFAIL;
+ }
+
+ gcry_md_write(h, in, in_len);
+ v = gcry_md_read(h, 0);
+ memcpy(out, v, digest_len);
+ gcry_md_close(h);
+ return ERR_OK;
+}
diff --git a/src/digest.h b/src/digest.h
@@ -0,0 +1,23 @@
+#ifndef _KEE_DIGEST_H
+#define _KEE_DIGEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ /**
+ *
+ * \brief Calculate SHA256 digest over the given input data.
+ *
+ * \param in Input data.
+ * \param in_len Length of input data.
+ * \param out Contains digest data on success. Must have at least 32 bytes available.
+ * \return 0 on success, any other value indicates failure.
+ *
+ */
+ int calculate_digest(const char *in, size_t in_len, char *out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _KEE_DIGEST_H
diff --git a/src/endian.c b/src/endian.c
@@ -0,0 +1,44 @@
+#include "endian.h"
+
+int is_le() {
+ unsigned short s = 42;
+ return *((unsigned char*)&s) == 42;
+}
+
+// convert unsigned integer to little-endian representation
+int to_endian(char direction, int l, void *n) {
+ union le un;
+
+ if (l == 1 || is_le() == direction) {
+ return 0;
+ }
+ switch(l) {
+ case sizeof(long long):
+ un.ll = (long long*)n;
+ break;
+ case sizeof(int):
+ un.i = (int*)n;
+ break;
+ case sizeof(short):
+ un.s = (short*)n;
+ break;
+ default:
+ return 1;
+ }
+ flip_endian(l, &un);
+
+ return 0;
+}
+
+void flip_endian(int l, union le *n) {
+ int i;
+ char t;
+ unsigned char *ne;
+
+ ne = (n->c)+(l-1);
+ for (i = 0; i < l/2; i++) {
+ t = *(n->c+i);
+ *((n->c)+i) = *(ne-i);
+ *(ne-i) = t;
+ }
+}
diff --git a/src/endian.h b/src/endian.h
@@ -0,0 +1,45 @@
+#ifndef LASH_ENDIAN_H_
+#define LASH_ENDIAN_H_
+
+/**
+ *
+ * \brief Encapsulats all suppoerted integer lengths for endian conversion.
+ *
+ */
+union le {
+ short *s;
+ int *i;
+ long long *ll;
+ unsigned char *c;
+};
+
+//int le(int l, void *n);
+/*
+ * Return true (1) if system is little-endian.
+ */
+int is_le();
+/**
+ * Convert to specified endian order.
+ *
+ * The integer data in \c n is changed in-place.
+ *
+ * If \c direction is same as system endianness, or \c l==1, no action is taken.
+ *
+ * \param direction 0 for big-endian, 1 for little-endian.
+ * \param l Length of integer \c n in bytes.
+ * \param n Integer data.
+ * \return 1 if \c l is invalid byte size, 0 (success) otherwise.
+ *
+ */
+int to_endian(char direction, int l, void *n);
+/**
+ * Change endian order of given number.
+ *
+ * The integer data in \c n is changed in-place.
+ *
+ * \param l Length of integer \c in bytes.
+ * \param n Integer data.
+ */
+void flip_endian(int l, union le *n);
+
+#endif // LASH_ENDIAN_H_
diff --git a/src/err.h b/src/err.h
@@ -0,0 +1,19 @@
+/**
+ *
+ * Error codes within context of the kee application and backend.
+ *
+ */
+enum keeError {
+ ERR_OK,
+ ERR_FAIL,
+ ERR_SPACE,
+ ERR_NOCRYPTO,
+ ERR_NOKEY,
+ ERR_KEYFAIL,
+ ERR_DIGESTFAIL,
+ ERR_DB_FAIL,
+ ERR_DB_NOMATCH,
+ ERR_DB_INVALID,
+ ERR_QR_MISSING,
+ ERR_QR_INVALID,
+};
diff --git a/src/export.c b/src/export.c
@@ -0,0 +1,114 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <varint.h>
+
+#include "export.h"
+#include "common.h"
+#include "err.h"
+
+
+int export_init(struct kee_export *ex, size_t count) {
+ size_t unit;
+
+ ex->count = count;
+
+ // 10 is max varint
+ unit = sizeof(ex->lenlens) + sizeof(ex->data) + sizeof(ex->datalens) + 10;
+ ex->lens = malloc(unit * count);
+ if (ex->lens == NULL) {
+ return 1;
+ }
+ unit += sizeof(ex->lens) * count;
+ ex->lenlens = (size_t*)ex->lens + unit;
+ unit += sizeof(ex->lenlens) * count;
+ ex->data = (char**)ex->lens + unit;
+ unit += sizeof(ex->data) * count;
+ ex->datalens = (size_t*)ex->lens + unit;
+ return ERR_OK;
+}
+
+int export_add_item(struct kee_export *ex, char *in, size_t in_len) {
+ int r;
+ char b[10];
+
+ if (in_len == 0) {
+ return ERR_FAIL;
+ }
+
+ r = 0;
+ r += varint_write_u(b, in_len);
+ memcpy(ex->lens+(ex->count*10), b, 10);
+ *(ex->lenlens + sizeof(ex->lenlens) * ex->count) = (size_t)r;
+ *(ex->datalens + sizeof(ex->datalens) * ex->count) = in_len;
+ *(ex->data + sizeof(ex->data) * ex->count) = in;
+
+ ex->size += r;
+ ex->count++;
+ return r;
+
+}
+
+int export_write(struct kee_export *ex, char *out, size_t out_len) {
+ char *p;
+ int i;
+ size_t l;
+ size_t c;
+
+ if (out_len < ex->size) {
+ return -1;
+ }
+
+ p = out;
+ c = 0;
+ for (i = 0; i < ex->count; i++) {
+ l = ex->lenlens[i];
+ memcpy(p, ex->lens+(10*i), l);
+ p += l;
+ c += l;
+ l = ex->datalens[i];
+ memcpy(p, ex->data[i], l);
+ p += l;
+ c += l;
+ }
+ return c;
+}
+
+
+void export_free(struct kee_export *ex) {
+ free(ex->lens);
+}
+
+// data not copied, must be in scope for duration
+int import_init(struct kee_import *im, const char *in, size_t in_len) {
+ im->data = (char*)in;
+ im->size = in_len;
+ im->crsr = 0;
+ im->eof = 0;
+ return ERR_OK;
+}
+
+kee_boolean done(struct kee_import *im) {
+ return im->eof;
+}
+
+int import_read(struct kee_import *im, char *out, size_t out_len) {
+ int r;
+ uint64_t l;
+
+ if (im->size - im->crsr <= 0) {
+ im->eof = 1;
+ return 0;
+ }
+
+ r = varint_read_u(im->data + im->crsr, out_len, &l);
+ im->crsr += r;
+
+ memcpy(out, im->data + im->crsr, (size_t)l);
+ im->crsr += l;
+
+ return l;
+}
+
+void import_free(struct kee_import *im) {
+}
diff --git a/src/export.h b/src/export.h
@@ -0,0 +1,36 @@
+#ifndef _EXPORT_H
+#define _EXPORT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "common.h"
+
+struct kee_export {
+ size_t cap;
+ int count;
+ size_t size;
+ char *lens;
+ size_t *lenlens;
+ char **data;
+ size_t *datalens;
+};
+
+struct kee_import {
+ char *data;
+ size_t size;
+ unsigned int crsr;
+ kee_boolean eof;
+};
+
+int export_init(struct kee_export *ex, size_t count);
+void export_free(struct kee_export *ex);
+int export_add_item(struct kee_export *ex, char *in, size_t in_len);
+int export_write(struct kee_export *ex, char *out, size_t out_len);
+
+int import_init(struct kee_import *im, const char *in, size_t in_len);
+void import_free(struct kee_import *im);
+int import_read(struct kee_import *im, char *out, size_t out_len);
+kee_boolean done(struct kee_import *im);
+
+#endif // _EXPORT_H
diff --git a/src/gpg.h b/src/gpg.h
@@ -0,0 +1,117 @@
+#ifndef _KEE_GPG_H
+#define _KEE_GPG_H
+#include <string>
+#include <stddef.h>
+
+#define GPG_MIN_VERSION "1.10.2"
+#define CHACHA20_KEY_LENGTH_BYTES 32
+#define CHACHA20_NONCE_LENGTH_BYTES 12
+
+#ifndef ENCRYPT_BLOCKSIZE
+#define ENCRYPT_BLOCKSIZE 4096
+#endif
+
+/**
+ *
+ * \brief Encrypt the given string data with the provided encryption key and nonce.
+ *
+ * \sa encryptb
+ *
+ */
+int encrypt(char *ciphertext, size_t ciphertext_len, std::string indata, const char *key, const char *nonce);
+
+/**
+ *
+ * Encrypt the given binary data with the provided encryption key and nonce, using the CHACHA20-POLY1305 algorithm.
+ *
+ * If successful, \c ciphertext and \c ciphertext_len hold the encrypted data and its length respectively.
+ *
+ * \param ciphertext holds resulting ciphertext
+ * \param ciphertext_len holds the length of the resulting ciphertext
+ * \param indata input data to encrypt
+ * \param indata_len length of input data to encrypt
+ * \param key 256-bit key to use for encryption
+ * \param nonce 256-bit nonce to use as salt for encryption
+ * \return 0 on successful encryption.
+ *
+ */
+int encryptb (char *ciphertext, size_t ciphertext_len, const char *indata, size_t indata_len, const char *key, const char *nonce);
+
+/**
+ *
+ * Decrypt the given string ciphertext with the provided encryption key and nonce.
+ *
+ * \sa decryptb
+ *
+ */
+int decrypt(std::string *outdata, const char *ciphertext, size_t ciphertext_len, const char *key, const char *nonce);
+
+/**
+ *
+ * Decrypt the given binary ciphertext with the provided encryption key and nonce, using the CHACHA20-POLY1305 algorithm.
+ *
+ * If successful, the decrypted (plaintext) data will be stored in \c outdata.
+ *
+ * \param outdata holdes the resulting plaintext
+ * \param ciphertext encrypted data to decrypt
+ * \param ciphertext_len length of encrypted data
+ * \param key 256-bit key to use for decryption
+ * \param nonce 256-bit nonce to use as salt for decryption
+ * \return 0 on successful decryption.
+ *
+ */
+int decryptb(char *outdata, const char *ciphertext, size_t ciphertext_len, const char *key, const char *nonce);
+
+/**
+ *
+ * Calculate number of bytes a given byte count when rounded up to the given block size.
+ *
+ * For example, if \c blocksize is \c 5 and \c insize is \c 11, then \c 15 will be returned.
+ *
+ * \param insize size of data
+ * \param blocksize block size to round up to
+ *
+ */
+size_t get_padsize(size_t insize, size_t blocksize);
+
+/**
+ *
+ * \brief Interface to the encrypted key storage for both identity public key and the key used for encryption of the identity public key.
+ *
+ */
+class GpgStore {
+
+ public:
+ /// Sets correct context values for underlying \c gcrypt operations.
+ GpgStore();
+ /**
+ *
+ * Attempts to decrypt the identity public key with the given passphrase.
+ *
+ * If no public key exists, one will be created and encrypted using the passphrase.
+ *
+ * \param p path to key store
+ * \param passphrase passphrase for public key encryption
+ * \return 0 if successful, any other value indicates an error
+ *
+ */
+ int check(std::string p, std::string passphrase);
+ /**
+ *
+ * Returns the fingerprint of the identity public key.
+ *
+ * \return 160-bit fingerprint value
+ */
+ char *get_fingerprint();
+
+ private:
+ /// calculates sha256 digest for the given string value, using secure memory
+ int digest(char *out, std::string in);
+ //const char *m_version;
+ //char *m_seckey;
+ /// cached fingerprint value, in string format with zero terminator
+ char m_fingerprint[41];
+ /// cached digest length of sha256
+ unsigned int m_passphrase_digest_len;
+};
+#endif
diff --git a/src/hex.c b/src/hex.c
@@ -0,0 +1,21 @@
+#include <stddef.h>
+#include <stdio.h>
+
+#include "hex.h"
+
+
+int bin_to_hex(const unsigned char *data, size_t l, unsigned char *zHex, size_t *z) {
+ unsigned int i;
+
+ if (*z < (l*2)+1) {
+ return 1;
+ }
+
+ for (i = 0; i < l; i++) {
+ sprintf((char*)zHex+(i*2), "%02x", *(data+i));
+ }
+ *z = (i*2);
+ *(zHex+(*z)) = 0x0;
+ *z = *z + 1;
+ return 0;
+}
diff --git a/src/hex.h b/src/hex.h
@@ -0,0 +1,22 @@
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *
+ * \brief Convert data to hex string
+ *
+ * \param data Input data.
+ * \param l Input data length.
+ * \param zHex If successful, contains hex string output.
+ * \param z Pointer to available length of \c zHex. Will contain the string length after succesful conversion.
+ * \todo \c z output is superfluous as zHex will (should) be zero-terminated.
+ * \return 1 if Output buffer is insufficient to write the result string, otherwise 0 (success).
+ */
+int bin_to_hex(const unsigned char *data, size_t l, unsigned char *zHex, size_t *z);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/item.h b/src/item.h
@@ -0,0 +1,6 @@
+#ifndef _KEE_ITEM_H
+#define _KEE_ITEM_H
+
+useritem_preview(void 'item);
+
+#endif // _KEE_ITEM_H
diff --git a/src/kee.gresource.xml b/src/kee.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<gresources>
+ <gresource prefix="/org/defalsify/Kee">
+ <file preprocess="xml-stripblanks">main.ui</file>
+ </gresource>
+</gresources>
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,80 @@
+#include <string.h>
+#include <gtk/gtk.h>
+#include "ui.h"
+#include "db.h"
+
+
+static void act_scan(GSimpleAction *act, GVariant *param, GApplication *app) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "scan clicked");
+}
+
+static void act_quit(GSimpleAction *act, GVariant *param, GApplication *app) {
+ g_application_quit(app);
+}
+
+static void startup(GtkApplication *app, gpointer user_data) {
+ GMenu *menu_bar;
+ GMenu *menu;
+ GMenuItem *menu_item;
+ GMenuItem *menu_item_menu;
+ GSimpleAction *act;
+
+ menu_bar = g_menu_new();
+ menu_item_menu = g_menu_item_new("Menu", NULL);
+ menu = g_menu_new();
+ menu_item = g_menu_item_new("Scan", "app.scan");
+ g_menu_append_item(menu, menu_item);
+ g_object_unref(menu_item);
+
+ menu_item = g_menu_item_new("Quit", "app.quit");
+ g_menu_append_item(menu, menu_item);
+ g_object_unref(menu_item);
+
+ act = g_simple_action_new("quit", NULL);
+ g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(act));
+ g_signal_connect(act, "activate", G_CALLBACK(act_quit), app);
+
+ act = g_simple_action_new("scan", NULL);
+ g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(act));
+ g_signal_connect(act, "activate", G_CALLBACK(act_scan), app);
+
+ g_menu_item_set_submenu(menu_item_menu, G_MENU_MODEL(menu));
+ g_object_unref(menu);
+
+ g_menu_append_item(menu_bar, menu_item_menu);
+ g_object_unref(menu_item_menu);
+
+ gtk_application_set_menubar(GTK_APPLICATION(app), G_MENU_MODEL(menu_bar));
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "set up menus");
+}
+
+static void activate(GtkApplication *app, gpointer user_data) {
+ struct ui_container *ui;
+
+ ui = (struct ui_container*)user_data;
+ ui_build(app, ui);
+}
+
+static void deactivate(GtkApplication *app, gpointer user_data) {
+ struct ui_container *ui;
+
+ ui = (struct ui_container*)user_data;
+ ui_free(ui);
+}
+
+int main(int argc, char **argv) {
+ struct ui_container ui;
+ int r;
+ GtkApplication *app;
+ struct db_ctx db;
+
+ db_connect(&db, "./testdata_mdb");
+
+ app = gtk_application_new ("no.holbrook.example.Buidler", G_APPLICATION_DEFAULT_FLAGS);
+ g_signal_connect (app, "startup", G_CALLBACK (startup), &ui);
+ g_signal_connect (app, "activate", G_CALLBACK (activate), &ui);
+ g_signal_connect (app, "shutdown", G_CALLBACK (deactivate), &ui);
+ r = g_application_run (G_APPLICATION (app), argc, argv);
+ g_object_unref(app);
+ return r;
+}
diff --git a/src/resources.c b/src/resources.c
@@ -0,0 +1,324 @@
+#include <gio/gio.h>
+
+#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))
+# define SECTION __attribute__ ((section (".gresource.kee"), aligned (8)))
+#else
+# define SECTION
+#endif
+
+static const SECTION union { const guint8 data[1881]; const double alignment; void * const ptr;} kee_resource_data = {
+ "\107\126\141\162\151\141\156\164\000\000\000\000\000\000\000\000"
+ "\030\000\000\000\254\000\000\000\000\000\000\050\005\000\000\000"
+ "\000\000\000\000\003\000\000\000\004\000\000\000\004\000\000\000"
+ "\005\000\000\000\113\120\220\013\002\000\000\000\254\000\000\000"
+ "\004\000\114\000\260\000\000\000\264\000\000\000\161\002\354\244"
+ "\000\000\000\000\264\000\000\000\012\000\114\000\300\000\000\000"
+ "\304\000\000\000\324\265\002\000\377\377\377\377\304\000\000\000"
+ "\001\000\114\000\310\000\000\000\314\000\000\000\206\165\217\153"
+ "\004\000\000\000\314\000\000\000\007\000\166\000\330\000\000\000"
+ "\117\007\000\000\065\256\104\147\001\000\000\000\117\007\000\000"
+ "\004\000\114\000\124\007\000\000\130\007\000\000\157\162\147\057"
+ "\001\000\000\000\144\145\146\141\154\163\151\146\171\057\000\000"
+ "\004\000\000\000\057\000\000\000\000\000\000\000\155\141\151\156"
+ "\056\165\151\000\000\000\000\000\147\006\000\000\000\000\000\000"
+ "\074\077\170\155\154\040\166\145\162\163\151\157\156\075\042\061"
+ "\056\060\042\040\145\156\143\157\144\151\156\147\075\042\125\124"
+ "\106\055\070\042\077\076\012\074\151\156\164\145\162\146\141\143"
+ "\145\076\074\162\145\161\165\151\162\145\163\040\154\151\142\075"
+ "\042\147\164\153\042\040\166\145\162\163\151\157\156\075\042\064"
+ "\056\060\042\057\076\074\157\142\152\145\143\164\040\143\154\141"
+ "\163\163\075\042\107\164\153\127\151\156\144\157\167\042\040\151"
+ "\144\075\042\153\145\145\143\150\157\157\163\145\042\076\074\160"
+ "\162\157\160\145\162\164\171\040\156\141\155\145\075\042\143\150"
+ "\151\154\144\042\076\074\157\142\152\145\143\164\040\143\154\141"
+ "\163\163\075\042\107\164\153\114\151\163\164\102\157\170\042\057"
+ "\076\074\057\160\162\157\160\145\162\164\171\076\074\057\157\142"
+ "\152\145\143\164\076\074\157\142\152\145\143\164\040\143\154\141"
+ "\163\163\075\042\107\164\153\127\151\156\144\157\167\042\040\151"
+ "\144\075\042\153\145\145\163\143\141\156\042\076\074\160\162\157"
+ "\160\145\162\164\171\040\156\141\155\145\075\042\143\150\151\154"
+ "\144\042\076\074\157\142\152\145\143\164\040\143\154\141\163\163"
+ "\075\042\107\164\153\102\157\170\042\076\074\160\162\157\160\145"
+ "\162\164\171\040\156\141\155\145\075\042\157\162\151\145\156\164"
+ "\141\164\151\157\156\042\076\166\145\162\164\151\143\141\154\074"
+ "\057\160\162\157\160\145\162\164\171\076\074\143\150\151\154\144"
+ "\076\074\157\142\152\145\143\164\040\143\154\141\163\163\075\042"
+ "\107\164\153\111\155\141\147\145\042\076\074\160\162\157\160\145"
+ "\162\164\171\040\156\141\155\145\075\042\151\143\157\156\055\156"
+ "\141\155\145\042\076\147\164\153\055\155\151\163\163\151\156\147"
+ "\055\151\155\141\147\145\074\057\160\162\157\160\145\162\164\171"
+ "\076\074\160\162\157\160\145\162\164\171\040\156\141\155\145\075"
+ "\042\165\163\145\055\146\141\154\154\142\141\143\153\042\076\061"
+ "\074\057\160\162\157\160\145\162\164\171\076\074\057\157\142\152"
+ "\145\143\164\076\074\057\143\150\151\154\144\076\074\143\150\151"
+ "\154\144\076\074\160\154\141\143\145\150\157\154\144\145\162\057"
+ "\076\074\057\143\150\151\154\144\076\074\143\150\151\154\144\076"
+ "\074\160\154\141\143\145\150\157\154\144\145\162\057\076\074\057"
+ "\143\150\151\154\144\076\074\057\157\142\152\145\143\164\076\074"
+ "\057\160\162\157\160\145\162\164\171\076\074\057\157\142\152\145"
+ "\143\164\076\074\157\142\152\145\143\164\040\143\154\141\163\163"
+ "\075\042\107\164\153\127\151\156\144\157\167\042\040\151\144\075"
+ "\042\153\145\145\165\156\154\157\143\153\042\076\074\160\162\157"
+ "\160\145\162\164\171\040\156\141\155\145\075\042\143\150\151\154"
+ "\144\042\076\074\157\142\152\145\143\164\040\143\154\141\163\163"
+ "\075\042\107\164\153\102\157\170\042\040\151\144\075\042\153\145"
+ "\145\165\156\154\157\143\153\142\157\170\042\076\074\160\162\157"
+ "\160\145\162\164\171\040\156\141\155\145\075\042\157\162\151\145"
+ "\156\164\141\164\151\157\156\042\076\166\145\162\164\151\143\141"
+ "\154\074\057\160\162\157\160\145\162\164\171\076\074\143\150\151"
+ "\154\144\076\074\157\142\152\145\143\164\040\143\154\141\163\163"
+ "\075\042\107\164\153\105\156\164\162\171\042\040\151\144\075\042"
+ "\153\145\145\165\156\154\157\143\153\160\141\163\163\160\150\162"
+ "\141\163\145\042\076\074\160\162\157\160\145\162\164\171\040\156"
+ "\141\155\145\075\042\146\157\143\165\163\141\142\154\145\042\076"
+ "\061\074\057\160\162\157\160\145\162\164\171\076\074\057\157\142"
+ "\152\145\143\164\076\074\057\143\150\151\154\144\076\074\143\150"
+ "\151\154\144\076\074\157\142\152\145\143\164\040\143\154\141\163"
+ "\163\075\042\107\164\153\102\165\164\164\157\156\042\040\151\144"
+ "\075\042\153\145\145\165\156\154\157\143\153\163\165\142\155\151"
+ "\164\042\076\074\160\162\157\160\145\162\164\171\040\156\141\155"
+ "\145\075\042\154\141\142\145\154\042\040\164\162\141\156\163\154"
+ "\141\164\141\142\154\145\075\042\061\042\076\142\165\164\164\157"
+ "\156\074\057\160\162\157\160\145\162\164\171\076\074\160\162\157"
+ "\160\145\162\164\171\040\156\141\155\145\075\042\146\157\143\165"
+ "\163\141\142\154\145\042\076\061\074\057\160\162\157\160\145\162"
+ "\164\171\076\074\160\162\157\160\145\162\164\171\040\156\141\155"
+ "\145\075\042\162\145\143\145\151\166\145\163\055\144\145\146\141"
+ "\165\154\164\042\076\061\074\057\160\162\157\160\145\162\164\171"
+ "\076\074\057\157\142\152\145\143\164\076\074\057\143\150\151\154"
+ "\144\076\074\143\150\151\154\144\076\074\160\154\141\143\145\150"
+ "\157\154\144\145\162\057\076\074\057\143\150\151\154\144\076\074"
+ "\057\157\142\152\145\143\164\076\074\057\160\162\157\160\145\162"
+ "\164\171\076\074\057\157\142\152\145\143\164\076\074\157\142\152"
+ "\145\143\164\040\143\154\141\163\163\075\042\107\164\153\127\151"
+ "\156\144\157\167\042\040\151\144\075\042\153\145\145\166\151\145"
+ "\167\042\076\074\160\162\157\160\145\162\164\171\040\156\141\155"
+ "\145\075\042\143\150\151\154\144\042\076\074\157\142\152\145\143"
+ "\164\040\143\154\141\163\163\075\042\107\164\153\107\162\151\144"
+ "\042\040\151\144\075\042\166\151\145\167\042\076\074\143\150\151"
+ "\154\144\076\074\157\142\152\145\143\164\040\143\154\141\163\163"
+ "\075\042\107\164\153\111\155\141\147\145\042\076\074\160\162\157"
+ "\160\145\162\164\171\040\156\141\155\145\075\042\165\163\145\055"
+ "\146\141\154\154\142\141\143\153\042\076\061\074\057\160\162\157"
+ "\160\145\162\164\171\076\074\154\141\171\157\165\164\076\074\160"
+ "\162\157\160\145\162\164\171\040\156\141\155\145\075\042\143\157"
+ "\154\165\155\156\042\076\060\074\057\160\162\157\160\145\162\164"
+ "\171\076\074\160\162\157\160\145\162\164\171\040\156\141\155\145"
+ "\075\042\162\157\167\042\076\060\074\057\160\162\157\160\145\162"
+ "\164\171\076\074\160\162\157\160\145\162\164\171\040\156\141\155"
+ "\145\075\042\143\157\154\165\155\156\055\163\160\141\156\042\076"
+ "\062\074\057\160\162\157\160\145\162\164\171\076\074\160\162\157"
+ "\160\145\162\164\171\040\156\141\155\145\075\042\162\157\167\055"
+ "\163\160\141\156\042\076\062\074\057\160\162\157\160\145\162\164"
+ "\171\076\074\057\154\141\171\157\165\164\076\074\057\157\142\152"
+ "\145\143\164\076\074\057\143\150\151\154\144\076\074\143\150\151"
+ "\154\144\076\074\160\154\141\143\145\150\157\154\144\145\162\057"
+ "\076\074\057\143\150\151\154\144\076\074\143\150\151\154\144\076"
+ "\074\160\154\141\143\145\150\157\154\144\145\162\057\076\074\057"
+ "\143\150\151\154\144\076\074\143\150\151\154\144\076\074\160\154"
+ "\141\143\145\150\157\154\144\145\162\057\076\074\057\143\150\151"
+ "\154\144\076\074\143\150\151\154\144\076\074\160\154\141\143\145"
+ "\150\157\154\144\145\162\057\076\074\057\143\150\151\154\144\076"
+ "\074\143\150\151\154\144\076\074\160\154\141\143\145\150\157\154"
+ "\144\145\162\057\076\074\057\143\150\151\154\144\076\074\057\157"
+ "\142\152\145\143\164\076\074\057\160\162\157\160\145\162\164\171"
+ "\076\074\057\157\142\152\145\143\164\076\074\057\151\156\164\145"
+ "\162\146\141\143\145\076\012\000\000\050\165\165\141\171\051\113"
+ "\145\145\057\000\003\000\000\000" };
+
+static GStaticResource static_resource = { kee_resource_data.data, sizeof (kee_resource_data.data) - 1 /* nul terminator */, NULL, NULL, NULL };
+
+G_MODULE_EXPORT
+GResource *kee_get_resource (void);
+GResource *kee_get_resource (void)
+{
+ return g_static_resource_get_resource (&static_resource);
+}
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_CONSTRUCTOR_H__
+#define __G_CONSTRUCTOR_H__
+
+/*
+ If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and
+ destructors, in a usable way, including e.g. on library unload. If not you're on
+ your own.
+
+ Some compilers need #pragma to handle this, which does not work with macros,
+ so the way you need to use this is (for constructors):
+
+ #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+ #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor)
+ #endif
+ G_DEFINE_CONSTRUCTOR(my_constructor)
+ static void my_constructor(void) {
+ ...
+ }
+
+*/
+
+#ifndef __GTK_DOC_IGNORE__
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void);
+#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void);
+
+#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
+/* Visual studio 2008 and later has _Pragma */
+
+/*
+ * Only try to include gslist.h if not already included via glib.h,
+ * so that items using gconstructor.h outside of GLib (such as
+ * GResources) continue to build properly.
+ */
+#ifndef __G_LIB_H__
+#include "gslist.h"
+#endif
+
+#include <stdlib.h>
+
+#define G_HAS_CONSTRUCTORS 1
+
+/* We do some weird things to avoid the constructors being optimized
+ * away on VS2015 if WholeProgramOptimization is enabled. First we
+ * make a reference to the array from the wrapper to make sure its
+ * references. Then we use a pragma to make sure the wrapper function
+ * symbol is always included at the link stage. Also, the symbols
+ * need to be extern (but not dllexport), even though they are not
+ * really used from another object file.
+ */
+
+/* We need to account for differences between the mangling of symbols
+ * for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed
+ * with an underscore but symbols on x64/ARM/ARM64 are not.
+ */
+#ifdef _M_IX86
+#define G_MSVC_SYMBOL_PREFIX "_"
+#else
+#define G_MSVC_SYMBOL_PREFIX ""
+#endif
+
+#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX)
+#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX)
+
+#define G_MSVC_CTOR(_func,_sym_prefix) \
+ static void _func(void); \
+ extern int (* _array ## _func)(void); \
+ int _func ## _wrapper(void); \
+ int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \
+ __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \
+ __pragma(section(".CRT$XCU",read)) \
+ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper;
+
+#define G_MSVC_DTOR(_func,_sym_prefix) \
+ static void _func(void); \
+ extern int (* _array ## _func)(void); \
+ int _func ## _constructor(void); \
+ int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \
+ __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \
+ __pragma(section(".CRT$XCU",read)) \
+ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor;
+
+#elif defined (_MSC_VER)
+
+#define G_HAS_CONSTRUCTORS 1
+
+/* Pre Visual studio 2008 must use #pragma section */
+#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
+#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
+
+#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
+ section(".CRT$XCU",read)
+#define G_DEFINE_CONSTRUCTOR(_func) \
+ static void _func(void); \
+ static int _func ## _wrapper(void) { _func(); return 0; } \
+ __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper;
+
+#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
+ section(".CRT$XCU",read)
+#define G_DEFINE_DESTRUCTOR(_func) \
+ static void _func(void); \
+ static int _func ## _constructor(void) { atexit (_func); return 0; } \
+ __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
+
+#elif defined(__SUNPRO_C)
+
+/* This is not tested, but i believe it should work, based on:
+ * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c
+ */
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
+#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
+
+#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
+ init(_func)
+#define G_DEFINE_CONSTRUCTOR(_func) \
+ static void _func(void);
+
+#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
+ fini(_func)
+#define G_DEFINE_DESTRUCTOR(_func) \
+ static void _func(void);
+
+#else
+
+/* constructors not supported for this compiler */
+
+#endif
+
+#endif /* __GTK_DOC_IGNORE__ */
+#endif /* __G_CONSTRUCTOR_H__ */
+
+#ifdef G_HAS_CONSTRUCTORS
+
+#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(keeresource_constructor)
+#endif
+G_DEFINE_CONSTRUCTOR(keeresource_constructor)
+#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA
+#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(keeresource_destructor)
+#endif
+G_DEFINE_DESTRUCTOR(keeresource_destructor)
+
+#else
+#warning "Constructor not supported on this compiler, linking in resources will not work"
+#endif
+
+static void keeresource_constructor (void)
+{
+ g_static_resource_init (&static_resource);
+}
+
+static void keeresource_destructor (void)
+{
+ g_static_resource_fini (&static_resource);
+}
diff --git a/src/rpc.h b/src/rpc.h
@@ -0,0 +1,19 @@
+#ifndef _KEE_RPC_H
+#define _KEE_RPC_H
+
+#include <gtk/gtk.h>
+
+#ifndef RPC_BUFFER_SIZE
+#define RPC_BUFFER_SIZE 4096
+#endif
+
+#ifndef RPC_COMMAND_SIZE
+#define RPC_COMMAND_SIZE 1048576
+#endif
+
+
+struct kee_rpc {
+ GApplication *gap;
+}
+
+#endif // _KEE_RPC_H
diff --git a/src/settings.c b/src/settings.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <basedir.h>
+#include <sys/stat.h>
+
+#include "err.h"
+#include "debug.h"
+#include "settings.h"
+
+
+/**
+ * \todo make xdg optional
+ */
+int settings_new_from_xdg(struct kee_settings *z) {
+ return ERR_OK;
+}
+
+int settings_init(struct kee_settings *z) {
+ int r;
+ char s[1024];
+
+ r = mkdir((char*)z->data, S_IRUSR | S_IWUSR);
+ if (r) {
+ if (errno != EEXIST) {
+ debug_log(DEBUG_ERROR, strerror(errno));
+ return r;
+ }
+ }
+ sprintf(s, "datadir: %s\nrundir: %s", z->data, z->run);
+ debug_log(DEBUG_DEBUG, s);
+
+ return ERR_OK;
+}
+
+unsigned char *settings_get(struct kee_settings *z, enum SettingsType typ) {
+ switch(typ) {
+ case SETTINGS_DATA:
+ return z->data;
+ break;
+ case SETTINGS_RUN:
+ return z->run;
+ break;
+ case SETTINGS_LOCKTIME:
+ return z->locktime;
+ break;
+ default:
+ return (unsigned char*)"";
+ }
+}
+
+int settings_set(struct kee_settings *z, enum SettingsType typ, unsigned char* v) {
+ switch(typ) {
+ case SETTINGS_DATA:
+ z->data = v;
+ break;
+ case SETTINGS_RUN:
+ z->run = v;
+ break;
+ case SETTINGS_LOCKTIME:
+ z->locktime = v;
+ break;
+ default:
+ return ERR_FAIL;
+ }
+ return ERR_OK;
+}
diff --git a/src/settings.h b/src/settings.h
@@ -0,0 +1,54 @@
+#ifndef _KEE_SETTINGS
+#define _KEE_SETTINGS
+
+
+/**
+ * \brief Encapsulates settings for application.
+ *
+ */
+struct kee_settings {
+ unsigned char *data;
+ unsigned char *run;
+ unsigned char *locktime;
+};
+
+/**
+ *
+ * \brief Numeric code used to set and get specific settings items.
+ */
+enum SettingsType {
+ /// Data directory
+ SETTINGS_DATA = 0x01,
+ /// Runtime directory
+ SETTINGS_RUN = 0x02,
+ /// Milliseconds a key will stay unlocked since last application use.
+ SETTINGS_LOCKTIME = 0x10,
+};
+
+/**
+ * \param Prepare system for reading and/or writing settings.
+ *
+ * Create necessary directories.
+ *
+ * \return ERR_OK if successful, or \c errno of any error the has occurred.
+ */
+int settings_init(struct kee_settings *z);
+
+/**
+ * \brief Retrieve a single settings entry.
+ *
+ * \param typ Settings entry type to retrieve.
+ * \return Result string value. An empty string is returned if no value is set.
+ */
+unsigned char *settings_get(struct kee_settings *z, enum SettingsType typ);
+
+/**
+ * \brief Set a single settings entry.
+ *
+ * \param typ Settings entry type to set.
+ * \param v Value to set for entry.
+ * \return ERR_OK if successful, ERR_FAIL if invalid settings type.
+ */
+int settings_set(struct kee_settings *z, enum SettingsType typ, unsigned char* v);
+
+#endif // KEE_SETTINGS
diff --git a/src/term_debug.c b/src/term_debug.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include "debug.h"
+
+
+void debugLog(enum debugLevel level, const char *s) {
+ fprintf(stderr, "%d: %s\n", level, s);
+}
diff --git a/src/transport.c b/src/transport.c
@@ -0,0 +1,143 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <b64/cencode.h>
+#include <b64/cdecode.h>
+#include <zlib.h>
+#include "transport.h"
+#include "err.h"
+
+
+static int pack_compress(char *in, size_t in_len, char *out, size_t *out_len) {
+ int r;
+ z_stream z;
+
+ z.zalloc = Z_NULL;
+ z.zfree = Z_NULL;
+ z.opaque = Z_NULL;
+ z.data_type = Z_TEXT;
+ z.next_in = (unsigned char*)in;
+ z.avail_in = in_len;
+ z.next_out = (unsigned char*)out;
+ z.avail_out = *out_len;
+
+ r = deflateInit(&z, Z_BEST_COMPRESSION);
+ if (r != Z_OK) {
+ return 1;
+ }
+ r = deflate(&z, Z_FINISH);
+ if (r != Z_STREAM_END) {
+ return 2;
+ }
+ *out_len = z.total_out;
+
+ return 0;
+}
+
+static int pack_encode(char *in, size_t in_len, char *out, size_t *out_len) {
+ char *p;
+ int r;
+ base64_encodestate bst;
+
+ base64_init_encodestate(&bst);
+
+ p = out;
+ r = base64_encode_block(in, in_len, p, &bst);
+ if (r == 0) {
+ return 1;
+ }
+ *out_len = r;
+ p += r;
+ r = base64_encode_blockend(p, &bst);
+ if (r == 0) {
+ return 1;
+ }
+ *out_len += r;
+
+ return 0;
+
+}
+
+static int unpack_decompress(char *in, size_t in_len, char *out, size_t *out_len) {
+ int r;
+ z_stream z;
+
+ z.zalloc = Z_NULL;
+ z.zfree = Z_NULL;
+ z.opaque = Z_NULL;
+ z.data_type = Z_TEXT;
+ z.next_in = (unsigned char*)in;
+ z.avail_in = in_len;
+ z.next_out = (unsigned char*)out;
+ z.avail_out = *out_len;
+
+ r = inflateInit(&z);
+ if (r != Z_OK) {
+ return 1;
+ }
+ r = inflate(&z, Z_FINISH);
+ if (r != Z_STREAM_END) {
+ return 2;
+ }
+ *out_len = z.total_out;
+
+ return 0;
+}
+
+static int unpack_decode(char *in, size_t in_len, char *out, size_t *out_len) {
+ int r;
+ base64_decodestate dst;
+
+ base64_init_decodestate(&dst);
+ r = base64_decode_block(in, in_len, out, &dst);
+ if (r == 0) {
+ return 1;
+ }
+ *out_len = r;
+
+ return 0;
+}
+
+int pack(char *in, size_t in_len, char *out, size_t *out_len) {
+ int r;
+ char *buf;
+
+ buf = malloc(*out_len);
+
+ r = pack_compress(in, in_len, buf, out_len);
+ if (r) {
+ free(buf);
+ return ERR_FAIL;
+ }
+
+ r = pack_encode(buf, *out_len, out, out_len);
+ if (r) {
+ free(buf);
+ return ERR_FAIL;
+ }
+
+ free(buf);
+ return ERR_OK;
+}
+
+int unpack(char *in, size_t in_len, char *out, size_t *out_len) {
+ int r;
+ char *buf;
+
+ buf = malloc(*out_len);
+
+ r = unpack_decode(in, in_len, buf, out_len);
+ if (r) {
+ free(buf);
+ return ERR_FAIL;
+ }
+
+ r = unpack_decompress(buf, *out_len, out, out_len);
+ if (r) {
+ free(buf);
+ return ERR_FAIL;
+ }
+
+ free(buf);
+ return ERR_OK;
+}
diff --git a/src/transport.h b/src/transport.h
@@ -0,0 +1,40 @@
+#ifndef _TRANSPORT_H
+#define _TRANSPORT_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *
+ * \brief Compress and encode serialized data for transport in text format.
+ *
+ * \param in Input data
+ * \param in_len Length of input data
+ * \param out If successful, contains encoded result string.
+ * \param out_len Must contain available bytes in \c out. Will contain length of data written to \c out if successful.
+ *
+ * \return ERR_OK if successful, ERR_FAIL if not.
+ */
+int pack(char *in, size_t in_len, char *out, size_t *out_len);
+
+/**
+ *
+ * \brief Decode and decompress data encoded in text format for transport.
+ *
+ * \param in Input data
+ * \param in_len Length of input data
+ * \param out If successful, contains encoded result string.
+ * \param out_len Must contain available bytes in \c out. Will contain length of data written to \c out if successful.
+ *
+ * \return ERR_OK if successful, ERR_FAIL if not.
+ */
+int unpack(char *in, size_t in_len, char *out, size_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _TRANSPORT_H
diff --git a/src/ui.c b/src/ui.c
@@ -0,0 +1,97 @@
+#include <gtk/gtk.h>
+#include "ui.h"
+
+/***
+ * \todo change file to resource
+ */
+
+static void new_item(GtkListItemFactory *factory, GtkListItem *item, gpointer user_data) {
+}
+
+static void unlock_click(GtkWidget *button, gpointer user_data) {
+ struct ui_container *ui;
+
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "click");
+
+ ui = (struct ui_container*)user_data;
+ gtk_stack_set_visible_child(ui->stack, GTK_WIDGET(ui->front_view));
+}
+
+GtkWidget* ui_build_unlock(struct ui_container *ui) {
+ GtkWidget *box;
+ GtkWidget *entry;
+ GtkWidget *button;
+
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
+
+ entry = gtk_entry_new();
+ gtk_box_append(GTK_BOX(box), entry);
+
+ button = gtk_button_new_with_label("create");
+ gtk_box_append(GTK_BOX(box), button);
+ g_signal_connect (button, "clicked", G_CALLBACK (unlock_click), ui);
+
+ return GTK_WIDGET(box);
+}
+
+GtkWidget* ui_build_view(struct ui_container *ui) {
+ GtkSelectionModel *sel;
+ GtkListItemFactory *factory;
+
+ ui->front_list = G_LIST_MODEL(gtk_string_list_new(NULL));
+ sel = GTK_SELECTION_MODEL(gtk_single_selection_new(ui->front_list));
+ factory = gtk_signal_list_item_factory_new();
+ g_signal_connect(factory, "setup", G_CALLBACK(new_item), NULL);
+ ui->front_view = GTK_LIST_VIEW(gtk_list_view_new(GTK_SELECTION_MODEL(sel), factory));
+
+ return GTK_WIDGET(ui->front_view);
+}
+
+void ui_build(GtkApplication *app, struct ui_container *ui) {
+ GtkWidget *unlock;
+ GtkWidget *view;
+
+ ui->win = GTK_APPLICATION_WINDOW(gtk_application_window_new (app));
+ ui->stack = GTK_STACK(gtk_stack_new());
+
+ gtk_window_set_title (GTK_WINDOW (ui->win), "kee");
+ gtk_window_set_default_size (GTK_WINDOW (ui->win), 800, 600);
+
+ gtk_window_set_child(GTK_WINDOW(ui->win), GTK_WIDGET(ui->stack));
+ gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(ui->win), TRUE);
+
+ unlock = ui_build_unlock(ui);
+ gtk_stack_add_child(ui->stack, unlock);
+ view = ui_build_view(ui);
+ gtk_stack_add_child(ui->stack, view);
+
+ gtk_stack_set_visible_child(GTK_STACK(ui->stack), unlock);
+ //gtk_stack_set_visible_child(GTK_STACK(ui->stack), view);
+
+ gtk_window_present(GTK_WINDOW (ui->win));
+}
+
+//void ui_build_from_resource(GtkApplication *app, struct ui_container *ui) {
+// GtkBuilder *build;
+// GtkWidget *unlock;
+//
+// build = gtk_builder_new_from_resource("/org/defalsify/Kee/main.ui");
+// //ui->view = GTK_WINDOW(gtk_builder_get_object(build, "keechoose"));
+// unlock = GTK_WINDOW(gtk_builder_get_object(build, "keeunlock"));
+// if (!unlock) {
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "unlock widget could not load");
+// }
+//
+// ui->win = GTK_APPLICATION_WINDOW(gtk_application_window_new (app));
+// gtk_window_set_child(GTK_WINDOW(ui->win), GTK_WIDGET(unlock));
+//
+// gtk_window_present(GTK_WINDOW (ui->win));
+//}
+//
+
+
+void ui_free(struct ui_container *ui) {
+ //g_object_unref(ui->view);
+ //g_object_unref(ui->unlock);
+ //g_object_unref(ui->win);
+}
diff --git a/src/ui.h b/src/ui.h
@@ -0,0 +1,16 @@
+#ifndef _UI_H
+#define _UI_H
+
+#include <gtk/gtk.h>
+
+struct ui_container {
+ GtkApplicationWindow *win;
+ GtkStack *stack;
+ GListModel *front_list;
+ GtkListView *front_view;
+};
+
+void ui_build(GtkApplication *app, struct ui_container *ui);
+void ui_free(struct ui_container *ui);
+
+#endif // _UI_H
diff --git a/testdata.py b/testdata.py
@@ -0,0 +1,56 @@
+import lmdb
+import tempfile
+import logging
+import os
+import shutil
+import hashlib
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+d = os.path.dirname(__file__)
+d = os.path.join(d, 'testdata_mdb')
+logg.info('using ' + d)
+
+try:
+ shutil.rmtree(d)
+except FileNotFoundError:
+ pass
+os.makedirs(d)
+env = lmdb.open(d)
+dbi = env.open_db()
+
+#pfx = b'\x00\x00\x00'
+#pfx_two = b'\x00\x00\x01'
+
+keys = []
+vals = [
+ b"\x03foo\x03bar",
+ b"\x05xyzzy\x05plugh",
+ b"\x04inky\x05pinky",
+ b"\x06blinky\x05clyde",
+ ]
+
+for i in range(len(vals)):
+ k = b'\x01' + i.to_bytes(16, 'big')
+ #k += os.urandom(32)
+ h = hashlib.sha256()
+ h.update(vals[i])
+ k += h.digest()
+ keys.append(k)
+
+with env.begin(write=True) as tx:
+ for i in range(len(vals)):
+ v = i.to_bytes(2, byteorder='big')
+ tx.put(keys[i], vals[i])
+ #tx.put(keys[i], vals[i].encode('utf-8'))
+ #tx.put(pfx + v, vals[i].encode('utf-8'))
+ #tx.put(pfx_two + b'\x00\x00', b'xyzzy')
+ #tx.put(pfx_two + b'\x00\x01', b'plugh')
+
+with env.begin() as tx:
+ c = tx.cursor()
+ #if not c.set_range(b'\x00\x00\x01'):
+ # raise ValueError("no key")
+ for k, v in c:
+ logg.debug('have {} {}'.format(k, v))