diff --git a/inc/lektor/database.h b/inc/lektor/database.h index 4da2d086ac7ee9ca666078a5a341594198ef4f21..c6a399b88a301266cef52c3587bb381ee590dc1b 100644 --- a/inc/lektor/database.h +++ b/inc/lektor/database.h @@ -103,9 +103,9 @@ typedef bool (*lkt_search_database_func)(struct lkt_state *srv, size_t c, int id typedef bool (*lkt_search_queue_func) (struct lkt_state *srv, size_t c, int id, int id_len, const char *row); typedef bool (*lkt_search_sticker_func) (struct lkt_state *srv, size_t c, const char *sticker, const char *type, int uri, int value); -bool database_search_database_init(volatile sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); -bool database_search_queue_init (volatile sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); -bool database_search_sticker_init (volatile sqlite3 *db, char *type, char *name, struct lkt_search *ret); +bool database_search_database_init(volatile sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); +bool database_search_queue_init (volatile sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); +bool database_search_sticker_init (volatile sqlite3 *db, char *type, char *name, int uri, char op, int value, struct lkt_search *ret); bool database_search_iter(struct lkt_search *item); /* Next and prev operation on the queue. */ @@ -147,17 +147,7 @@ bool database_user_authentificate(volatile sqlite3 *db, const char *password); bool database_user_add(volatile sqlite3 *db, const char *username, const char *password); /* Stickers manipulations. */ -struct sticker_callback { - struct lkt_state *srv; - size_t c; - int uri, value, is_ok; - const char *name; - bool (*call)(void *args, const char *sticker, const char *type, int uri, int value); -}; - bool database_sticker_create (volatile sqlite3 *db, const char *name); bool database_sticker_delete (volatile sqlite3 *db, const char *name); bool database_sticker_delete_specify(volatile sqlite3 *sb, const char *type, int uri, const char *name); -bool database_sticker_list (volatile sqlite3 *db, const char *type, struct sticker_callback *call); bool database_sticker_set (volatile sqlite3 *db, const char *type, const char *name, int uri, int value); -bool database_sticker_get (volatile sqlite3 *db, const char *type, const char *name, int uri, struct sticker_callback *call); diff --git a/scripts/init.sql b/scripts/init.sql index 040b0ff1005adb785082cc1a462f9e2436df7318..98d88281bebf0cb13ffa638df8ec077dedb615a5 100644 --- a/scripts/init.sql +++ b/scripts/init.sql @@ -98,6 +98,7 @@ INSERT OR REPLACE INTO users (username, password) VALUES ('sakura', 'hashire'); -- Used to implement the stickers MPD functionnality, documentation can be found -- here: https://www.musicpd.org/doc/html/protocol.html#stickers. Need to be -- authentified to use stickers commands. Support tags for `song` and `plt`. +-- URIs in stickers are kara/playlist IDs. CREATE TABLE IF NOT EXISTS 'stickers' ( id INTEGER PRIMARY KEY diff --git a/src/commands.c b/src/commands.c index 4e2b1a5b585ca60cc51a95184d2eca6f70f1671a..b92c2a106bd1a464bf46ed14e96fd2b91893ee0a 100644 --- a/src/commands.c +++ b/src/commands.c @@ -713,82 +713,11 @@ command_user_add(struct lkt_state *srv, size_t c, volatile sqlite3 *db, char *ar /* Stickers */ static bool -sticker_send_one_value(void *_args, const char *sticker, const char *type, int uri, int value) +sticker_send(struct lkt_state *srv, size_t c, char *name, int id, int value) { - UNUSED(sticker, type, uri); - struct sticker_callback *args = (struct sticker_callback *) _args; - struct lkt_message *out = lkt_message_new(); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value); - lkt_state_send(args->srv, args->c, out); - return false; -} - -static bool -sticker_send_all(void *_args, const char *sticker, const char *type, int uri, int value) -{ - struct sticker_callback *args = (struct sticker_callback *) _args; - struct lkt_message *out = lkt_message_new(); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%s: %d\nsticker: %s\nvalue: %d\n", - type, uri, sticker, value); - lkt_state_send(args->srv, args->c, out); - return true; -} - -static bool -sticker_send_check_uri(void *_args, const char *sticker, const char *type, int uri, int value) -{ - struct sticker_callback *args = (struct sticker_callback *) _args; - UNUSED(type); - - if (uri == args->uri) { - struct lkt_message *out = lkt_message_new(); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%s: %d\n", sticker, value); - lkt_state_send(args->srv, args->c, out); - } - - return true; -} - -static bool -sticker_send_value_check_uri_name(void *_args, const char *sticker, const char *type, int uri, int value) -{ - struct sticker_callback *args = (struct sticker_callback *) _args; - struct lkt_message *out; - UNUSED(type); - - if (uri == args->uri || !strcasecmp(sticker, args->name)) { - out = lkt_message_new(); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value); - lkt_state_send(args->srv, args->c, out); - } - - return true; -} - -static bool -sticker_check_is_present_eq(void *_args, const char *sticker, const char *type, int uri, int value) -{ - UNUSED(type); - struct sticker_callback *args = (struct sticker_callback *) _args; - args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value == args->value); - return true; -} - -static bool -sticker_check_is_present_lt(void *_args, const char *sticker, const char *type, int uri, int value) -{ - UNUSED(type); - struct sticker_callback *args = (struct sticker_callback *) _args; - args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value < args->value); - return true; -} - -static bool -sticker_check_is_present_gt(void *_args, const char *sticker, const char *type, int uri, int value) -{ - UNUSED(type); - struct sticker_callback *args = (struct sticker_callback *) _args; - args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value > args->value); + struct lkt_message *msg = lkt_message_new(); + msg->data_len = snprintf(msg->data, LKT_MESSAGE_ARGS_MAX, "%d: %s . %d", id, name, value); + lkt_state_send(srv, c, msg); return true; } @@ -800,12 +729,14 @@ command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS char *endptr; STRTOL(uri, argv[1], endptr, err); RETURN_IF(err, "STRTOL failed", false); - struct sticker_callback cb = { + struct lkt_search cb = { .srv = srv, .c = c, - .call = sticker_send_one_value, }; - RETURN_UNLESS(database_sticker_get(srv->db, argv[0], argv[2], uri, &cb), "Failed to get sticker", false); + if (!database_search_sticker_init(srv->db, argv[0], argv[2], uri, 0, 0, &cb)) + return false; + while(database_search_iter(&cb)) + continue; return true; } @@ -827,9 +758,10 @@ command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS bool command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]) { - struct sticker_callback callback = { + struct lkt_search callback = { .srv = srv, .c = c, + .call = (void(*)(void)) sticker_send, }; /* Simple list {type} {uri} command */ @@ -855,38 +787,29 @@ command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARG goto unknown; just_list_all: - callback.call = sticker_send_all; - return database_sticker_list(srv->db, argv[0], &callback); + if (!database_search_sticker_init(srv->db, argv[0], NULL, 0, 0, 0, &callback)) + return false; + goto iter; simple_list_command: - callback.uri = atoi(argv[1]); /* FIXME: Use strtol. */ - callback.call = sticker_send_check_uri; - return database_sticker_list(srv->db, argv[0], &callback); + if (!database_search_sticker_init(srv->db, argv[0], NULL, atoi(argv[1]), 0, 0, &callback)) + return false; + goto iter; list_stickers_in_uri: - callback.uri = atoi(argv[1]); /* FIXME: Use strtol. */ - callback.name = argv[2]; - callback.call = sticker_send_value_check_uri_name; - return database_sticker_list(srv->db, argv[0], &callback); - return false; + if (!database_search_sticker_init(srv->db, argv[0], argv[2], atoi(argv[1]), 0, 0, &callback)) + return false; + goto iter; list_stickers_check_value: - callback.uri = atoi(argv[1]); /* FIXME: Use strtol. */ - callback.value = atoi(argv[4]); /* FIXME: Use strtol. */ - callback.name = argv[2]; - switch (argv[3][0]) { - case '=': - callback.call = sticker_check_is_present_eq; - return database_sticker_list(srv->db, argv[0], &callback); - case '<': - callback.call = sticker_check_is_present_lt; - return database_sticker_list(srv->db, argv[0], &callback); - case '>': - callback.call = sticker_check_is_present_gt; - return database_sticker_list(srv->db, argv[0], &callback); - default: - return 0; - } + if (!database_search_sticker_init(srv->db, argv[0], argv[2], atoi(argv[1]), argv[3][0], atoi(argv[4]), &callback)) + return false; + goto iter; + +iter: + while (database_search_iter(&callback)) + continue; + return true; unknown: LOG_ERROR_SCT("COMMAND", "%s", "Specified command is invalid or unknown"); diff --git a/src/database/find.c b/src/database/find.c index c95a2374aa30b9ff2ad2c5716b0f00da61c56889..2d378348c93fddb45143ad77d8ac88d63a7a6cef 100644 --- a/src/database/find.c +++ b/src/database/find.c @@ -35,7 +35,7 @@ error: } bool -database_search_sticker_init(volatile sqlite3 *db, char *type, char *name, struct lkt_search *ret) +database_search_sticker_init(volatile sqlite3 *db, char *type, char *name, int uri, char op, int value, struct lkt_search *ret) { /* No bound checks in strcats, should be fine. Possible SQL injection, depend on the `type`. */ static const char *SQL_all_types = @@ -47,17 +47,20 @@ database_search_sticker_init(volatile sqlite3 *db, char *type, char *name, struc "ON sts.sticker = 'stickers'.id"; static const char *SQL_one_type = "SELECT name, 'stickers.%s'.id, value " - "FROM 'stickers' " + "FROM 'stickers.%s' AS sts" "LEFT OUTER JOIN 'stickers' " - "ON 'stickers'.id = 'stickers.%s'.sticker"; - static const char *SQL_check_name = " AND name = ?;"; + "ON 'stickers'.id = sts.sticker"; char SQL[LKT_MAX_SQLITE_STATEMENT]; if (type == NULL) memcpy(SQL, SQL_all_types, strlen(SQL_all_types) + 1); else sprintf(SQL, SQL_one_type, type, type); - strcat(SQL, name ? SQL_check_name : ";"); + if (uri != 0) + sprintf(SQL, " AND sts.id = %d", uri); + if (op != 0) + sprintf(SQL, " AND sts.value %s %d", op == '>' ? ">=" : op == '<' ? "<=" : "=", value); + strcat(SQL, name ? " AND name = ?;": ";"); SQLITE_PREPARE(db, ret->stmt, SQL, error); if (name) diff --git a/src/database/stickers.c b/src/database/stickers.c index 376621987d250e6819dd9feb44637f7a6e44d2ad..bc49c60339d52926e49cf96e6dd7197fdf2b8f20 100644 --- a/src/database/stickers.c +++ b/src/database/stickers.c @@ -45,66 +45,6 @@ error: return ret; } -bool -database_sticker_list(volatile sqlite3 *db, const char *type, struct sticker_callback *call) -{ - const char *SQL = NULL; - int ret = false, uri, value; - sqlite3_stmt *stmt; - const char *sticker; - - if (type == NULL) - SQL = - "SELECT name, sts.id, value FROM 'stickers' LEFT OUTER JOIN " - "( SELECT id, sticker, value FROM 'stickers.song'" - " UNION" - " SELECT id, sticker, value FROM 'stickers.plt'" - ") AS sts" - "ON sts.sticker = 'stickers'.id;"; - else if (!strcasecmp(type, "plt")) - SQL = - "SELECT name, 'stickers.plt'.id, value " - "FROM 'stickers' " - "LEFT OUTER JOIN 'stickers' " - "ON 'stickers'.id = 'stickers.plt'.sticker;"; - else if (!strcasecmp(type, "song")) - SQL = - "SELECT name, 'stickers.song'.id, value " - "FROM 'stickers' " - "LEFT OUTER JOIN 'stickers' " - "ON 'stickers'.id = 'stickers.song'.sticker;"; - else { - LOG_ERROR_SCT("DB", "Type '%s' is invalid", type); - return false; - } - - SQLITE_PREPARE(db, stmt, SQL, error); - - for (;;) { - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - sticker = (const char *) sqlite3_column_text(stmt, 0); - uri = sqlite3_column_int(stmt, 1); - value = sqlite3_column_int(stmt, 2); - if (!call->call(call, sticker, type, uri, value)) - goto end_loop; - continue; - - case SQLITE_DONE: - goto end_loop; - - default: - goto error; - } - } - -end_loop: - ret = true; -error: - sqlite3_finalize(stmt); - return ret; -} - bool database_sticker_set(volatile sqlite3 *db, const char *type, const char *name, int uri, int value) { @@ -146,58 +86,6 @@ error: return ret; } -bool -database_sticker_get(volatile sqlite3 *db, const char *type, const char *name, int uri, struct sticker_callback *call) -{ - const char *SQL = NULL; - sqlite3_stmt *stmt; - int ret = false, value; - - if (!strcasecmp(type, "song")) - SQL = - "SELECT value " - "FROM 'stickers' " - "JOIN 'stickers.song'" - " ON 'stickers'.id = 'stickers.song'.sticker" - " AND 'stickers'.name = ?;\n"; - else if (!strcasecmp(type, "plt")) - SQL = - "SELECT value " - "FROM 'stickers' " - "JOIN 'stickers.plt'" - " ON 'stickers'.id = 'stickers.plt'.sticker" - " AND 'stickers'.name = ?;\n"; - else { - LOG_ERROR_SCT("DB", "Type '%s' is invalid", type); - return false; - } - - SQLITE_PREPARE(db, stmt, SQL, error); - SQLITE_BIND_TEXT(db, stmt, 1, name, error); - - for (;;) { - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - value = sqlite3_column_int(stmt, 1); - if (!call->call(call, name, type, uri, value)) - goto end_loop; - continue; - - case SQLITE_DONE: - goto end_loop; - - default: - goto error; - } - } - -end_loop: - ret = true; -error: - sqlite3_finalize(stmt); - return ret; -} - bool database_sticker_delete_specify(volatile sqlite3 *db, const char *type, int uri, const char *name) { diff --git a/src/main/lkt.c b/src/main/lkt.c index ea03894d8bd2574d6c0eb9acca7b65c1d0bafae6..4c69449bb3a2d4db76ed837810870b69412437ea 100644 --- a/src/main/lkt.c +++ b/src/main/lkt.c @@ -378,7 +378,7 @@ ok: noreturn void queue_pop__(struct lkt_cmd_args *args) { - fail_if(!args->argc, "Invalid argument"); + fail_if(args->argc, "Invalid argument"); int songid = 0; char buff[LKT_MESSAGE_MAX]; FILE *sock = lkt_connect();