258 lines
8.9 KiB
C
258 lines
8.9 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include "tools_internal.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "cjson/cJSON.h"
|
|
#include "../../nostr_core_lib/nostr_core/nostr_core.h"
|
|
|
|
static char* json_error_local(const char* msg) {
|
|
cJSON* root = cJSON_CreateObject();
|
|
if (!root) return NULL;
|
|
cJSON_AddBoolToObject(root, "success", 0);
|
|
cJSON_AddStringToObject(root, "error", msg ? msg : "unknown error");
|
|
char* out = cJSON_PrintUnformatted(root);
|
|
cJSON_Delete(root);
|
|
return out;
|
|
}
|
|
|
|
static cJSON* parse_args_local(const char* args_json) {
|
|
const char* raw = args_json ? args_json : "{}";
|
|
cJSON* args = cJSON_Parse(raw);
|
|
if (!args || !cJSON_IsObject(args)) {
|
|
cJSON_Delete(args);
|
|
return NULL;
|
|
}
|
|
return args;
|
|
}
|
|
|
|
static int is_hex_string_len_local(const char* s, size_t expected_len) {
|
|
if (!s) return 0;
|
|
if (strlen(s) != expected_len) return 0;
|
|
for (size_t i = 0; i < expected_len; i++) {
|
|
if (!((s[i] >= '0' && s[i] <= '9') ||
|
|
(s[i] >= 'a' && s[i] <= 'f') ||
|
|
(s[i] >= 'A' && s[i] <= 'F'))) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
char* execute_nostr_encode(const char* args_json) {
|
|
cJSON* args = parse_args_local(args_json);
|
|
if (!args) return json_error_local("invalid arguments JSON");
|
|
|
|
cJSON* type = cJSON_GetObjectItemCaseSensitive(args, "type");
|
|
cJSON* hex = cJSON_GetObjectItemCaseSensitive(args, "hex");
|
|
cJSON* relays = cJSON_GetObjectItemCaseSensitive(args, "relays");
|
|
cJSON* kind = cJSON_GetObjectItemCaseSensitive(args, "kind");
|
|
cJSON* identifier = cJSON_GetObjectItemCaseSensitive(args, "identifier");
|
|
|
|
if (!type || !cJSON_IsString(type) || !type->valuestring ||
|
|
!hex || !cJSON_IsString(hex) || !hex->valuestring || !is_hex_string_len_local(hex->valuestring, 64U)) {
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode requires type string and hex 64-char string");
|
|
}
|
|
|
|
char type_name[32];
|
|
snprintf(type_name, sizeof(type_name), "%s", type->valuestring);
|
|
|
|
unsigned char key_bytes[32];
|
|
if (nostr_hex_to_bytes(hex->valuestring, key_bytes, sizeof(key_bytes)) != 0) {
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode invalid hex");
|
|
}
|
|
|
|
const char** relay_ptrs = NULL;
|
|
int relay_count = 0;
|
|
if (relays) {
|
|
if (!cJSON_IsArray(relays)) {
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode relays must be an array of relay URLs");
|
|
}
|
|
relay_count = cJSON_GetArraySize(relays);
|
|
if (relay_count > 0) {
|
|
relay_ptrs = (const char**)malloc((size_t)relay_count * sizeof(char*));
|
|
if (!relay_ptrs) {
|
|
cJSON_Delete(args);
|
|
return json_error_local("memory allocation failed");
|
|
}
|
|
for (int i = 0; i < relay_count; i++) {
|
|
cJSON* relay = cJSON_GetArrayItem(relays, i);
|
|
if (!relay || !cJSON_IsString(relay) || !relay->valuestring || relay->valuestring[0] == '\0') {
|
|
free(relay_ptrs);
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode relays must contain non-empty strings");
|
|
}
|
|
relay_ptrs[i] = relay->valuestring;
|
|
}
|
|
}
|
|
}
|
|
|
|
char bech32[256] = {0};
|
|
char uri[1200] = {0};
|
|
int rc = NOSTR_ERROR_INVALID_INPUT;
|
|
|
|
if (strcmp(type_name, "npub") == 0) {
|
|
rc = nostr_key_to_bech32(key_bytes, "npub", bech32);
|
|
if (rc == NOSTR_SUCCESS) {
|
|
snprintf(uri, sizeof(uri), "nostr:%s", bech32);
|
|
}
|
|
} else if (strcmp(type_name, "nsec") == 0) {
|
|
rc = nostr_key_to_bech32(key_bytes, "nsec", bech32);
|
|
if (rc == NOSTR_SUCCESS) {
|
|
snprintf(uri, sizeof(uri), "nostr:%s", bech32);
|
|
}
|
|
} else if (strcmp(type_name, "note") == 0) {
|
|
rc = nostr_key_to_bech32(key_bytes, "note", bech32);
|
|
if (rc == NOSTR_SUCCESS) {
|
|
snprintf(uri, sizeof(uri), "nostr:%s", bech32);
|
|
}
|
|
} else if (strcmp(type_name, "nprofile") == 0) {
|
|
rc = nostr_build_uri_nprofile(key_bytes, relay_ptrs, relay_count, uri, sizeof(uri));
|
|
} else if (strcmp(type_name, "nevent") == 0) {
|
|
int kind_value = -1;
|
|
if (kind) {
|
|
if (!cJSON_IsNumber(kind)) {
|
|
free(relay_ptrs);
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode nevent kind must be an integer");
|
|
}
|
|
kind_value = (int)kind->valuedouble;
|
|
}
|
|
rc = nostr_build_uri_nevent(key_bytes, relay_ptrs, relay_count, NULL, kind_value, 0, uri, sizeof(uri));
|
|
} else if (strcmp(type_name, "naddr") == 0) {
|
|
if (!kind || !cJSON_IsNumber(kind)) {
|
|
free(relay_ptrs);
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode naddr requires kind integer");
|
|
}
|
|
if (!identifier || !cJSON_IsString(identifier) || !identifier->valuestring || identifier->valuestring[0] == '\0') {
|
|
free(relay_ptrs);
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode naddr requires non-empty identifier string");
|
|
}
|
|
rc = nostr_build_uri_naddr(identifier->valuestring, key_bytes, (int)kind->valuedouble,
|
|
relay_ptrs, relay_count, uri, sizeof(uri));
|
|
} else {
|
|
free(relay_ptrs);
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_encode currently supports npub, nsec, note, nprofile, nevent, naddr");
|
|
}
|
|
|
|
free(relay_ptrs);
|
|
cJSON_Delete(args);
|
|
if (rc != NOSTR_SUCCESS) {
|
|
return json_error_local("nostr_encode failed");
|
|
}
|
|
|
|
cJSON* out = cJSON_CreateObject();
|
|
if (!out) return NULL;
|
|
cJSON_AddBoolToObject(out, "success", 1);
|
|
cJSON_AddStringToObject(out, "type", type_name);
|
|
cJSON_AddStringToObject(out, "uri", uri);
|
|
cJSON_AddBoolToObject(out, "limited_support", 1);
|
|
char* json = cJSON_PrintUnformatted(out);
|
|
cJSON_Delete(out);
|
|
return json;
|
|
}
|
|
|
|
char* execute_nostr_decode(const char* args_json) {
|
|
cJSON* args = parse_args_local(args_json);
|
|
if (!args) return json_error_local("invalid arguments JSON");
|
|
|
|
cJSON* uri_in = cJSON_GetObjectItemCaseSensitive(args, "uri");
|
|
if (!uri_in || !cJSON_IsString(uri_in) || !uri_in->valuestring || uri_in->valuestring[0] == '\0') {
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_decode requires uri string");
|
|
}
|
|
|
|
const char* in = uri_in->valuestring;
|
|
if (strncmp(in, "nostr:", 6) == 0) {
|
|
in += 6;
|
|
}
|
|
|
|
unsigned char key[32];
|
|
char key_hex[65] = {0};
|
|
const char* type = NULL;
|
|
int rc = NOSTR_ERROR_INVALID_INPUT;
|
|
|
|
if (strncmp(in, "npub1", 5) == 0) {
|
|
type = "npub";
|
|
rc = nostr_decode_npub(in, key);
|
|
} else if (strncmp(in, "nsec1", 5) == 0) {
|
|
type = "nsec";
|
|
rc = nostr_decode_nsec(in, key);
|
|
} else {
|
|
cJSON_Delete(args);
|
|
return json_error_local("nostr_decode currently supports npub and nsec");
|
|
}
|
|
|
|
cJSON_Delete(args);
|
|
if (rc != NOSTR_SUCCESS) {
|
|
return json_error_local("nostr_decode failed");
|
|
}
|
|
|
|
nostr_bytes_to_hex(key, 32, key_hex);
|
|
|
|
cJSON* out = cJSON_CreateObject();
|
|
if (!out) return NULL;
|
|
cJSON_AddBoolToObject(out, "success", 1);
|
|
cJSON_AddStringToObject(out, "type", type);
|
|
if (strcmp(type, "npub") == 0) {
|
|
cJSON_AddStringToObject(out, "pubkey", key_hex);
|
|
} else {
|
|
cJSON_AddStringToObject(out, "private_key", key_hex);
|
|
}
|
|
cJSON_AddBoolToObject(out, "limited_support", 1);
|
|
|
|
char* json = cJSON_PrintUnformatted(out);
|
|
cJSON_Delete(out);
|
|
return json;
|
|
}
|
|
|
|
char* execute_nostr_pubkey(tools_context_t* ctx, const char* args_json) {
|
|
if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");
|
|
|
|
cJSON* args = parse_args_local(args_json);
|
|
if (!args) return json_error_local("invalid arguments JSON");
|
|
cJSON_Delete(args);
|
|
|
|
cJSON* out = cJSON_CreateObject();
|
|
if (!out) return NULL;
|
|
|
|
cJSON_AddBoolToObject(out, "success", 1);
|
|
cJSON_AddStringToObject(out, "pubkey", ctx->cfg->keys.public_key_hex);
|
|
|
|
char* json = cJSON_PrintUnformatted(out);
|
|
cJSON_Delete(out);
|
|
return json;
|
|
}
|
|
|
|
char* execute_nostr_npub(tools_context_t* ctx, const char* args_json) {
|
|
if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");
|
|
|
|
cJSON* args = parse_args_local(args_json);
|
|
if (!args) return json_error_local("invalid arguments JSON");
|
|
cJSON_Delete(args);
|
|
|
|
char npub[256] = {0};
|
|
if (nostr_key_to_bech32(ctx->cfg->keys.public_key, "npub", npub) != NOSTR_SUCCESS) {
|
|
return json_error_local("failed to encode npub");
|
|
}
|
|
|
|
cJSON* out = cJSON_CreateObject();
|
|
if (!out) return NULL;
|
|
|
|
cJSON_AddBoolToObject(out, "success", 1);
|
|
cJSON_AddStringToObject(out, "npub", npub);
|
|
|
|
char* json = cJSON_PrintUnformatted(out);
|
|
cJSON_Delete(out);
|
|
return json;
|
|
}
|