js: Move /save to C.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 15m51s
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 15m51s
This commit is contained in:
parent
01b8c209de
commit
863e50203e
65
core/core.js
65
core/core.js
@ -932,70 +932,6 @@ async function blobHandler(request, response, blobId, uri) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let process;
|
let process;
|
||||||
if (uri == '/save') {
|
|
||||||
let match;
|
|
||||||
if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) {
|
|
||||||
let user = match[1];
|
|
||||||
let appName = match[2];
|
|
||||||
let credentials = await httpd.auth_query(request.headers);
|
|
||||||
if (
|
|
||||||
credentials &&
|
|
||||||
credentials.session &&
|
|
||||||
(credentials.session.name == user ||
|
|
||||||
(credentials.permissions.administration && user == 'core'))
|
|
||||||
) {
|
|
||||||
let database = new Database(user);
|
|
||||||
|
|
||||||
let app_object = JSON.parse(utf8Decode(request.body));
|
|
||||||
let previous_id = await database.get('path:' + appName);
|
|
||||||
if (previous_id) {
|
|
||||||
try {
|
|
||||||
let previous_object = JSON.parse(
|
|
||||||
utf8Decode(await ssb.blobGet(previous_id))
|
|
||||||
);
|
|
||||||
delete previous_object.previous;
|
|
||||||
delete app_object.previous;
|
|
||||||
if (JSON.stringify(previous_object) == JSON.stringify(app_object)) {
|
|
||||||
response.writeHead(200, {
|
|
||||||
'Content-Type': 'text/plain; charset=utf-8',
|
|
||||||
});
|
|
||||||
response.end('/' + previous_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
app_object.previous = previous_id;
|
|
||||||
let newBlobId = await ssb.blobStore(JSON.stringify(app_object));
|
|
||||||
|
|
||||||
let apps = new Set();
|
|
||||||
let apps_original = await database.get('apps');
|
|
||||||
try {
|
|
||||||
apps = new Set(JSON.parse(apps_original));
|
|
||||||
} catch {}
|
|
||||||
if (!apps.has(appName)) {
|
|
||||||
apps.add(appName);
|
|
||||||
}
|
|
||||||
apps = JSON.stringify([...apps].sort());
|
|
||||||
if (apps != apps_original) {
|
|
||||||
await database.set('apps', apps);
|
|
||||||
}
|
|
||||||
await database.set('path:' + appName, newBlobId);
|
|
||||||
response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
|
|
||||||
response.end('/' + newBlobId);
|
|
||||||
} else {
|
|
||||||
response.writeHead(401, {'Content-Type': 'text/plain; charset=utf-8'});
|
|
||||||
response.end('401 Unauthorized');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (blobId === '') {
|
|
||||||
let newBlobId = await ssb.blobStore(request.body);
|
|
||||||
response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
|
|
||||||
response.end('/' + newBlobId);
|
|
||||||
} else {
|
|
||||||
response.writeHead(400, {'Content-Type': 'text/plain; charset=utf-8'});
|
|
||||||
response.end('Invalid name.');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let data;
|
let data;
|
||||||
let match;
|
let match;
|
||||||
let id;
|
let id;
|
||||||
@ -1074,7 +1010,6 @@ async function blobHandler(request, response, blobId, uri) {
|
|||||||
} else {
|
} else {
|
||||||
sendData(response, data, undefined, {});
|
sendData(response, data, undefined, {});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssb.addEventListener('message', function () {
|
ssb.addEventListener('message', function () {
|
||||||
|
@ -407,6 +407,11 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
|
|||||||
|
|
||||||
if (connection->body_length == connection->content_length)
|
if (connection->body_length == connection->content_length)
|
||||||
{
|
{
|
||||||
|
/* Null-terminate for convenience. */
|
||||||
|
if (connection->body)
|
||||||
|
{
|
||||||
|
((char*)connection->body)[connection->body_length] = '\0';
|
||||||
|
}
|
||||||
tf_http_request_t* request = tf_malloc(sizeof(tf_http_request_t));
|
tf_http_request_t* request = tf_malloc(sizeof(tf_http_request_t));
|
||||||
*request = (tf_http_request_t) {
|
*request = (tf_http_request_t) {
|
||||||
.http = connection->http,
|
.http = connection->http,
|
||||||
@ -500,7 +505,7 @@ static size_t _http_on_read_plain_internal(tf_http_connection_t* connection, con
|
|||||||
|
|
||||||
if (connection->content_length)
|
if (connection->content_length)
|
||||||
{
|
{
|
||||||
connection->body = tf_realloc(connection->body, connection->content_length);
|
connection->body = tf_realloc(connection->body, connection->content_length + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_http_find_handler(connection->http, connection->path, &connection->callback, &connection->trace_name, &connection->user_data) || !connection->callback)
|
if (!_http_find_handler(connection->http, connection->path, &connection->callback, &connection->trace_name, &connection->user_data) || !connection->callback)
|
||||||
|
260
src/httpd.js.c
260
src/httpd.js.c
@ -966,6 +966,56 @@ static void _httpd_endpoint_static(tf_http_request_t* request)
|
|||||||
tf_file_stat(task, path, _httpd_endpoint_static_stat, request);
|
tf_file_stat(task, path, _httpd_endpoint_static_stat, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _user_app_t
|
||||||
|
{
|
||||||
|
const char* user;
|
||||||
|
const char* app;
|
||||||
|
} user_app_t;
|
||||||
|
|
||||||
|
static user_app_t* _parse_user_app_from_path(const char* path, const char* expected_suffix)
|
||||||
|
{
|
||||||
|
if (!path || path[0] != '/' || path[1] != '~')
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length = strlen(path);
|
||||||
|
size_t suffix_length = expected_suffix ? strlen(expected_suffix) : 0;
|
||||||
|
if (length < suffix_length || strcmp(path + length - suffix_length, expected_suffix) != 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* slash = strchr(path + 2, '/');
|
||||||
|
if (!slash)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* user = path + 2;
|
||||||
|
size_t user_length = (size_t)(slash - user);
|
||||||
|
const char* app = slash + 1;
|
||||||
|
size_t app_length = (size_t)(length - suffix_length - user_length - 3);
|
||||||
|
user_app_t* result = tf_malloc(sizeof(user_app_t) + user_length + 1 + app_length + 1);
|
||||||
|
|
||||||
|
*result = (user_app_t) {
|
||||||
|
.user = (char*)(result + 1),
|
||||||
|
.app = (char*)(result + 1) + user_length + 1,
|
||||||
|
};
|
||||||
|
memcpy((char*)result->user, user, user_length);
|
||||||
|
((char*)result->user)[user_length] = '\0';
|
||||||
|
memcpy((char*)result->app, app, app_length);
|
||||||
|
((char*)result->app)[app_length] = '\0';
|
||||||
|
|
||||||
|
if (!_is_name_valid(result->user) || !_is_name_valid(result->app))
|
||||||
|
{
|
||||||
|
tf_free(result);
|
||||||
|
result = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct _view_t
|
typedef struct _view_t
|
||||||
{
|
{
|
||||||
tf_http_request_t* request;
|
tf_http_request_t* request;
|
||||||
@ -996,24 +1046,23 @@ static void _httpd_endpoint_view_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
view_t* view = user_data;
|
view_t* view = user_data;
|
||||||
tf_http_request_t* request = view->request;
|
tf_http_request_t* request = view->request;
|
||||||
char blob_id[256] = "";
|
char blob_id[256] = "";
|
||||||
if (request->path[0] == '/' && request->path[1] == '~')
|
|
||||||
|
user_app_t* user_app = _parse_user_app_from_path(request->path, "/view");
|
||||||
|
if (user_app)
|
||||||
{
|
{
|
||||||
char user[256] = "";
|
size_t app_path_length = strlen("path:") + strlen(user_app->app) + 1;
|
||||||
char path[1024] = "";
|
char* app_path = tf_malloc(app_path_length);
|
||||||
const char* slash = strchr(request->path + 2, '/');
|
snprintf(app_path, app_path_length, "path:%s", user_app->app);
|
||||||
if (slash)
|
const char* value = tf_ssb_db_get_property(ssb, user_app->user, app_path);
|
||||||
{
|
|
||||||
snprintf(user, sizeof(user), "%.*s", (int)(slash - (request->path + 2)), request->path + 2);
|
|
||||||
snprintf(path, sizeof(path), "path:%.*s", (int)(strlen(slash + 1) - strlen("/view")), slash + 1);
|
|
||||||
const char* value = tf_ssb_db_get_property(ssb, user, path);
|
|
||||||
snprintf(blob_id, sizeof(blob_id), "%s", value);
|
snprintf(blob_id, sizeof(blob_id), "%s", value);
|
||||||
|
tf_free(app_path);
|
||||||
tf_free((void*)value);
|
tf_free((void*)value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (request->path[0] == '/' && request->path[1] == '&')
|
else if (request->path[0] == '/' && request->path[1] == '&')
|
||||||
{
|
{
|
||||||
snprintf(blob_id, sizeof(blob_id), "%.*s", (int)(strlen(request->path) - strlen("/view") - 1), request->path + 1);
|
snprintf(blob_id, sizeof(blob_id), "%.*s", (int)(strlen(request->path) - strlen("/view") - 1), request->path + 1);
|
||||||
}
|
}
|
||||||
|
tf_free(user_app);
|
||||||
|
|
||||||
if (*blob_id)
|
if (*blob_id)
|
||||||
{
|
{
|
||||||
@ -1082,6 +1131,162 @@ static void _httpd_endpoint_view(tf_http_request_t* request)
|
|||||||
tf_ssb_run_work(ssb, _httpd_endpoint_view_work, _httpd_endpoint_view_after_work, view);
|
tf_ssb_run_work(ssb, _httpd_endpoint_view_work, _httpd_endpoint_view_after_work, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _save_t
|
||||||
|
{
|
||||||
|
tf_http_request_t* request;
|
||||||
|
int response;
|
||||||
|
char blob_id[256];
|
||||||
|
} save_t;
|
||||||
|
|
||||||
|
static void _httpd_endpoint_save_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
save_t* save = user_data;
|
||||||
|
tf_http_request_t* request = save->request;
|
||||||
|
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
|
||||||
|
|
||||||
|
JSMallocFunctions funcs = { 0 };
|
||||||
|
tf_get_js_malloc_functions(&funcs);
|
||||||
|
JSRuntime* runtime = JS_NewRuntime2(&funcs, NULL);
|
||||||
|
JSContext* context = JS_NewContext(runtime);
|
||||||
|
|
||||||
|
JSValue jwt = _authenticate_jwt(ssb, context, session);
|
||||||
|
JSValue user = JS_GetPropertyStr(context, jwt, "name");
|
||||||
|
const char* user_string = JS_ToCString(context, user);
|
||||||
|
|
||||||
|
if (user_string && _is_name_valid(user_string))
|
||||||
|
{
|
||||||
|
user_app_t* user_app = _parse_user_app_from_path(request->path, "/save");
|
||||||
|
if (user_app)
|
||||||
|
{
|
||||||
|
if (strcmp(user_string, user_app->user) == 0 || (strcmp(user_app->user, "core") == 0 && tf_ssb_db_user_has_permission(ssb, user_string, "administration")))
|
||||||
|
{
|
||||||
|
size_t path_length = strlen("path:") + strlen(user_app->app) + 1;
|
||||||
|
char* app_path = tf_malloc(path_length);
|
||||||
|
snprintf(app_path, path_length, "path:%s", user_app->app);
|
||||||
|
|
||||||
|
const char* old_blob_id = tf_ssb_db_get_property(ssb, user_app->user, app_path);
|
||||||
|
|
||||||
|
JSValue new_app = JS_ParseJSON(context, request->body, request->content_length, NULL);
|
||||||
|
tf_util_report_error(context, new_app);
|
||||||
|
if (JS_IsObject(new_app))
|
||||||
|
{
|
||||||
|
uint8_t* old_blob = NULL;
|
||||||
|
size_t old_blob_size = 0;
|
||||||
|
if (tf_ssb_db_blob_get(ssb, old_blob_id, &old_blob, &old_blob_size))
|
||||||
|
{
|
||||||
|
JSValue old_app = JS_ParseJSON(context, (const char*)old_blob, old_blob_size, NULL);
|
||||||
|
if (JS_IsObject(old_app))
|
||||||
|
{
|
||||||
|
JSAtom previous = JS_NewAtom(context, "previous");
|
||||||
|
JS_DeleteProperty(context, old_app, previous, 0);
|
||||||
|
JS_DeleteProperty(context, new_app, previous, 0);
|
||||||
|
|
||||||
|
JSValue old_app_json = JS_JSONStringify(context, old_app, JS_NULL, JS_NULL);
|
||||||
|
JSValue new_app_json = JS_JSONStringify(context, new_app, JS_NULL, JS_NULL);
|
||||||
|
const char* old_app_str = JS_ToCString(context, old_app_json);
|
||||||
|
const char* new_app_str = JS_ToCString(context, new_app_json);
|
||||||
|
|
||||||
|
if (old_app_str && new_app_str && strcmp(old_app_str, new_app_str) == 0)
|
||||||
|
{
|
||||||
|
snprintf(save->blob_id, sizeof(save->blob_id), "/%s", old_blob_id);
|
||||||
|
save->response = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeCString(context, old_app_str);
|
||||||
|
JS_FreeCString(context, new_app_str);
|
||||||
|
JS_FreeValue(context, old_app_json);
|
||||||
|
JS_FreeValue(context, new_app_json);
|
||||||
|
JS_FreeAtom(context, previous);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, old_app);
|
||||||
|
tf_free(old_blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!save->response)
|
||||||
|
{
|
||||||
|
if (old_blob_id)
|
||||||
|
{
|
||||||
|
JS_SetPropertyStr(context, new_app, "previous", JS_NewString(context, old_blob_id));
|
||||||
|
}
|
||||||
|
JSValue new_app_json = JS_JSONStringify(context, new_app, JS_NULL, JS_NULL);
|
||||||
|
size_t new_app_length = 0;
|
||||||
|
const char* new_app_str = JS_ToCStringLen(context, &new_app_length, new_app_json);
|
||||||
|
|
||||||
|
char blob_id[250] = { 0 };
|
||||||
|
if (tf_ssb_db_blob_store(ssb, (const uint8_t*)new_app_str, new_app_length, blob_id, sizeof(blob_id), NULL) &&
|
||||||
|
tf_ssb_db_set_property(ssb, user_app->user, app_path, blob_id))
|
||||||
|
{
|
||||||
|
tf_ssb_db_add_value_to_array_property(ssb, user_app->user, "apps", user_app->app);
|
||||||
|
snprintf(save->blob_id, sizeof(save->blob_id), "/%s", blob_id);
|
||||||
|
save->response = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeCString(context, new_app_str);
|
||||||
|
JS_FreeValue(context, new_app_json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
save->response = 400;
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, new_app);
|
||||||
|
|
||||||
|
tf_free(app_path);
|
||||||
|
tf_free((void*)old_blob_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
save->response = 401;
|
||||||
|
}
|
||||||
|
tf_free(user_app);
|
||||||
|
}
|
||||||
|
else if (strcmp(request->path, "/save") == 0)
|
||||||
|
{
|
||||||
|
char blob_id[250] = { 0 };
|
||||||
|
if (tf_ssb_db_blob_store(ssb, request->body, request->content_length, blob_id, sizeof(blob_id), NULL))
|
||||||
|
{
|
||||||
|
snprintf(save->blob_id, sizeof(save->blob_id), "/%s", blob_id);
|
||||||
|
save->response = 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
save->response = 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_free((void*)session);
|
||||||
|
JS_FreeCString(context, user_string);
|
||||||
|
JS_FreeValue(context, user);
|
||||||
|
JS_FreeValue(context, jwt);
|
||||||
|
JS_FreeContext(context);
|
||||||
|
JS_FreeRuntime(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_save_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
save_t* save = user_data;
|
||||||
|
tf_http_request_t* request = save->request;
|
||||||
|
if (*save->blob_id)
|
||||||
|
{
|
||||||
|
tf_http_respond(request, 200, NULL, 0, save->blob_id, strlen(save->blob_id));
|
||||||
|
}
|
||||||
|
tf_http_request_unref(request);
|
||||||
|
tf_free(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_save(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
tf_http_request_ref(request);
|
||||||
|
tf_task_t* task = request->user_data;
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
save_t* save = tf_malloc(sizeof(save_t));
|
||||||
|
*save = (save_t) {
|
||||||
|
.request = request,
|
||||||
|
};
|
||||||
|
tf_ssb_run_work(ssb, _httpd_endpoint_save_work, _httpd_endpoint_save_after_work, save);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct _delete_t
|
typedef struct _delete_t
|
||||||
{
|
{
|
||||||
tf_http_request_t* request;
|
tf_http_request_t* request;
|
||||||
@ -1104,32 +1309,31 @@ static void _httpd_endpoint_delete_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
const char* user_string = JS_ToCString(context, user);
|
const char* user_string = JS_ToCString(context, user);
|
||||||
if (user_string && _is_name_valid(user_string))
|
if (user_string && _is_name_valid(user_string))
|
||||||
{
|
{
|
||||||
size_t length = strlen(user_string);
|
user_app_t* user_app = _parse_user_app_from_path(request->path, "/delete");
|
||||||
if (request->path && request->path[0] == '/' && request->path[1] == '~' &&
|
if (user_app)
|
||||||
(strncmp(request->path + 2, user_string, length) == 0 ||
|
|
||||||
(strncmp(request->path + 2, "core", strlen("core")) == 0 && tf_ssb_db_user_has_permission(ssb, user_string, "administration"))) &&
|
|
||||||
request->path[2 + length] == '/')
|
|
||||||
{
|
{
|
||||||
char* app_name = tf_strdup(request->path + 2 + length + 1);
|
if (strcmp(user_string, user_app->user) == 0 || (strcmp(user_app->user, "core") == 0 && tf_ssb_db_user_has_permission(ssb, user_string, "administration")))
|
||||||
if (app_name)
|
|
||||||
{
|
{
|
||||||
if (strlen(app_name) > strlen("/delete") && strcmp(app_name + strlen(app_name) - strlen("/delete"), "/delete") == 0)
|
size_t path_length = strlen("path:") + strlen(user_app->app) + 1;
|
||||||
{
|
|
||||||
app_name[strlen(app_name) - strlen("/delete")] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t path_length = strlen("path:") + strlen(app_name) + 1;
|
|
||||||
char* app_path = tf_malloc(path_length);
|
char* app_path = tf_malloc(path_length);
|
||||||
snprintf(app_path, path_length, "path:%s", app_name);
|
snprintf(app_path, path_length, "path:%s", user_app->app);
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
changed = tf_ssb_db_remove_value_from_array_property(ssb, user_string, "apps", app_name) || changed;
|
changed = tf_ssb_db_remove_value_from_array_property(ssb, user_string, "apps", user_app->app) || changed;
|
||||||
changed = tf_ssb_db_remove_property(ssb, user_string, app_path) || changed;
|
changed = tf_ssb_db_remove_property(ssb, user_string, app_path) || changed;
|
||||||
delete->response = changed ? 200 : 404;
|
delete->response = changed ? 200 : 404;
|
||||||
tf_free(app_name);
|
|
||||||
tf_free(app_path);
|
tf_free(app_path);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete->response = 401;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete->response = 404;
|
||||||
|
}
|
||||||
|
tf_free(user_app);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1898,7 +2102,9 @@ void tf_httpd_register(JSContext* context)
|
|||||||
tf_http_add_handler(http, "/~*/*/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/~*/*/", _httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/&*.sha256/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/&*.sha256/", _httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/*/view", _httpd_endpoint_view, NULL, task);
|
tf_http_add_handler(http, "/*/view", _httpd_endpoint_view, NULL, task);
|
||||||
|
tf_http_add_handler(http, "/~*/*/save", _httpd_endpoint_save, NULL, task);
|
||||||
tf_http_add_handler(http, "/~*/*/delete", _httpd_endpoint_delete, NULL, task);
|
tf_http_add_handler(http, "/~*/*/delete", _httpd_endpoint_delete, NULL, task);
|
||||||
|
tf_http_add_handler(http, "/save", _httpd_endpoint_save, NULL, task);
|
||||||
|
|
||||||
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
|
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
|
||||||
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);
|
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);
|
||||||
|
21
src/ssb.db.c
21
src/ssb.db.c
@ -1825,6 +1825,27 @@ bool tf_ssb_db_remove_value_from_array_property(tf_ssb_t* ssb, const char* id, c
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool tf_ssb_db_add_value_to_array_property(tf_ssb_t* ssb, const char* id, const char* key, const char* value)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||||
|
sqlite3_stmt* statement = NULL;
|
||||||
|
if (sqlite3_prepare(db,
|
||||||
|
"INSERT INTO properties (id, key, value) VALUES (?1, ?2, json_array(?3)) ON CONFLICT DO UPDATE SET value = json_insert(properties.value, '$[#]', ?3) WHERE "
|
||||||
|
"properties.id = ?1 AND properties.key = ?2 AND NOT EXISTS (SELECT 1 FROM json_each(properties.value) AS entry WHERE entry.value = ?3)",
|
||||||
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, key, -1, NULL) == SQLITE_OK &&
|
||||||
|
sqlite3_bind_text(statement, 3, value, -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
result = sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) != 0;
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
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)
|
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;
|
sqlite3_stmt* statement = NULL;
|
||||||
|
10
src/ssb.db.h
10
src/ssb.db.h
@ -418,6 +418,16 @@ bool tf_ssb_db_remove_property(tf_ssb_t* ssb, const char* id, const char* key);
|
|||||||
*/
|
*/
|
||||||
bool tf_ssb_db_remove_value_from_array_property(tf_ssb_t* ssb, const char* id, const char* key, const char* value);
|
bool tf_ssb_db_remove_value_from_array_property(tf_ssb_t* ssb, const char* id, const char* key, const char* value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Ensure a value is in an entry in the properties table that is a JSON array.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param id The user.
|
||||||
|
** @param key The property key.
|
||||||
|
** @param value The value to add to the JSON array.
|
||||||
|
** @return true if the property was updated.
|
||||||
|
*/
|
||||||
|
bool tf_ssb_db_add_value_to_array_property(tf_ssb_t* ssb, const char* id, const char* key, const char* value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Resolve a hostname to its index path by global settings.
|
** Resolve a hostname to its index path by global settings.
|
||||||
** @param ssb The SSB instance.
|
** @param ssb The SSB instance.
|
||||||
|
@ -158,6 +158,29 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
|
|||||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
||||||
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
||||||
|
|
||||||
|
const char* value = tf_ssb_db_get_property(ssb0, "user", "array");
|
||||||
|
assert(value == NULL);
|
||||||
|
assert(tf_ssb_db_add_value_to_array_property(ssb0, "user", "array", "1") == true);
|
||||||
|
value = tf_ssb_db_get_property(ssb0, "user", "array");
|
||||||
|
assert(strcmp(value, "[\"1\"]") == 0);
|
||||||
|
tf_free((void*)value);
|
||||||
|
assert(tf_ssb_db_add_value_to_array_property(ssb0, "user", "array", "2") == true);
|
||||||
|
value = tf_ssb_db_get_property(ssb0, "user", "array");
|
||||||
|
assert(strcmp(value, "[\"1\",\"2\"]") == 0);
|
||||||
|
tf_free((void*)value);
|
||||||
|
assert(tf_ssb_db_add_value_to_array_property(ssb0, "user", "array", "1") == false);
|
||||||
|
assert(tf_ssb_db_add_value_to_array_property(ssb0, "user", "array", "2") == false);
|
||||||
|
value = tf_ssb_db_get_property(ssb0, "user", "array");
|
||||||
|
assert(strcmp(value, "[\"1\",\"2\"]") == 0);
|
||||||
|
tf_free((void*)value);
|
||||||
|
assert(tf_ssb_db_remove_value_from_array_property(ssb0, "user", "array", "1") == true);
|
||||||
|
assert(tf_ssb_db_remove_value_from_array_property(ssb0, "user", "array", "1") == false);
|
||||||
|
assert(tf_ssb_db_remove_value_from_array_property(ssb0, "user", "array", "2") == true);
|
||||||
|
assert(tf_ssb_db_remove_value_from_array_property(ssb0, "user", "array", "2") == false);
|
||||||
|
value = tf_ssb_db_get_property(ssb0, "user", "array");
|
||||||
|
assert(strcmp(value, "[]") == 0);
|
||||||
|
tf_free((void*)value);
|
||||||
|
|
||||||
uv_idle_t idle0 = { .data = ssb0 };
|
uv_idle_t idle0 = { .data = ssb0 };
|
||||||
uv_idle_init(&loop, &idle0);
|
uv_idle_init(&loop, &idle0);
|
||||||
uv_idle_start(&idle0, _ssb_test_idle);
|
uv_idle_start(&idle0, _ssb_test_idle);
|
||||||
|
Loading…
Reference in New Issue
Block a user