diff --git a/core/app.js b/core/app.js index 12ce9f32..2b830d11 100644 --- a/core/app.js +++ b/core/app.js @@ -52,7 +52,7 @@ function socket(request, response, client) { var packageName; var blobId; var match; - if (match = /^\/(&[^\.]*\.\w+)(\/?.*)/.exec(message.path)) { + if (match = /^\/([&%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(message.path)) { blobId = match[1]; } else if (match = /^\/\~([^\/]+)\/([^\/]+)\/$/.exec(message.path)) { var user = match[1]; diff --git a/core/core.js b/core/core.js index 90829d47..9db74919 100644 --- a/core/core.js +++ b/core/core.js @@ -267,16 +267,16 @@ async function getProcessBlob(blobId, key, options) { } process.task.setImports(imports); process.task.activate(); - let source = await ssb.blobGet(blobId); + let source = await getBlobOrContent(blobId); var appSource = utf8Decode(source); try { var app = JSON.parse(appSource); if (app.type == "tildefriends-app") { var id = app.files["app.js"]; - var blob = await ssb.blobGet(id); + var blob = await getBlobOrContent(id); appSource = utf8Decode(blob); await Promise.all(Object.keys(app.files).map(async function(f) { - await process.task.loadFile([f, await ssb.blobGet(app.files[f])]); + await process.task.loadFile([f, await getBlobOrContent(app.files[f])]); })); } } catch (e) { @@ -387,6 +387,16 @@ function sendData(response, data) { } } +async function getBlobOrContent(id) { + if (!id) { + return; + } else if (id.startsWith('&')) { + return ssb.blobGet(id); + } else if (id.startsWith('%')) { + return ssb.messageContentGet(id); + } +} + async function blobHandler(request, response, blobId, uri) { var found = false; if (!found) { @@ -402,7 +412,7 @@ async function blobHandler(request, response, blobId, uri) { } if (!uri) { - response.writeHead(301, {"Location": blobId + "/"}); + response.writeHead(301, {"Location": "/" + blobId + "/"}); response.end(data); return; } @@ -414,14 +424,14 @@ async function blobHandler(request, response, blobId, uri) { if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) { var id = await new Database(match[1]).get('path:' + match[2]); if (id) { - data = await ssb.blobGet(id); + data = await getBlobOrContent(id); if (match[3]) { var app = JSON.parse(data); data = app.files[match[3]]; } } } else { - data = await ssb.blobGet(blobId); + data = await getBlobOrContent(blobId); } sendData(response, data); } else if (uri == "/save") { @@ -449,12 +459,16 @@ async function blobHandler(request, response, blobId, uri) { var db = new Database(match[1]); var id = await db.get('path:' + match[2]); if (id) { - data = utf8Decode(await ssb.blobGet(id)); + data = utf8Decode(await getBlobOrContent(id)); var app = JSON.parse(data); - print(JSON.stringify(app)); data = app.files[uri.substring(1)]; - data = await ssb.blobGet(data); + data = await getBlobOrContent(data); } + } else { + data = utf8Decode(await getBlobOrContent(blobId)); + var app = JSON.parse(data); + data = app.files[uri.substring(1)]; + data = await getBlobOrContent(data); } sendData(response, data); } @@ -479,7 +493,7 @@ httpd.all("", function(request, response) { return response.end(); } else if (match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri)) { return blobHandler(request, response, match[1], match[2]); - } else if (match = /^\/(&[^\.]*\.\w+)(\/?.*)/.exec(request.uri)) { + } else if (match = /^\/([&\%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(request.uri)) { return blobHandler(request, response, match[1], match[2]); } else if (match = /^\/static(\/.*)/.exec(request.uri)) { return staticFileHandler(request, response, null, match[1]); diff --git a/src/ssb.c b/src/ssb.c index e924d3ea..208a853a 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -1902,6 +1902,31 @@ bool tf_ssb_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* ou return result; } +bool tf_ssb_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size) +{ + bool result = false; + sqlite3_stmt* statement; + const char* query = "SELECT content FROM messages WHERE id = ?"; + if (sqlite3_prepare(ssb->db, query, -1, &statement, NULL) == SQLITE_OK) { + if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK && + sqlite3_step(statement) == SQLITE_ROW) { + const uint8_t* blob = sqlite3_column_blob(statement, 0); + int size = sqlite3_column_bytes(statement, 0); + if (out_blob) { + *out_blob = malloc(size + 1); + memcpy(*out_blob, blob, size); + (*out_blob)[size] = '\0'; + } + if (out_size) { + *out_size = size; + } + result = true; + } + sqlite3_finalize(statement); + } + return result; +} + static bool _tf_ssb_parse_broadcast(const char* in_broadcast, tf_ssb_broadcast_t* out_broadcast) { char public_key_str[45] = { 0 }; diff --git a/src/ssb.h b/src/ssb.h index 43a3f6fa..8ef3d29a 100644 --- a/src/ssb.h +++ b/src/ssb.h @@ -70,6 +70,8 @@ bool tf_ssb_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author bool tf_ssb_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size); bool tf_ssb_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* out_id, size_t out_id_size); +bool tf_ssb_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size); + typedef void (tf_ssb_connections_changed_callback_t)(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data); void tf_ssb_add_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t callback, void* user_data); const char** tf_ssb_get_connection_ids(tf_ssb_t* ssb); diff --git a/src/ssb.qjs.c b/src/ssb.qjs.c index c059f11c..7ac72e0c 100644 --- a/src/ssb.qjs.c +++ b/src/ssb.qjs.c @@ -82,6 +82,22 @@ static JSValue _tf_ssb_blobStore(JSContext* context, JSValueConst this_val, int return result; } +static JSValue _tf_ssb_messageContentGet(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + JSValue result = JS_NULL; + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + if (ssb) { + const char* id = JS_ToCString(context, argv[0]); + uint8_t* blob = NULL; + size_t size = 0; + if (tf_ssb_message_content_get(ssb, id, &blob, &size)) { + result = JS_NewArrayBufferCopy(context, blob, size); + free(blob); + } + } + return result; +} + static JSValue _tf_ssb_connections(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { JSValue result = JS_NULL; @@ -285,6 +301,7 @@ void tf_ssb_init(JSContext* context, tf_ssb_t* ssb) 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, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 2)); + JS_SetPropertyStr(context, object, "messageContentGet", JS_NewCFunction(context, _tf_ssb_messageContentGet, "messageContentGet", 1)); JS_SetPropertyStr(context, object, "connections", JS_NewCFunction(context, _tf_ssb_connections, "connections", 0)); JS_SetPropertyStr(context, object, "createHistoryStream", JS_NewCFunction(context, _tf_ssb_createHistoryStream, "createHistoryStream", 1)); JS_SetPropertyStr(context, object, "sqlStream", JS_NewCFunction(context, _tf_ssb_sqlStream, "sqlStream", 3));