diff --git a/inc/lektor/database.h b/inc/lektor/database.h index ea396b229b66930e474fd911e2159528e9892c11..b6fe3bf2aa4333f7223d80d612ef99b1849be357 100644 --- a/inc/lektor/database.h +++ b/inc/lektor/database.h @@ -92,21 +92,24 @@ struct lkt_search { lkt_search_sticker, } type; - void (*call)(void); /* Called during the iter phase, casted */ + void (*call)(void); /* Called during the iter phase, casted */ struct lkt_state *srv; size_t c; - long continuation; /* The continuation state of the client */ - int msg_count; /* How much messages we can send */ + long continuation; /* The continuation state of the client */ + int msg_count; /* How much messages we can send */ - const char *st_name; /* Stickers and playlists */ - int st_value; /* The value of a sticker */ - int st_uri; /* URI of a sticker */ - char st_op; /* Comparaison operator for stickers */ - char *st_type; /* Type of sticker */ + union { + const char *st_name; /* Sticker name */ + const char *plt_name; /* Playlist name */ + }; + int st_value; /* The value of a sticker */ + int st_uri; /* URI of a sticker */ + char st_op; /* Comparaison operator for stickers */ + char *st_type; /* Type of sticker */ - struct lkt_uri ka_uri; /* Search by uri, depract the use of ka_rgx */ + struct lkt_uri ka_uri; /* Search by uri */ }; typedef bool (*lkt_search_init_add_func)(volatile sqlite3 *, struct lkt_uri *, int); @@ -117,6 +120,7 @@ typedef bool (*lkt_search_sticker_func) (struct lkt_state *srv, size_t c, const bool database_search_database_init(volatile sqlite3 *db, struct lkt_search *ret); bool database_search_queue_init (volatile sqlite3 *db, struct lkt_search *ret); bool database_search_sticker_init (volatile sqlite3 *db, struct lkt_search *ret); +bool database_search_playlist_init(volatile sqlite3 *db, struct lkt_search *ret); bool database_search_iter(struct lkt_search *item); /* Next and prev operation on the queue. */ diff --git a/src/commands.c b/src/commands.c index 92f63810f3b300e1f3d8bcba16a3b6742c1e0daf..497786cee00542d7dac11879057c1949d7179be9 100644 --- a/src/commands.c +++ b/src/commands.c @@ -282,9 +282,8 @@ command_addid(volatile sqlite3 *db, struct lkt_win *win, errno = 0; int i; struct lkt_uri uri = { .type = uri_id }; - for (i = 0; (uri.id = strtol(args[i], NULL, 0)) && ! errno; ++i) { + for (i = 0; (uri.id = strtol(args[i], NULL, 0)) && ! errno; ++i) errno |= database_queue_add_id(db, uri.id, 1); - } *watch_mask_ptr |= MPD_IDLE_PLAYLIST; return ! errno; } @@ -443,15 +442,22 @@ command_find(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX], .srv = srv, .c = c, .continuation = continuation, - .msg_count = lkt_remaining_msg(srv, c) - 3, /* Reserve slots for OK/ACK and continue: */ + /* Reserve slots for OK/ACK and continue: */ + .msg_count = lkt_remaining_msg(srv, c) - 3, .call = (void(*)(void)) lkt_callback_send_row_v2, .ka_uri = {0}, + .plt_name = args[0], /* In case of playlist searchs */ }; /* Check args */ RETURN_UNLESS(args[0], "Invalid argument", false); - RETURN_UNLESS(lkt_uri_from_list(&search.ka_uri, args), - "Failed to create the uri from the cmd", false); + if (!lkt_uri_from_list(&search.ka_uri, args)) { + /* Try from idx 1, in case of playlust searches */ + LOG_DEBUG("COMMAND", "URI may not starts at idx 0, may be because " + "of playlist search. At idx 0, value was '%s'", args[0]); + RETURN_UNLESS(lkt_uri_from_list(&search.ka_uri, &args[1]), + "Failed to create the uri", false); + } /* Make the search langand do the right action */ RETURN_UNLESS(init(srv->db, &search), "Failed to init search", false); diff --git a/src/database/find.c b/src/database/find.c index 6d1024feff341213ab59f3884b39d8b70faad964..30707bf11c7b653f257bc6ed5fb71989936c836f 100644 --- a/src/database/find.c +++ b/src/database/find.c @@ -8,6 +8,12 @@ #include <stdio.h> #include <string.h> +static inline int +__check_sticker_type(const char *type) +{ + return ! ( STR_MATCH(type, "kara") || STR_MATCH(type, "plt") ); +} + bool database_search_database_init(volatile sqlite3 *db, struct lkt_search *ret) { @@ -33,11 +39,43 @@ error: return false; } +bool +database_search_playlist_init(volatile sqlite3 *db, struct lkt_search *ret) +{ + RETURN_UNLESS(ret, "Exit because return pointer is NULL", false); + static const char *SQL_TEMPLATE = + "WITH content AS (" + " SELECT kara.id AS id, string, LENGTH(CAST(kara.id AS TEXT)) AS len" + " FROM kara_playlist" + " JOIN playlist ON playlist.id = playlist_id" + " AND playlist.name COLLATE nocase = ?" + " JOIN kara ON kara.id = kara_id" + " AND %s LIKE ?)" + "SELECT id, string, (SELECT MAX(len) FROM content)" + "FROM content LIMIT %d OFFSET %d;"; + char SQL_STMT[LKT_MAX_SQLITE_STATEMENT]; + ret->type = lkt_search_playlist; + + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE, + ret->ka_uri.column_name, ret->msg_count, ret->continuation); + SQLITE_PREPARE(db, ret->stmt, SQL_STMT, error); + SQLITE_BIND_TEXT(db, ret->stmt, 1, ret->plt_name, error); + SQLITE_BIND_TEXT(db, ret->stmt, 2, ret->ka_uri.value, error); + ret->db = db; + return true; +error: + sqlite3_finalize(ret->stmt); + return false; +} + bool database_search_sticker_init(volatile sqlite3 *db, struct lkt_search *ret) { - /* No bound checks in strcats, should be fine. Possible SQL injection, - depend on the `type`. */ + if (__check_sticker_type(ret->st_type)) { + LOG_ERROR("DB", "Type '%s' is invalid", ret->st_type); + return false; + } + static const char *SQL_all_types = "SELECT name, sts.id, value FROM 'stickers' JOIN " "( SELECT id, sticker, value, 'kara' FROM 'stickers.kara'" @@ -125,19 +163,19 @@ database_search_iter(struct lkt_search *item) switch (item->type) { case lkt_search_database: case lkt_search_queue: - id = sqlite3_column_int(item->stmt, 0); - sql_row = (const char *) sqlite3_column_text(item->stmt, 1); - id_len = sqlite3_column_int(item->stmt, 2); + case lkt_search_playlist: + id = sqlite3_column_int (item->stmt, 0); + sql_row = sqlite3_column_chars(item->stmt, 1); + id_len = sqlite3_column_int (item->stmt, 2); return ((lkt_search_database_func) item->call) (item->srv, item->c, id, id_len, sql_row); case lkt_search_sticker: - sql_row = (const char *) sqlite3_column_text(item->stmt, 0); /* Name */ - id = sqlite3_column_int(item->stmt, 1); /* Id */ - code = sqlite3_column_int(item->stmt, 2); /* Val */ - type = (const char *) sqlite3_column_text(item->stmt, 3); /* Type */ + sql_row = sqlite3_column_chars(item->stmt, 0); /* Name */ + id = sqlite3_column_int (item->stmt, 1); /* Id */ + code = sqlite3_column_int (item->stmt, 2); /* Val */ + type = sqlite3_column_chars(item->stmt, 3); /* Type */ return ((lkt_search_sticker_func) item->call) (item->srv, item->c, sql_row, type, id, code); - case lkt_search_playlist: default: LOG_WARN("DB", "Search type %d is not implemented", item->type); goto end; diff --git a/src/database/queue.c b/src/database/queue.c index 3cd69290dd0d70b7ad1dc8eb2a12e6c885bc946b..03ec706a050ed23e25b61e36f2642c0feeb6c97d 100644 --- a/src/database/queue.c +++ b/src/database/queue.c @@ -669,7 +669,7 @@ database_queue_dump(volatile sqlite3 *db, const char *plt_name) static const char *SQL = "WITH plt_id AS (SELECT playlist.id AS id FROM playlist" " WHERE name COLLATE nocase = ?) " - "INSERT INTO kara_playlist (kara_id, playlist_id)" + "INSERT OR IGNORE INTO kara_playlist (kara_id, playlist_id)" " SELECT DISTINCT kara_id, plt_id.id" " FROM queue, plt_id" " ORDER BY RANDOM();"; diff --git a/src/main/lkt.c b/src/main/lkt.c index 20895dc829b8d4e24d9f5af6a2e628a0c849ce16..6057953f84154b01486ddeb97d9d04ad75354885 100644 --- a/src/main/lkt.c +++ b/src/main/lkt.c @@ -855,7 +855,7 @@ redo: write_socket(sock, "%d listplaylist %s %s://", continuation, args->argv[0], args->argv[1]); - for (i = 1; i < args->argc - 1; ++i) + for (i = 2; i < args->argc - 1; ++i) write_socket(sock, "%s ", args->argv[i]); write_socket(sock, "%s\n", args->argv[i]); diff --git a/src/net/listen.c b/src/net/listen.c index a960160afe9fb2d154181ddbf16026788769b490..59ffd99c68c90ee7726d4c9a059d7b7be1301da8 100644 --- a/src/net/listen.c +++ b/src/net/listen.c @@ -254,6 +254,8 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd) err = ! command_plt_import(srv->db, cmd.args, &srv->mpd_idle_events); else if (STR_MATCH(cmd.name, "__dump")) err = ! command_dump(srv->db, cmd.args, &srv->mpd_idle_events); + else if (STR_MATCH(cmd.name, "listplaylist")) + err = ! command_find(srv, c, cmd.args, cmd.cont, database_search_playlist_init); else if (STR_MATCH(cmd.name, "random")) err = !command_set_playback_option(srv, c, lkt_playback_option_random, cmd.args);