v0.0.16 - Add .fips identity-cache fallback for URL signer resolution and fix Write menu Exit hotkey underline

This commit is contained in:
Laan Tungir
2026-05-08 11:36:34 -04:00
parent c08fae7ba8
commit 2bc2305f22
3 changed files with 167 additions and 23 deletions

View File

@@ -11,8 +11,8 @@
*/
#define NT_VERSION_MAJOR 0
#define NT_VERSION_MINOR 0
#define NT_VERSION_PATCH 15
#define NT_VERSION "v0.0.15"
#define NT_VERSION_PATCH 16
#define NT_VERSION "v0.0.16"
#include <ncurses.h>
#include <stdio.h>
@@ -183,31 +183,41 @@ int main(int argc, char *argv[]) {
nt_set_app_info("NOSTR TERMINAL", NT_VERSION);
nt_set_window_title("Nostr Terminal");
/* Splash screen */
/* Splash screen (no header/footer, centered text) */
{
TuiFrame frame = nt_frame("> Splash");
TuiStatus status = {"Press any key to continue"};
WINDOW *body = (WINDOW *)tuin_body_window();
int h = 0;
int w = 0;
const char *title = "NOSTR TERMINAL";
int len = (int)strlen(title);
int col;
int row;
const char *version = NT_VERSION;
int title_len = (int)strlen(title);
int version_len = (int)strlen(version);
int title_col;
int version_col;
int title_row;
tuin_render_header(&frame);
getmaxyx(body, h, w);
col = (w - len) / 2;
if (col < 0) {
col = 0;
getmaxyx(stdscr, h, w);
title_col = (w - title_len) / 2;
if (title_col < 0) {
title_col = 0;
}
row = h / 2;
werase(body);
mvwprintw(body, row, col, "%s", title);
mvwprintw(body, row + 1, col, "%s", NT_VERSION);
wnoutrefresh(body);
tuin_render_footer(&status);
doupdate();
version_col = (w - version_len) / 2;
if (version_col < 0) {
version_col = 0;
}
title_row = (h / 2) - 1;
if (title_row < 0) {
title_row = 0;
}
werase(stdscr);
mvprintw(title_row, title_col, "%s", title);
if (title_row + 1 < h) {
mvprintw(title_row + 1, version_col, "%s", version);
}
refresh();
tuin_get_key();
}

View File

@@ -56,7 +56,7 @@ void menu_write(void) {
{NT_HK("T", "weet it"), 't'},
{NT_HK("B", "log post (kind 30023)"), 'b'},
{NT_HK("D", "iary entry (kind 30024)"), 'd'},
{NT_HK("X", "Exit without saving"), 'x'},
{"E" NT_HK("x", "it without saving"), 'x'},
};
TuiFrame frame = nt_frame("> Main Menu > Write");
TuiStatus status = nt_status();

View File

@@ -337,6 +337,125 @@ static int nsigner_parse_url(const char *url, char *host_out, size_t host_out_sz
return NT_NSIGNER_OK;
}
static int nsigner_host_has_fips_suffix(const char *host) {
size_t n;
if (!host) {
return 0;
}
n = strlen(host);
return (n > 5 && strcmp(host + (n - 5), ".fips") == 0);
}
static int nsigner_extract_npub_from_fips_host(const char *host,
char *npub_out,
size_t npub_out_sz) {
const char *dot;
size_t n;
if (!host || !npub_out || npub_out_sz == 0 || !nsigner_host_has_fips_suffix(host)) {
return -1;
}
dot = strstr(host, ".fips");
if (!dot || dot == host) {
return -1;
}
n = (size_t)(dot - host);
if (n == 0 || n >= npub_out_sz) {
return -1;
}
memcpy(npub_out, host, n);
npub_out[n] = '\0';
return 0;
}
static int nsigner_lookup_identity_cache_ipv6(const char *npub,
char *ipv6_out,
size_t ipv6_out_sz) {
FILE *fp;
char chunk[512];
char *json_buf = NULL;
size_t used = 0;
size_t cap = 0;
cJSON *root = NULL;
cJSON *entries;
int rc = -1;
if (!npub || !ipv6_out || ipv6_out_sz == 0) {
return -1;
}
fp = popen("fipsctl show identity-cache 2>/dev/null", "r");
if (!fp) {
return -1;
}
while (fgets(chunk, sizeof(chunk), fp)) {
size_t need = strlen(chunk);
if (used + need + 1 > cap) {
size_t new_cap = (cap == 0) ? 2048 : (cap * 2);
char *tmp;
while (new_cap < used + need + 1) {
new_cap *= 2;
}
tmp = (char *)realloc(json_buf, new_cap);
if (!tmp) {
free(json_buf);
pclose(fp);
return -1;
}
json_buf = tmp;
cap = new_cap;
}
memcpy(json_buf + used, chunk, need);
used += need;
json_buf[used] = '\0';
}
pclose(fp);
if (!json_buf || used == 0) {
free(json_buf);
return -1;
}
root = cJSON_Parse(json_buf);
free(json_buf);
if (!root) {
return -1;
}
entries = cJSON_GetObjectItemCaseSensitive(root, "entries");
if (cJSON_IsArray(entries)) {
cJSON *entry;
cJSON_ArrayForEach(entry, entries) {
cJSON *npub_item = cJSON_GetObjectItemCaseSensitive(entry, "npub");
cJSON *ipv6_item;
if (!cJSON_IsString(npub_item) || !npub_item->valuestring) {
continue;
}
if (strcmp(npub_item->valuestring, npub) != 0) {
continue;
}
ipv6_item = cJSON_GetObjectItemCaseSensitive(entry, "ipv6_addr");
if (cJSON_IsString(ipv6_item) && ipv6_item->valuestring &&
ipv6_item->valuestring[0] != '\0' &&
strlen(ipv6_item->valuestring) < ipv6_out_sz) {
memcpy(ipv6_out, ipv6_item->valuestring, strlen(ipv6_item->valuestring) + 1);
rc = 0;
break;
}
}
}
cJSON_Delete(root);
return rc;
}
static int nsigner_connect_url(const char *url) {
struct addrinfo hints;
struct addrinfo *res = NULL;
@@ -356,7 +475,22 @@ static int nsigner_connect_url(const char *url) {
snprintf(port_str, sizeof(port_str), "%d", port);
if (getaddrinfo(host, port_str, &hints, &res) != 0) {
return -1;
char npub[128];
char ipv6_addr[128];
if (nsigner_extract_npub_from_fips_host(host, npub, sizeof(npub)) == 0 &&
nsigner_lookup_identity_cache_ipv6(npub, ipv6_addr, sizeof(ipv6_addr)) == 0) {
struct addrinfo numeric_hints;
memset(&numeric_hints, 0, sizeof(numeric_hints));
numeric_hints.ai_family = AF_UNSPEC;
numeric_hints.ai_socktype = SOCK_STREAM;
numeric_hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(ipv6_addr, port_str, &numeric_hints, &res) != 0) {
return -1;
}
} else {
return -1;
}
}
for (ai = res; ai; ai = ai->ai_next) {