diff --git a/core/app.js b/core/app.js index b4bd6f62..cacfc8e9 100644 --- a/core/app.js +++ b/core/app.js @@ -149,7 +149,7 @@ function socket(request, response, client) { parentApp: parentApp, id: blobId, }, - await core.getIdentityInfo( + await ssb.getIdentityInfo( credentials?.session?.name, packageOwner, packageName diff --git a/core/core.js b/core/core.js index 7a49fbd3..a055ae45 100644 --- a/core/core.js +++ b/core/core.js @@ -545,7 +545,7 @@ async function getProcessBlob(blobId, key, options) { { action: 'identities', }, - await getIdentityInfo( + await ssb.getIdentityInfo( process?.credentials?.session?.name, options?.packageOwner, options?.packageName @@ -785,6 +785,7 @@ async function getProcessBlob(blobId, key, options) { ); } }; + imports.ssb.getIdentityInfo = undefined; imports.fetch = function (url, options) { return http.fetch(url, options, gGlobalSettings.fetch_hosts); }; @@ -1553,43 +1554,10 @@ function storePermission(user, packageOwner, packageName, permission, allow) { } } -async function getIdentityInfo(user, packageOwner, packageName) { - let identities = await ssb.getIdentities(user); - let names = new Object(); - for (let identity of identities) { - names[identity] = identity; - } - await ssb.sqlAsync( - ` - SELECT author, name FROM ( - SELECT - messages.author, - RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, - messages.content ->> 'name' AS name - FROM messages - JOIN json_each(?) AS ids - ON messages.author = ids.value - WHERE json_extract(messages.content, '$.type') = 'about' AND content ->> 'about' = messages.author AND name IS NOT NULL) - WHERE author_rank = 1 - `, - [JSON.stringify(identities)], - function (row) { - names[row.author] = row.name; - } - ); - - return { - identities: identities, - identity: await ssb.getActiveIdentity(user, packageOwner, packageName), - names: names, - }; -} - export { gGlobalSettings as globalSettings, setGlobalSettings, enableStats, invoke, getSessionProcessBlob, - getIdentityInfo, }; diff --git a/src/ssb.db.c b/src/ssb.db.c index 10a8b228..c58fdb95 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -1735,3 +1735,22 @@ bool tf_ssb_db_set_property(tf_ssb_t* ssb, const char* id, const char* key, cons tf_ssb_release_db_writer(ssb, db); return result; } + +bool tf_ssb_db_identity_get_active(sqlite3* db, const char* user, const char* package_owner, const char* package_name, char* out_identity, size_t out_identity_size) +{ + sqlite3_stmt* statement = NULL; + bool found = false; + if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = ? AND key = 'id:' || ? || ':' || ?", -1, &statement, NULL) == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK && + sqlite3_bind_text(statement, 2, package_owner, -1, NULL) == SQLITE_OK && + sqlite3_bind_text(statement, 3, package_name, -1, NULL) == SQLITE_OK && + sqlite3_step(statement) == SQLITE_ROW) + { + snprintf(out_identity, out_identity_size, "%s", (const char*)sqlite3_column_text(statement, 0)); + found = true; + } + sqlite3_finalize(statement); + } + return found; +} diff --git a/src/ssb.db.h b/src/ssb.db.h index d1fa685c..9016a749 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -195,6 +195,18 @@ bool tf_ssb_db_identity_delete(tf_ssb_t* ssb, const char* user, const char* publ */ bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key); +/** +** Get the active identity for a user for a given package. +** @param db An sqlite3 database. +** @param user The username. +** @param package_owner The username of the package owner. +** @param package_name The name of the package. +** @param[out] out_identity Populated with the identity. +** @param out_identity_size The size of the out_identity buffer. +** @return true If the identity was retrieved. +*/ +bool tf_ssb_db_identity_get_active(sqlite3* db, const char* user, const char* package_owner, const char* package_name, char* out_identity, size_t out_identity_size); + /** ** Call a function for each identity owned by a user. ** @param ssb The SSB instance. diff --git a/src/ssb.js.c b/src/ssb.js.c index 6a0e219d..7d9f4a52 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -330,19 +330,7 @@ static void _tf_ssb_getActiveIdentity_work(uv_work_t* work) tf_trace_begin(trace, "_tf_ssb_getActiveIdentity_work"); sqlite3* db = tf_ssb_acquire_db_reader(request->ssb); - sqlite3_stmt* statement = NULL; - request->result = sqlite3_prepare(db, "SELECT value FROM properties WHERE id = ? AND key = 'id:' || ? || ':' || ?", -1, &statement, NULL); - if (request->result == SQLITE_OK) - { - if (sqlite3_bind_text(statement, 1, request->name, -1, NULL) == SQLITE_OK && - sqlite3_bind_text(statement, 2, request->package_owner, -1, NULL) == SQLITE_OK && - sqlite3_bind_text(statement, 3, request->package_name, -1, NULL) == SQLITE_OK && - sqlite3_step(statement) == SQLITE_ROW) - { - snprintf(request->identity, sizeof(request->identity), "%s", (const char*)sqlite3_column_text(statement, 0)); - } - sqlite3_finalize(statement); - } + tf_ssb_db_identity_get_active(db, request->name, request->package_owner, request->package_name, request->identity, sizeof(request->identity)); tf_ssb_release_db_reader(request->ssb, db); if (!*request->identity) @@ -408,6 +396,149 @@ static JSValue _tf_ssb_getActiveIdentity(JSContext* context, JSValueConst this_v return result; } +typedef struct _identity_info_work_t +{ + uv_work_t request; + tf_ssb_t* ssb; + JSContext* context; + const char* name; + const char* package_owner; + const char* package_name; + int count; + char** identities; + char** names; + int result; + char active_identity[k_id_base64_len]; + JSValue promise[2]; +} identity_info_work_t; + +static void _tf_ssb_getIdentityInfo_visit(const char* identity, void* data) +{ + identity_info_work_t* request = data; + request->identities = tf_resize_vec(request->identities, (request->count + 1) * sizeof(char*)); + request->names = tf_resize_vec(request->names, (request->count + 1) * sizeof(char*)); + request->identities[request->count] = tf_strdup(identity); + request->names[request->count] = NULL; + request->count++;; +} + +static void _tf_ssb_getIdentityInfo_work(uv_work_t* work) +{ + identity_info_work_t* request = work->data; + tf_ssb_db_identity_visit(request->ssb, request->name, _tf_ssb_getIdentityInfo_visit, request); + + sqlite3* db = tf_ssb_acquire_db_reader(request->ssb); + sqlite3_stmt* statement = NULL; + request->result = sqlite3_prepare(db, + "SELECT author, name FROM ( " + " SELECT " + " messages.author, " + " RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, " + " messages.content ->> 'name' AS name " + " FROM messages " + " JOIN identities ON messages.author = ids.value " + " WHERE WHERE identities.user = ? AND json_extract(messages.content, '$.type') = 'about' AND content ->> 'about' = messages.author AND name IS NOT NULL) " + "WHERE author_rank = 1 ", -1, &statement, NULL); + if (request->result == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 1, request->name, -1, NULL) == SQLITE_OK) + { + int r = SQLITE_OK; + while ((r = sqlite3_step(statement)) == SQLITE_OK) + { + for (int i = 0; i < request->count; i++) + { + const char* identity = (const char*)sqlite3_column_text(statement, 0); + const char* name = (const char*)sqlite3_column_text(statement, 1); + if (strcmp(request->identities[i], identity) == 0 && !request->names[i]) + { + request->names[i] = tf_strdup(name); + } + break; + } + } + } + sqlite3_finalize(statement); + } + + tf_ssb_db_identity_get_active(db, request->name, request->package_owner, request->package_name, request->active_identity, sizeof(request->active_identity)); + if (!*request->active_identity && request->count) + { + snprintf(request->active_identity, sizeof(request->active_identity), "%s", request->identities[0]); + } + tf_ssb_release_db_reader(request->ssb, db); +} + +static void _tf_ssb_getIdentityInfo_after_work(uv_work_t* work, int status) +{ + identity_info_work_t* request = work->data; + JSContext* context = request->context; + JSValue result = JS_NewObject(context); + + JSValue identities = JS_NewArray(context); + for (int i = 0; i < request->count; i++) + { + JS_SetPropertyUint32(context, identities, i, JS_NewString(context, request->identities[i])); + } + JS_SetPropertyStr(context, result, "identities", identities); + + JSValue names = JS_NewObject(context); + for (int i = 0; i < request->count; i++) + { + JS_SetPropertyStr(context, names, request->identities[i], JS_NewString(context, request->names[i] ? request->names[i] : request->identities[i])); + } + JS_SetPropertyStr(context, result, "names", names); + + JS_SetPropertyStr(context, result, "identity", JS_NewString(context, request->active_identity)); + + JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 1, &result); + tf_util_report_error(context, error); + JS_FreeValue(context, error); + JS_FreeValue(context, result); + JS_FreeValue(context, request->promise[0]); + JS_FreeValue(context, request->promise[1]); + + for (int i = 0; i < request->count; i++) + { + tf_free(request->identities[i]); + tf_free(request->names[i]); + } + tf_free(request->identities); + tf_free(request->names); + tf_free((void*)request->name); + tf_free((void*)request->package_owner); + tf_free((void*)request->package_name); + tf_free(request); +} + +static JSValue _tf_ssb_getIdentityInfo(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + const char* name = JS_ToCString(context, argv[0]); + const char* package_owner = JS_ToCString(context, argv[1]); + const char* package_name = JS_ToCString(context, argv[2]); + identity_info_work_t* work = tf_malloc(sizeof(identity_info_work_t)); + *work = (identity_info_work_t) + { + .request = { .data = work }, + .ssb = JS_GetOpaque(this_val, _tf_ssb_classId), + .context = context, + .name = tf_strdup(name), + .package_owner = tf_strdup(package_owner), + .package_name = tf_strdup(package_name), + }; + JSValue result = JS_NewPromiseCapability(context, work->promise); + JS_FreeCString(context, name); + JS_FreeCString(context, package_owner); + JS_FreeCString(context, package_name); + + int r = uv_queue_work(tf_ssb_get_loop(work->ssb), &work->request, _tf_ssb_getIdentityInfo_work, _tf_ssb_getIdentityInfo_after_work); + if (r) + { + _tf_ssb_getIdentityInfo_after_work(&work->request, r); + } + return result; +} + typedef struct _append_message_t { JSContext* context; @@ -1812,6 +1943,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) JS_SetPropertyStr(context, object, "getServerIdentity", JS_NewCFunction(context, _tf_ssb_getServerIdentity, "getServerIdentity", 0)); JS_SetPropertyStr(context, object, "getAllIdentities", JS_NewCFunction(context, _tf_ssb_getAllIdentities, "getAllIdentities", 0)); JS_SetPropertyStr(context, object, "getActiveIdentity", JS_NewCFunction(context, _tf_ssb_getActiveIdentity, "getActiveIdentity", 3)); + JS_SetPropertyStr(context, object, "getIdentityInfo", JS_NewCFunction(context, _tf_ssb_getIdentityInfo, "getIdentityInfo", 3)); JS_SetPropertyStr(context, object, "getMessage", JS_NewCFunction(context, _tf_ssb_getMessage, "getMessage", 2)); JS_SetPropertyStr(context, object, "blobGet", JS_NewCFunction(context, _tf_ssb_blobGet, "blobGet", 1)); JS_SetPropertyStr(context, object, "messageContentGet", JS_NewCFunction(context, _tf_ssb_messageContentGet, "messageContentGet", 1));