diff --git a/src/parsers/smartpl_lexer.l b/src/parsers/smartpl_lexer.l index 3b6951fe..e423b5cf 100644 --- a/src/parsers/smartpl_lexer.l +++ b/src/parsers/smartpl_lexer.l @@ -50,6 +50,7 @@ %option case-insensitive +/* quoted \"(\\(.|\n)|[^\\"\n])*\" */ quoted \"[^\"\n]*[\"\n] yyyymmdd [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] diff --git a/src/parsers/smartpl_parser.y b/src/parsers/smartpl_parser.y index dcde356c..e4ebaea5 100644 --- a/src/parsers/smartpl_parser.y +++ b/src/parsers/smartpl_parser.y @@ -188,31 +188,9 @@ int smartpl_lex_parse(struct smartpl_result *result, const char *input); %code top { #ifndef DEBUG_PARSER_MOCK #include "db.h" +#include "misc.h" #else -enum data_kind { - DATA_KIND_FILE = 0, - DATA_KIND_HTTP = 1, - DATA_KIND_SPOTIFY = 2, - DATA_KIND_PIPE = 3, -}; - -enum media_kind { - MEDIA_KIND_MUSIC = 1, - MEDIA_KIND_MOVIE = 2, - MEDIA_KIND_PODCAST = 4, - MEDIA_KIND_AUDIOBOOK = 8, - MEDIA_KIND_MUSICVIDEO = 32, - MEDIA_KIND_TVSHOW = 64, -}; - -static char * db_escape_string(const char *str) -{ - char *new = strdup(str); - char *ptr; - while ((ptr = strpbrk(new, "\\'"))) - *ptr = 'X'; - return new; -} +#include "owntonefunctions.h" #endif } @@ -253,6 +231,27 @@ enum sql_append_type { static void sql_from_ast(struct smartpl_result *, struct result_part *, struct ast *); +// Escapes any '%' or '_' that might be in the string +static void sql_like_escape(char **value, char *escape_char) +{ + char *s = *value; + size_t len = strlen(s); + char *new; + + *escape_char = 0; + + // Fast path, nothing to escape + if (!strpbrk(s, "_%")) + return; + + len = 2 * len; // Enough for every char to be escaped + new = realloc(s, len); + safe_snreplace(new, len, "%", "\\%"); + safe_snreplace(new, len, "_", "\\_"); + *escape_char = '\\'; + *value = new; +} + static void sql_str_escape(char **value) { char *old = *value; @@ -285,6 +284,8 @@ static void sql_append(struct smartpl_result *result, struct result_part *part, static void sql_append_recursive(struct smartpl_result *result, struct result_part *part, struct ast *a, const char *op, const char *op_not, bool is_not, enum sql_append_type append_type) { + char escape_char; + switch (append_type) { case SQL_APPEND_OPERATOR: @@ -301,8 +302,11 @@ static void sql_append_recursive(struct smartpl_result *result, struct result_pa case SQL_APPEND_OPERATOR_LIKE: sql_from_ast(result, part, a->l); sql_append(result, part, " %s '%%", is_not ? op_not : op); + sql_like_escape((char **)(&a->r->data), &escape_char); sql_from_ast(result, part, a->r); sql_append(result, part, "%%'"); + if (escape_char) + sql_append(result, part, " ESCAPE '%c'", escape_char); break; case SQL_APPEND_FIELD: assert(a->l == NULL); diff --git a/src/smartpl_query.c b/src/smartpl_query.c index 4117f0e1..1f7566c2 100644 --- a/src/smartpl_query.c +++ b/src/smartpl_query.c @@ -90,6 +90,8 @@ smartpl_query_parse_string(struct smartpl *smartpl, const char *expression) { struct smartpl_result result; + DPRINTF(E_SPAM, L_SCAN, "Parse smartpl query input '%s'\n", expression); + if (smartpl_lex_parse(&result, expression) != 0) { DPRINTF(E_LOG, L_SCAN, "Could not parse '%s': %s\n", expression, result.errmsg); @@ -110,6 +112,9 @@ smartpl_query_parse_string(struct smartpl *smartpl, const char *expression) smartpl->order = safe_strdup(result.order); smartpl->limit = result.limit; + DPRINTF(E_SPAM, L_SCAN, "Parse smartpl query output '%s': WHERE %s HAVING %s ORDER BY %s LIMIT %d\n", + smartpl->title, smartpl->query_where, smartpl->having, smartpl->order, smartpl->limit); + return 0; }