2021-08-22 15:34:28 -04:00
|
|
|
#include "ssb.import.h"
|
|
|
|
|
2022-06-04 13:04:51 -04:00
|
|
|
#include "mem.h"
|
2021-08-22 15:41:27 -04:00
|
|
|
#include "ssb.db.h"
|
2021-08-22 15:34:28 -04:00
|
|
|
#include "ssb.h"
|
2022-07-09 11:13:35 -04:00
|
|
|
#include "util.js.h"
|
2021-08-22 15:34:28 -04:00
|
|
|
|
2022-01-25 21:49:45 -05:00
|
|
|
#include <quickjs.h>
|
2021-08-22 15:34:28 -04:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <sqlite3.h>
|
|
|
|
#include <uv.h>
|
|
|
|
|
|
|
|
typedef struct _tf_import_file_t {
|
|
|
|
uv_fs_t req;
|
|
|
|
uv_file file;
|
|
|
|
tf_ssb_t* ssb;
|
|
|
|
const char* user;
|
|
|
|
const char* parent;
|
|
|
|
const char* name;
|
|
|
|
char data[k_ssb_blob_bytes_max];
|
|
|
|
int* work_left;
|
|
|
|
} tf_import_file_t;
|
|
|
|
|
|
|
|
static void _tf_ssb_import_file_close(uv_fs_t* req)
|
|
|
|
{
|
|
|
|
tf_import_file_t* file = req->data;
|
|
|
|
(*file->work_left)--;
|
|
|
|
uv_fs_req_cleanup(req);
|
2022-06-04 13:04:51 -04:00
|
|
|
tf_free(req->data);
|
2021-08-22 15:34:28 -04:00
|
|
|
}
|
|
|
|
|
2022-01-25 21:49:45 -05:00
|
|
|
static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char* app)
|
|
|
|
{
|
|
|
|
sqlite3_stmt* statement;
|
|
|
|
JSContext* context = tf_ssb_get_context(ssb);
|
|
|
|
JSValue apps = JS_UNDEFINED;
|
|
|
|
if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'apps'", -1, &statement, NULL) == SQLITE_OK)
|
|
|
|
{
|
|
|
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
|
|
|
sqlite3_step(statement) == SQLITE_ROW)
|
|
|
|
{
|
|
|
|
const char* json = (const char*)sqlite3_column_text(statement, 0);
|
|
|
|
apps = JS_ParseJSON(context, json, strlen(json), NULL);
|
|
|
|
}
|
|
|
|
sqlite3_finalize(statement);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!JS_IsArray(context, apps))
|
|
|
|
{
|
|
|
|
JS_FreeValue(context, apps);
|
|
|
|
apps = JS_NewArray(context);
|
|
|
|
}
|
2022-11-26 21:19:40 -05:00
|
|
|
int32_t length = tf_util_get_length(context, apps);
|
2022-01-25 21:49:45 -05:00
|
|
|
JS_SetPropertyUint32(context, apps, length, JS_NewString(context, app));
|
|
|
|
|
|
|
|
JSValue json = JS_JSONStringify(context, apps, JS_NULL, JS_NULL);
|
|
|
|
const char* text = JS_ToCString(context, json);
|
|
|
|
if (sqlite3_prepare(tf_ssb_get_db(ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'apps', $2)", -1, &statement, NULL) == SQLITE_OK)
|
|
|
|
{
|
|
|
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
|
|
|
sqlite3_bind_text(statement, 2, text, -1, NULL) == SQLITE_OK &&
|
|
|
|
sqlite3_step(statement) == SQLITE_OK)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
sqlite3_finalize(statement);
|
|
|
|
}
|
|
|
|
JS_FreeCString(context, text);
|
|
|
|
JS_FreeValue(context, json);
|
|
|
|
|
|
|
|
JS_FreeValue(context, apps);
|
|
|
|
}
|
|
|
|
|
2021-08-22 15:34:28 -04:00
|
|
|
static void _tf_ssb_import_file_read(uv_fs_t* req)
|
|
|
|
{
|
|
|
|
tf_import_file_t* file = req->data;
|
|
|
|
char id[k_id_base64_len];
|
2021-10-10 17:51:38 -04:00
|
|
|
if (req->result >= 0)
|
|
|
|
{
|
2022-10-12 08:27:32 -04:00
|
|
|
bool is_new = false;
|
|
|
|
if (tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id), &is_new))
|
2021-10-10 17:51:38 -04:00
|
|
|
{
|
2022-10-12 08:27:32 -04:00
|
|
|
if (is_new)
|
|
|
|
{
|
|
|
|
printf("Stored %s/%s as %s.\n", file->parent, file->name, id);
|
|
|
|
}
|
2021-10-10 17:51:38 -04:00
|
|
|
if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
sqlite3_stmt* statement;
|
2021-10-10 17:51:38 -04:00
|
|
|
if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0';
|
|
|
|
if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK &&
|
|
|
|
sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK &&
|
|
|
|
sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
|
2021-10-10 17:51:38 -04:00
|
|
|
sqlite3_step(statement) == SQLITE_DONE)
|
|
|
|
{
|
2022-10-12 08:27:32 -04:00
|
|
|
if (sqlite3_changes(tf_ssb_get_db(file->ssb)))
|
|
|
|
{
|
|
|
|
printf("Registered %s path:%s as %s.\n", file->user, file->name, id);
|
|
|
|
}
|
2022-01-25 21:49:45 -05:00
|
|
|
_tf_ssb_import_add_app(file->ssb, file->user, file->name);
|
2021-08-22 15:34:28 -04:00
|
|
|
}
|
|
|
|
sqlite3_finalize(statement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uv_fs_req_cleanup(req);
|
|
|
|
uv_fs_close(tf_ssb_get_loop(file->ssb), req, file->file, _tf_ssb_import_file_close);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _tf_ssb_import_file_open(uv_fs_t* req)
|
|
|
|
{
|
|
|
|
tf_import_file_t* file = req->data;
|
|
|
|
file->file = req->result;
|
|
|
|
uv_fs_req_cleanup(req);
|
|
|
|
uv_fs_read(tf_ssb_get_loop(file->ssb), req, file->file, &(uv_buf_t) { .base = file->data, .len = k_ssb_blob_bytes_max }, 1, 0, _tf_ssb_import_file_read);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct _tf_import_t {
|
|
|
|
tf_ssb_t* ssb;
|
|
|
|
const char* user;
|
|
|
|
const char* parent;
|
|
|
|
uv_fs_t req;
|
|
|
|
int work_left;
|
|
|
|
} tf_import_t;
|
|
|
|
|
|
|
|
static void _tf_ssb_import_scandir(uv_fs_t* req)
|
|
|
|
{
|
|
|
|
tf_import_t* import = req->data;
|
|
|
|
uv_dirent_t ent;
|
2021-10-10 17:51:38 -04:00
|
|
|
while (uv_fs_scandir_next(req, &ent) == 0)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
size_t len = strlen(import->parent) + strlen(ent.name) + 2;
|
2022-06-04 13:04:51 -04:00
|
|
|
char* path = tf_malloc(len);
|
2021-08-22 15:34:28 -04:00
|
|
|
snprintf(path, len, "%s/%s", import->parent, ent.name);
|
2021-10-10 17:51:38 -04:00
|
|
|
if (ent.type == UV_DIRENT_DIR)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
tf_ssb_import(import->ssb, import->user, path);
|
2021-10-10 17:51:38 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
size_t size = sizeof(tf_import_file_t) + strlen(import->parent) +1 + strlen(ent.name) + 1;
|
2022-06-04 13:04:51 -04:00
|
|
|
tf_import_file_t* file = tf_malloc(size);
|
2021-08-22 15:34:28 -04:00
|
|
|
memset(file, 0, size);
|
|
|
|
file->ssb = import->ssb;
|
|
|
|
file->user = import->user;
|
|
|
|
file->parent = (void*)(file + 1);
|
|
|
|
file->name = file->parent + strlen(import->parent) + 1;
|
|
|
|
file->req.data = file;
|
|
|
|
file->work_left = &import->work_left;
|
|
|
|
memcpy((char*)file->parent, import->parent, strlen(import->parent) + 1);
|
|
|
|
memcpy((char*)file->name, ent.name, strlen(ent.name) + 1);
|
|
|
|
|
|
|
|
import->work_left++;
|
|
|
|
int r = uv_fs_open(tf_ssb_get_loop(import->ssb), &file->req, path, 0, 0, _tf_ssb_import_file_open);
|
2021-10-10 17:51:38 -04:00
|
|
|
if (r < 0)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
printf("Failed to open %s: %s.\n", path, uv_strerror(r));
|
2022-06-04 13:04:51 -04:00
|
|
|
tf_free(file);
|
2021-08-22 15:34:28 -04:00
|
|
|
import->work_left--;
|
|
|
|
}
|
|
|
|
}
|
2022-06-04 13:04:51 -04:00
|
|
|
tf_free(path);
|
2021-08-22 15:34:28 -04:00
|
|
|
}
|
|
|
|
import->work_left--;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path)
|
|
|
|
{
|
|
|
|
tf_import_t import = {
|
|
|
|
.ssb = ssb,
|
|
|
|
.user = user,
|
|
|
|
.parent = path,
|
|
|
|
.work_left = 1,
|
|
|
|
};
|
|
|
|
import.req.data = &import;
|
2022-01-12 19:16:27 -05:00
|
|
|
sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000);
|
2021-08-22 15:34:28 -04:00
|
|
|
int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &import.req, path, 0, _tf_ssb_import_scandir);
|
2021-10-10 17:51:38 -04:00
|
|
|
if (r)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
printf("Failed to scan directory %s: %s.", path, uv_strerror(r));
|
|
|
|
}
|
|
|
|
|
2021-10-10 17:51:38 -04:00
|
|
|
while (import.work_left > 0)
|
|
|
|
{
|
2021-08-22 15:34:28 -04:00
|
|
|
uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE);
|
|
|
|
}
|
|
|
|
uv_fs_req_cleanup(&import.req);
|
|
|
|
}
|