Use vendored jsqlite
ober
11ee796b0a05fecef2b612ff66f837d95cdf7259
--- a/.jerbuild +++ b/.jerbuild @@ -7,5 +7,5 @@ (entry "signal/main.ss") (output "jerboa-signal") -(libdirs "." "../jsqlite/src") +(libdirs "." "vendor/jsqlite/src") (static-native #t) --- a/Makefile +++ b/Makefile @@ -8,8 +8,6 @@ JERBUILD ?= $(shell if [ -x ./jerbuild ] && [ -x ./jerboa ]; then \ printf '%s\n' ./jerbuild; \ elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ] && [ -x "$(JERBOA_TOOL_DIR)/jerboa" ]; then \ printf '%s\n' "$(JERBOA_TOOL_DIR)/jerbuild"; \ -elif [ -x ../jerboa/dist/jerbuild ] && [ -x ../jerboa/dist/jerboa ]; then \ - printf '%s\n' ../jerboa/dist/jerbuild; \ elif command -v jerbuild >/dev/null 2>&1 && command -v jerboa >/dev/null 2>&1; then \ command -v jerbuild; \ else \ @@ -17,12 +15,16 @@ else \ fi) JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null) -JSQLITE_SRC ?= $(CURDIR)/../jsqlite/src +VENDOR ?= $(CURDIR)/vendor +JSQLITE_REPO ?= $(VENDOR)/jsqlite +JSQLITE_URL ?= https://git.sr.ht/~lisp/jsqlite +JSQLITE_SRC ?= $(JSQLITE_REPO)/src LIBDIRS = --libdirs $(CURDIR):$(JSQLITE_SRC):$(JH)/lib DEFAULT_JERBOA_NATIVE_A = $(JH)/jerboa-native-rs/target/release/libjerboa_native.a JERBOA_NATIVE_A ?= $(DEFAULT_JERBOA_NATIVE_A) +JERBOA_NATIVE_LIB ?= $(JH)/jerboa-native-rs/target/release/libjerboa_native.$(TUI_SHIM_EXT) export JERBOA_NATIVE_A -JEXEC = $(JERBUILD) exec $(LIBDIRS) +JEXEC = JERBOA_NATIVE_LIB="$(JERBOA_NATIVE_LIB)" $(JERBUILD) exec $(LIBDIRS) BIN := jerboa-signal BIN_DIR := $(HOME)/.local/bin TUI_SHIM_DIR := $(CURDIR)/vendor/termbox2 @@ -33,16 +35,14 @@ else TUI_SHIM_EXT := so endif TUI_SHIM := $(TUI_SHIM_DIR)/signal_tui_shim.$(TUI_SHIM_EXT) -LOG_SHIM := signal_log_shim.$(TUI_SHIM_EXT) -SQLCIPHER_PREFIX := $(shell brew --prefix sqlcipher 2>/dev/null) -.PHONY: all build binary run run-tui test install install-log-shim clean help vendor-deps tui-shim log-shim ensure-jerboa-tools ensure-jerboa-native +.PHONY: all build binary run run-tui test install clean help vendor-deps tui-shim ensure-jerboa-tools ensure-jerboa-native ensure-jsqlite .DEFAULT_GOAL := help all: binary # Standalone native binary via .jerbuild (entry signal/main.ss -> jerboa-signal). -binary: ensure-jerboa-native +binary: ensure-jerboa-native ensure-jsqlite $(JERBUILD) build build: binary @@ -72,22 +72,19 @@ install: binary test ! -f signal_tui_shim.$(TUI_SHIM_EXT) || install -m 0755 signal_tui_shim.$(TUI_SHIM_EXT) $(BIN_DIR)/signal_tui_shim.$(TUI_SHIM_EXT) @echo "Installed $(BIN) to $(BIN_DIR)/$(BIN)" -install-log-shim: log-shim - @if [ -f "$(LOG_SHIM)" ]; then \ - mkdir -p $(BIN_DIR); \ - install -m 0755 $(LOG_SHIM) $(BIN_DIR)/$(LOG_SHIM); \ - echo "Installed legacy $(LOG_SHIM) to $(BIN_DIR)/$(LOG_SHIM)"; \ - else \ - echo "No legacy $(LOG_SHIM) built."; \ - fi - clean: rm -f $(BIN) rm -f signal_tui_shim.dylib signal_tui_shim.so - rm -f signal_log_shim.dylib signal_log_shim.so find signal \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null || true -vendor-deps: vendor/termbox2 +vendor-deps: ensure-jsqlite vendor/termbox2 + +ensure-jsqlite: + @if [ ! -f "$(JSQLITE_SRC)/jsqlite/api.ss" ]; then \ + mkdir -p "$(VENDOR)"; \ + git clone --depth 1 "$(JSQLITE_URL)" "$(JSQLITE_REPO)"; \ + fi + @test -f "$(JSQLITE_SRC)/jsqlite/api.ss" vendor/termbox2: mkdir -p vendor @@ -98,8 +95,6 @@ ensure-jerboa-tools: echo "=== Using project-local ./jerbuild ==="; \ elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ] && [ -x "$(JERBOA_TOOL_DIR)/jerboa" ]; then \ echo "=== Using downloaded Jerboa toolchain: $(JERBOA_TOOL_DIR) ==="; \ - elif [ -x ../jerboa/dist/jerbuild ] && [ -x ../jerboa/dist/jerboa ]; then \ - echo "=== Using sibling Jerboa build: ../jerboa/dist/jerbuild ==="; \ elif command -v jerbuild >/dev/null 2>&1 && command -v jerboa >/dev/null 2>&1; then \ echo "=== Using Jerboa toolchain from PATH: $$(command -v jerbuild) ==="; \ else \ @@ -141,19 +136,6 @@ tui-shim: vendor/termbox2 signal/tui/signal_tui_shim.c cp $(TUI_SHIM) signal_tui_shim.$(TUI_SHIM_EXT) -# Legacy encrypted-logging shim (SQLCipher). New logs use the jsqlite encrypted -# backend; build this only to open pre-jsqlite SQLCipher log files. -log-shim: - @if [ -z "$(SQLCIPHER_PREFIX)" ]; then \ - echo "log-shim: sqlcipher not found; skipping legacy SQLCipher support."; \ - echo " enable legacy logs with: brew install sqlcipher && make log-shim"; \ - else \ - cc -shared -fPIC \ - -I$(SQLCIPHER_PREFIX)/include/sqlcipher \ - -L$(SQLCIPHER_PREFIX)/lib -lsqlcipher \ - -o $(LOG_SHIM) signal/log_shim.c && echo "Built $(LOG_SHIM)"; \ - fi - help: @echo "jerboa-signal -- Signal client over signal-cli" @echo "" @@ -163,10 +145,8 @@ help: @echo " run-tui Build shim and start the TUI" @echo " test Run tests" @echo " install Install ./jerboa-signal to ~/.local/bin" - @echo " install-log-shim Install optional legacy SQLCipher log shim" @echo " ensure-jerboa-tools Ensure jerboa/jerbuild are available" @echo " tui-shim Build the termbox2 TUI shim" - @echo " log-shim Build the legacy SQLCipher log shim" @echo " clean Remove build artifacts" @echo "" @echo "Prerequisite: signal-cli must be linked to your Signal account." --- a/README.md +++ b/README.md @@ -24,10 +24,8 @@ jerboa-signal tui [-a +PHONE] # terminal UI shell Older 0.14.4.1 builds drop current sealed-sender receive envelopes with `getServerGuid(...) must not be null`. - Jerboa build tools. `make binary`/`make install` use project-local - `./jerbuild` first, then `.jerboa/bin`, then `../jerboa/dist`, then `PATH`, - and download the matching release artifact if none are available. -- Optional, for the encrypted message log: `brew install sqlcipher`, then - `make log-shim` (without it the TUI runs fine, just without logging) + `./jerbuild` first, then `.jerboa/bin`, then `PATH`, and download the + matching release artifact if none are available. ## One-time setup: link as a secondary device @@ -102,10 +100,10 @@ too. Use q, Esc, or Ctrl-C to quit. ## Encrypted message log The TUI can log every message to an encrypted SQLite database so you keep a copy -even when the sender later deletes it ("delete for everyone"). New logs use +even when the sender later deletes it ("delete for everyone"). Logs use `jsqlite` for the SQLite engine and a Jerboa-native encrypted container around the database image; contents and metadata are unreadable without your -passphrase. +passphrase. No native SQLite or SQLCipher library is loaded. Enable it: @@ -113,14 +111,6 @@ Enable it: ./jerboa-signal tui # prompts: passphrase for encrypted message log ``` -Existing SQLCipher log files are still supported as a legacy fallback. To open -those old files, install SQLCipher and build the optional shim: - -```sh -brew install sqlcipher -make log-shim -``` - At startup the TUI asks for a passphrase (echo off). Enter one to log; leave it blank to skip logging for that session. To run unattended, set `JERBOA_SIGNAL_DB_KEY` instead and the prompt is skipped. @@ -156,7 +146,6 @@ Modules: - `signal/store.ss` — persistent local state (deleted-conversation list) - `signal/capture.ss` — normalizes a notification into an encrypted log row - `signal/logdb.ss` + `signal/log_crypto.ss` — encrypted jsqlite log backend -- `signal/log_shim.c` — optional legacy SQLCipher log reader - `signal/tui/` — termbox2 FFI and the first terminal shell ## License --- a/docs/full-port.md +++ b/docs/full-port.md @@ -303,7 +303,7 @@ These names are placeholders. The implementation must map them to actual current ## 8. Storage Plan With jsqlite -Use `~/mine/jsqlite` as the SQLite replacement. Do not use C SQLite unless the user explicitly changes this decision. +Use vendored `jsqlite` as the SQLite replacement. Do not use C SQLite unless the user explicitly changes this decision. Storage goals: deleted file mode 100644 --- a/signal/log_shim.c +++ /dev/null @@ -1,225 +0,0 @@ -/* signal_log_shim.c -- SQLCipher-backed encrypted message log for jerboa-signal. - * - * Captures every Signal event as an append-only row so messages survive even - * when the sender later deletes them ("delete for everyone" / remote delete): - * a remote-delete is just another row and never touches the original. - * - * The database is a real encrypted SQLite file (SQLCipher, AES-256) -- contents - * AND metadata are unreadable without the key. - * - * Build: - * cc -shared -fPIC \ - * -I$(brew --prefix sqlcipher)/include/sqlcipher \ - * -L$(brew --prefix sqlcipher)/lib -lsqlcipher \ - * -o signal_log_shim.dylib signal/log_shim.c - * - * Self-test: - * cc -DSIGNAL_LOG_TEST -I... -L... -lsqlcipher signal/log_shim.c -o /tmp/logtest - */ - -#include <sqlite3.h> /* SQLCipher's sqlite3.h (note the -I path) */ -#include <string.h> -#include <unistd.h> /* getpass */ - -/* SQLCipher exports these, but sqlite3.h only declares them under - * SQLITE_HAS_CODEC. Declare directly so we link against libsqlcipher's - * symbols without depending on the header guard. */ -extern int sqlite3_key(sqlite3 *db, const void *pKey, int nKey); - -static const char *SCHEMA = - "CREATE TABLE IF NOT EXISTS messages (" - " id INTEGER PRIMARY KEY AUTOINCREMENT," - " logged_at INTEGER DEFAULT (strftime('%s','now'))," - " account TEXT," - " direction TEXT," /* in | out | system | event */ - " conversation TEXT," /* direct:<target> | group:<id> */ - " sender TEXT," - " timestamp INTEGER,"/* Signal message timestamp (ms), if any */ - " kind TEXT," /* data | remote-delete | edit | receipt | typing | ... */ - " body TEXT," - " raw TEXT NOT NULL" /* full raw JSON of the event */ - ");" - "CREATE INDEX IF NOT EXISTS idx_messages_conv ON messages(conversation);" - "CREATE INDEX IF NOT EXISTS idx_messages_ts ON messages(timestamp);"; - -/* Open (creating if needed) the encrypted DB at `path`, keyed with `key`. - * Returns a sqlite3* (as void*) or NULL on failure (including a wrong key). */ -void *signal_log_open(const char *path, const char *key) { - sqlite3 *db = NULL; - char *err = NULL; - - if (sqlite3_open(path, &db) != SQLITE_OK) { - if (db) sqlite3_close(db); - return NULL; - } - /* The key must be applied before any other database access. */ - if (key && key[0] != '\0') { - if (sqlite3_key(db, key, (int)strlen(key)) != SQLITE_OK) { - sqlite3_close(db); - return NULL; - } - } - /* Touch the schema so a wrong key fails here, not mid-insert. */ - if (sqlite3_exec(db, "SELECT count(*) FROM sqlite_master;", - NULL, NULL, &err) != SQLITE_OK) { - if (err) sqlite3_free(err); - sqlite3_close(db); - return NULL; - } - if (sqlite3_exec(db, SCHEMA, NULL, NULL, &err) != SQLITE_OK) { - if (err) sqlite3_free(err); - sqlite3_close(db); - return NULL; - } - (void)sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL); - return (void *)db; -} - -int signal_log_close(void *handle) { - if (!handle) return 0; - return sqlite3_close((sqlite3 *)handle); -} - -static void bind_text_or_null(sqlite3_stmt *st, int idx, const char *s) { - if (s && s[0] != '\0') - sqlite3_bind_text(st, idx, s, -1, SQLITE_TRANSIENT); - else - sqlite3_bind_null(st, idx); -} - -/* Append one row. Text args may be empty (stored as NULL). Returns 0 on success. */ -int signal_log_put(void *handle, - const char *account, const char *direction, - const char *conversation, const char *sender, - long long timestamp, const char *kind, - const char *body, const char *raw) { - if (!handle) return -1; - sqlite3 *db = (sqlite3 *)handle; - static const char *SQL = - "INSERT INTO messages" - " (account,direction,conversation,sender,timestamp,kind,body,raw)" - " VALUES (?,?,?,?,?,?,?,?);"; - sqlite3_stmt *st = NULL; - if (sqlite3_prepare_v2(db, SQL, -1, &st, NULL) != SQLITE_OK) - return sqlite3_errcode(db); - bind_text_or_null(st, 1, account); - bind_text_or_null(st, 2, direction); - bind_text_or_null(st, 3, conversation); - bind_text_or_null(st, 4, sender); - sqlite3_bind_int64(st, 5, (sqlite3_int64)timestamp); - bind_text_or_null(st, 6, kind); - bind_text_or_null(st, 7, body); - sqlite3_bind_text(st, 8, raw ? raw : "", -1, SQLITE_TRANSIENT); - int rc = sqlite3_step(st); - sqlite3_finalize(st); - return (rc == SQLITE_DONE) ? 0 : rc; -} - -/* Total rows logged (verification helper). Returns -1 on error. */ -long long signal_log_count(void *handle) { - if (!handle) return -1; - sqlite3 *db = (sqlite3 *)handle; - sqlite3_stmt *st = NULL; - if (sqlite3_prepare_v2(db, "SELECT count(*) FROM messages;", -1, &st, NULL) - != SQLITE_OK) - return -1; - long long n = -1; - if (sqlite3_step(st) == SQLITE_ROW) - n = (long long)sqlite3_column_int64(st, 0); - sqlite3_finalize(st); - return n; -} - -/* Begin reading the most recent displayable rows, newest first. Data-like rows - * with a body and a conversation qualify; receipts, typing indicators and bare - * events are noise for history. Returns a statement handle - * (step/text/int/close below) or NULL. - * Columns: 0 direction, 1 conversation, 2 sender, 3 timestamp, 4 kind, 5 body. */ -void *signal_log_recent(void *handle, long long limit) { - if (!handle) return NULL; - sqlite3 *db = (sqlite3 *)handle; - static const char *SQL = - "SELECT direction, conversation, sender, timestamp, kind, body" - " FROM messages" - " WHERE direction IN ('in','out')" - " AND conversation IS NOT NULL AND conversation <> ''" - " AND body IS NOT NULL AND body <> ''" - " ORDER BY id DESC LIMIT ?;"; - sqlite3_stmt *st = NULL; - if (sqlite3_prepare_v2(db, SQL, -1, &st, NULL) != SQLITE_OK) - return NULL; - sqlite3_bind_int64(st, 1, (sqlite3_int64)(limit > 0 ? limit : 0)); - return (void *)st; -} - -/* 1 = a row is available, 0 = done (or error -- treated as done). */ -int signal_log_row_step(void *stmt) { - if (!stmt) return 0; - return sqlite3_step((sqlite3_stmt *)stmt) == SQLITE_ROW ? 1 : 0; -} - -/* Text column of the current row; "" for NULL. Valid until the next step. */ -const char *signal_log_row_text(void *stmt, int col) { - if (!stmt) return ""; - const unsigned char *s = sqlite3_column_text((sqlite3_stmt *)stmt, col); - return s ? (const char *)s : ""; -} - -long long signal_log_row_int(void *stmt, int col) { - if (!stmt) return 0; - return (long long)sqlite3_column_int64((sqlite3_stmt *)stmt, col); -} - -int signal_log_row_close(void *stmt) { - if (!stmt) return 0; - return sqlite3_finalize((sqlite3_stmt *)stmt); -} - -/* Read a passphrase from the controlling terminal with echo disabled. - * Returns a pointer to a static buffer (copied by the FFI into a Scheme string). */ -const char *signal_log_getpass(const char *prompt) { - char *p = getpass(prompt ? prompt : "Passphrase: "); - return p ? p : ""; -} - -#ifdef SIGNAL_LOG_TEST -#include <stdio.h> -int main(void) { - const char *path = "/tmp/jerboa-signal-logtest.db"; - unlink(path); - void *db = signal_log_open(path, "correct horse battery staple"); - if (!db) { printf("FAIL open\n"); return 1; } - signal_log_put(db, "+15550001111", "in", "direct:+15550002222", - "Alice", 1717000000000LL, "data", "hello world", - "{\"method\":\"receive\"}"); - signal_log_put(db, "+15550001111", "in", "direct:+15550002222", - "Alice", 1717000005000LL, "remote-delete", "", - "{\"remoteDelete\":{\"timestamp\":1717000000000}}"); - long long n = signal_log_count(db); - signal_log_close(db); - - /* Reopen with correct key: both rows (incl. the original) still present. */ - void *db2 = signal_log_open(path, "correct horse battery staple"); - long long n2 = db2 ? signal_log_count(db2) : -1; - - /* Readback: only the data row qualifies (remote-delete has no body). */ - int recent = 0, body_ok = 0; - void *st = db2 ? signal_log_recent(db2, 10) : NULL; - while (st && signal_log_row_step(st)) { - recent++; - body_ok = strcmp(signal_log_row_text(st, 5), "hello world") == 0 - && signal_log_row_int(st, 3) == 1717000000000LL; - } - if (st) signal_log_row_close(st); - if (db2) signal_log_close(db2); - - /* Wrong key must fail. */ - void *db3 = signal_log_open(path, "wrong key"); - int wrong_rejected = (db3 == NULL); - if (db3) signal_log_close(db3); - - printf("rows=%lld reopened=%lld recent=%d body_ok=%d wrong_key_rejected=%d\n", - n, n2, recent, body_ok, wrong_rejected); - return (n == 2 && n2 == 2 && recent == 1 && body_ok && wrong_rejected) ? 0 : 2; -} -#endif --- a/signal/logdb.ss +++ b/signal/logdb.ss @@ -2,8 +2,8 @@ ;;; signal/logdb -- encrypted message log. ;;; ;;; New logs use a Jerboa-native jsqlite database image stored inside an -;;; authenticated encrypted container. Existing SQLCipher databases are still -;;; opened through the legacy shim so old logs do not disappear. +;;; authenticated encrypted container. No native SQLite or SQLCipher shim is +;;; loaded. (library (signal logdb) (export logdb-available? @@ -31,97 +31,6 @@ (defstruct jlog (path key salt db)) ;; -------------------------------------------------------------------------- - ;; Legacy SQLCipher shim. This stays only for old messages-*.db files that - ;; were created by the previous backend. - - (def *shim-load-state* (box 'untried)) - - (def (try-load-shim path) - (and (file-exists? path) (load-shared-object path))) - - (def (ensure-shim-loaded!) - (cond - [(eq? (unbox *shim-load-state*) 'loaded) #t] - [(eq? (unbox *shim-load-state*) 'missing) #f] - [else - (let* ([bin-dir (path-directory (car (command-line)))] - [home (or (getenv "HOME") ".")] - [loaded? - (or (foreign-entry? "signal_log_open") - (guard (e [#t #f]) - (or (try-load-shim "signal_log_shim.dylib") - (try-load-shim "signal_log_shim.so") - (try-load-shim (path-join bin-dir "signal_log_shim.dylib")) - (try-load-shim (path-join bin-dir "signal_log_shim.so")) - (try-load-shim (path-join home ".local" "bin" - "signal_log_shim.dylib")) - (try-load-shim (path-join home ".local" "bin" - "signal_log_shim.so")))))]) - (set-box! *shim-load-state* (if loaded? 'loaded 'missing)) - loaded?)])) - - (def (legacy-entry? name) - (or (foreign-entry? name) - (and (ensure-shim-loaded!) (foreign-entry? name)))) - - (def (legacy-available?) - (legacy-entry? "signal_log_open")) - - (def (legacy-open path key) - (and (legacy-entry? "signal_log_open") - (let ([h ((foreign-procedure "signal_log_open" (string string) uptr) - path key)]) - (and (not (= h 0)) h)))) - - (def (legacy-close handle) - (when (and handle (legacy-entry? "signal_log_close")) - ((foreign-procedure "signal_log_close" (uptr) int) handle) - (void))) - - (def (legacy-put handle account direction conversation sender - timestamp kind body raw) - (and handle - (legacy-entry? "signal_log_put") - (= 0 ((foreign-procedure "signal_log_put" - (uptr string string string string integer-64 string string string) - int) - handle account direction conversation sender - timestamp kind body raw)))) - - (def (legacy-count handle) - (if (and handle (legacy-entry? "signal_log_count")) - ((foreign-procedure "signal_log_count" (uptr) integer-64) handle) - -1)) - - (def (legacy-recent handle limit) - (if (and handle - (legacy-entry? "signal_log_recent") - (legacy-entry? "signal_log_row_step") - (legacy-entry? "signal_log_row_text") - (legacy-entry? "signal_log_row_int") - (legacy-entry? "signal_log_row_close")) - (guard (e [#t '()]) - (let ([open (foreign-procedure "signal_log_recent" (uptr integer-64) uptr)] - [step (foreign-procedure "signal_log_row_step" (uptr) int)] - [text (foreign-procedure "signal_log_row_text" (uptr int) string)] - [num (foreign-procedure "signal_log_row_int" (uptr int) integer-64)] - [close (foreign-procedure "signal_log_row_close" (uptr) int)]) - (let ([st (open handle limit)]) - (if (= st 0) - '() - (dynamic-wind - (lambda () (void)) - (lambda () - (let loop ([acc '()]) - (if (= (step st) 1) - (loop (cons (list (text st 0) (text st 1) (text st 2) - (num st 3) (text st 4) (text st 5)) - acc)) - acc))) - (lambda () (close st))))))) - '())) - - ;; -------------------------------------------------------------------------- ;; jsqlite encrypted container. (def *jlog-magic* (string->utf8 "JSQLITELOGv1\n")) @@ -309,24 +218,10 @@ ;; Public API. (def (logdb-available?) - (or (log-crypto-available?) (legacy-available?))) - - (def (backend-env-is? name) - (let ([v (getenv "JERBOA_SIGNAL_LOG_BACKEND")]) - (and v (string-ci=? v name)))) - - (def (wrap-legacy h) - (and h (make-logdb-handle 'legacy h))) + (log-crypto-available?)) (def (logdb-open path key) - (cond - [(or (backend-env-is? "sqlcipher") (backend-env-is? "legacy")) - (wrap-legacy (legacy-open path key))] - [(backend-env-is? "jsqlite") - (open-jlog path key)] - [else - (or (open-jlog path key) - (wrap-legacy (legacy-open path key)))])) + (open-jlog path key)) (def (logdb-close handle) (when (logdb-handle? handle) @@ -336,8 +231,6 @@ (let ([log (logdb-handle-inner handle)]) (persist-jlog! log) (sqlite-close (jlog-db log))))] - [(legacy) - (legacy-close (logdb-handle-inner handle))] [else (void)]))) (def (logdb-put handle account direction conversation sender @@ -349,10 +242,6 @@ (jlog-put (logdb-handle-inner handle) account direction conversation sender timestamp kind body raw)] - [(legacy) - (legacy-put (logdb-handle-inner handle) - account direction conversation sender - timestamp kind body raw)] [else #f])))) (def (logdb-count handle) @@ -360,7 +249,6 @@ (guard (e [#t -1]) (case (logdb-handle-backend handle) [(jsqlite) (jlog-count (logdb-handle-inner handle))] - [(legacy) (legacy-count (logdb-handle-inner handle))] [else -1])) -1)) @@ -369,12 +257,11 @@ (guard (e [#t '()]) (case (logdb-handle-backend handle) [(jsqlite) (jlog-recent (logdb-handle-inner handle) limit)] - [(legacy) (legacy-recent (logdb-handle-inner handle) limit)] [else '()])) '())) ;; Read a passphrase before termbox takes over the terminal. This avoids - ;; depending on the old SQLCipher C shim just to get getpass(3). + ;; depending on native terminal helpers. (def (logdb-prompt-passphrase prompt) (display prompt) (flush-output-port (current-output-port))