diff --git a/src/Makefile.am b/src/Makefile.am index 8b035c2d..50f5ae00 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,7 +47,7 @@ wavstreamer_SOURCES = wavstreamer.c mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \ webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \ mp3-scanner.h mp3-scanner.c rend-unix.h strcasestr.c strcasestr.h \ - strsep.c dynamic-art.c dynamic-art.h query.c query.h ssc.c ssc.h \ + strsep.c dynamic-art.c dynamic-art.h ssc.c ssc.h \ db-generic.c db-generic.h dispatch.c dispatch.h \ rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \ scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \ diff --git a/src/query.c b/src/query.c deleted file mode 100644 index 00bc5833..00000000 --- a/src/query.c +++ /dev/null @@ -1,657 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include - -#include "db-generic.h" -#include "err.h" -#include "query.h" - -static query_node_t* query_build(const char* query); -static void query_free(query_node_t* query); -static int query_build_clause(query_node_t *query, char **current, int *size); - -static const query_field_t *find_field(const char* name, - const query_field_t* fields); -// static int arith_query(query_node_t* query, void* target); -// static int string_query(query_node_t* query, void* target); - -static query_node_t *match_specifier(const char* query, - const char** cursor, - const query_field_t* fields); - -static query_node_t *group_match(const char* query, - const char** cursor, - const query_field_t* fields); - -static query_node_t *single_match(const char* query, - const char** cursor, - const query_field_t* fields); - -static int get_field_name(const char** pcursor, - const char* query, - char* name, - int len); - -static query_node_t *match_number(const query_field_t* field, - char not, char opcode, - const char** pcursor, - const char* query); - -static query_node_t *match_string(const query_field_t* field, - char not, char opcode, - const char** pcursor, - const char* query); - -char *query_unescape(const char* query); - - -static query_field_t song_fields[] = { - { qft_string, "dmap.itemname", "title" }, - { qft_i32, "dmap.itemid", "id" }, - { qft_string, "daap.songalbum", "album" }, - { qft_string, "daap.songartist", "artist" }, - { qft_i32, "daap.songbitrate", "bitrate" }, - { qft_string, "daap.songcomment", "comment" }, - { qft_i32, "daap.songcompilation", "compilation" }, - { qft_string, "daap.songcomposer", "composer" }, - { qft_i32, "daap.songdatakind", "data_kind" }, - { qft_string, "daap.songdataurl", "url" }, - { qft_i32, "daap.songdateadded", "time_added" }, - { qft_i32, "daap.songdatemodified","time_modified" }, - { qft_string, "daap.songdescription", "description" }, - { qft_i32, "daap.songdisccount", "total_discs" }, - { qft_i32, "daap.songdiscnumber", "disc" }, - { qft_string, "daap.songformat", "type" }, - { qft_string, "daap.songgenre", "genre" }, - { qft_i32, "daap.songsamplerate", "samplerate" }, - { qft_i32, "daap.songsize", "file_size" }, - // { qft_i32_const, "daap.songstarttime", 0 }, - { qft_i32, "daap.songstoptime", "song_length" }, - { qft_i32, "daap.songtime", "song_length" }, - { qft_i32, "daap.songtrackcount", "total_tracks" }, - { qft_i32, "daap.songtracknumber", "track" }, - { qft_i32, "daap.songyear", "year" }, - { 0, NULL, NULL } -}; - -char *query_sql_escape(char *term) { - int new_size=0; - char *src, *dst; - char *pnew; - - src=term; - while(*src) { - new_size++; - if(*src == '\'') - new_size++; - src++; - } - - src=term; - pnew=(char*)malloc(new_size+1); - if(!pnew) - return pnew; - - dst=pnew; - while(*src) { - if(*src == '\'') - *dst++='\''; - *dst++=*src++; - } - - *dst='\0'; - return pnew; -} - -char *query_build_sql(char *query) { - query_node_t *pquery; - char sql[2048]; - char *sqlptr=sql; - int size=sizeof(sql); - - DPRINTF(E_DBG,L_QRY,"Building query for %s\n",query); - pquery=query_build(query); - if(pquery) { - if(!query_build_clause(pquery,&sqlptr,&size)) { - query_free(pquery); - DPRINTF(E_DBG,L_QRY,"Built query: %s\n",sql); - return strdup(sql); - } - DPRINTF(E_DBG,L_QRY,"Error converting query to SQL\n"); - query_free(pquery); - } else { - DPRINTF(E_DBG,L_QRY,"Error building query\n"); - } - return NULL; -} - - -query_node_t* query_build(const char* query) { - query_node_t* left = 0; - char* raw = (char*)query; //query_unescape(query); - const char* cursor = raw; - query_node_t* right = 0; - query_type_t join; - - if(0 == (left = match_specifier(query, &cursor, song_fields))) - goto error; - - while(*cursor) - { - query_node_t* con; - - switch(*cursor) - { - case '+': - case ' ': join = qot_and; break; - case ',': join = qot_or; break; - default: - DPRINTF(E_LOG,L_QRY, "Illegal character '%c' (0%o) at index %d: %s\n", - *cursor, *cursor, cursor - raw, raw); - goto error; - } - - cursor++; - - if(0 == (right = match_specifier(raw, &cursor, song_fields))) - goto error; - - con = (query_node_t*) calloc(1, sizeof(*con)); - con->type = join; - con->left.node = left; - con->right.node = right; - - left = con; - } - - if(query != raw) - free(raw); - - return left; - - error: - if(left != 0) - query_free(left); - if(raw != query) - free(raw); - - return NULL; -} - -static query_node_t* match_specifier(const char* query, - const char** cursor, - const query_field_t* fields) { - switch(**cursor) { - case '\'': - return single_match(query, cursor, fields); - case '(': - return group_match(query, cursor, fields); - } - - DPRINTF(E_LOG,L_QRY,"Illegal character '%c' (0%o) at index %d: %s\n", - **cursor, **cursor, *cursor - query, query); - return NULL; -} - -static query_node_t* group_match(const char* query, - const char** pcursor, - const query_field_t* fields) -{ - query_node_t* left = 0; - query_node_t* right = 0; - query_node_t* join = 0; - query_type_t opcode; - const char* cursor = *pcursor; - - /* skip the opening ')' */ - ++cursor; - - if(0 == (left = single_match(query, &cursor, fields))) - return NULL; - - switch(*cursor) - { - case '+': - case ' ': - opcode = qot_and; - break; - - case ',': - opcode = qot_or; - break; - - default: - DPRINTF(E_LOG,L_QRY,"Illegal character '%c' (0%o) at index %d: %s\n", - *cursor, *cursor, cursor - query, query); - goto error; - } - - if(0 == (right = single_match(query, &cursor, fields))) - goto error; - - if(*cursor != ')') - { - DPRINTF(E_LOG,L_QRY,"Illegal character '%c' (0%o) at index %d: %s\n", - *cursor, *cursor, cursor - query, query); - goto error; - } - - *pcursor = cursor + 1; - - join = (query_node_t*) calloc(1, sizeof(*join)); - join->type = opcode; - join->left.node = left; - join->right.node = right; - - return join; - - error: - if(0 != left) - query_free(left); - if(0 != right) - query_free(right); - - return 0; -} - -static query_node_t* single_match(const char* query, - const char** pcursor, - const query_field_t* fields) -{ - char fname[64]; - const query_field_t* field; - char not = 0; - char op = 0; - query_node_t* node = 0; - - /* skip opening ' */ - (*pcursor)++; - - /* collect the field name */ - if(!get_field_name(pcursor, query, fname, sizeof(fname))) - return NULL; - - if(**pcursor == '!') - { - not = '!'; - ++(*pcursor); - } - - if(strchr(":+- ", **pcursor)) - { - op = **pcursor; - ++(*pcursor); - } - else - { - DPRINTF(E_LOG,L_QRY,"Illegal Operator: %c (0%o) at index %d: %s\n", - **pcursor, **pcursor, *pcursor - query, query); - return NULL; - } - - if(0 == (field = find_field(fname, fields))) - { - DPRINTF(E_LOG,L_QRY,"Unknown field: %s\n", fname); - return NULL; - } - - switch(field->type) - { - case qft_i32: - case qft_i64: - node = match_number(field, not, op, pcursor, query); - break; - - case qft_string: - node = match_string(field, not, op, pcursor, query); - break; - - default: - DPRINTF(E_LOG,L_QRY,"Invalid field type: %d\n", field->type); - break; - } - - if(**pcursor != '\'') - { - DPRINTF(E_LOG,L_QRY,"Illegal Character: %c (0%o) index %d: %s\n", - **pcursor, **pcursor, *pcursor - query, query); - query_free(node); - node = 0; - } - else - ++(*pcursor); - - return node; -} - -static int get_field_name(const char** pcursor, - const char* query, - char* name, - int len) { - const char* cursor = *pcursor; - - if(!isalpha(*cursor)) - return 0; - - while(isalpha(*cursor) || *cursor == '.') { - if(--len <= 0) { - DPRINTF(E_LOG,L_QRY,"token length exceeded at offset %d: %s\n", - cursor - query, query); - return 0; - } - - *name++ = *cursor++; - } - - *pcursor = cursor; - - *name = 0; - - return 1; -} - -static query_node_t* match_number(const query_field_t* field, - char not, char opcode, - const char** pcursor, - const char* query) -{ - query_node_t* node = (query_node_t*) calloc(1, sizeof(*node)); - - switch(opcode) - { - case ':': - node->type = not ? qot_ne : qot_eq; - break; - case '+': - case ' ': - node->type = not ? qot_le : qot_gt; - break; - case '-': - node->type = not ? qot_ge : qot_lt; - break; - } - - node->left.field = field; - - switch(field->type) - { - case qft_i32: - node->right.i32 = strtol(*pcursor, (char**) pcursor, 10); - break; - case qft_i64: - node->right.i64 = strtoll(*pcursor, (char**) pcursor, 10); - break; - default: - DPRINTF(E_LOG,L_QRY,"Bad field type -- invalid query\n"); - break; - } - - if(**pcursor != '\'') - { - DPRINTF(E_LOG,L_QRY,"Illegal char in number '%c' (0%o) at index %d: %s\n", - **pcursor, **pcursor, *pcursor - query, query); - free(node); - return 0; - } - - return node; -} - -static query_node_t* match_string(const query_field_t* field, - char not, char opcode, - const char** pcursor, - const char* query) -{ - char match[256]; - char* dst = match; - int left = sizeof(match); - const char* cursor = *pcursor; - query_type_t op = qot_is; - query_node_t* node; - - if(opcode != ':') { - DPRINTF(E_LOG,L_QRY,"Illegal operation on string: %c at index %d: %s\n", - opcode, cursor - query - 1); - return NULL; - } - - if(*cursor == '*') { - op = qot_ends; - cursor++; - } - - while(*cursor && *cursor != '\'') { - if(--left == 0) { - DPRINTF(E_LOG,L_QRY,"string too long at index %d: %s\n", - cursor - query, query); - return NULL; - } - - if(*cursor == '\\') { - switch(*++cursor) { - case '*': - case '\'': - case '\\': - *dst++ = *cursor++; - break; - default: - DPRINTF(E_LOG,L_QRY,"Illegal escape: %c (0%o) at index %d: %s\n", - *cursor, *cursor, cursor - query, query); - return NULL; - } - } else { - *dst++ = *cursor++; - } - } - - if(dst[-1] == '*') { - op = (op == qot_is) ? qot_begins : qot_contains; - dst--; - } - - *dst = 0; - - node = (query_node_t*) calloc(1, sizeof(*node)); - node->type = op; - node->left.field = field; - node->right.str = query_sql_escape(match); - - *pcursor = cursor; - - return node; -} - - -void query_free(query_node_t* query) { - if(0 != query) - { - switch(query->type) - { - // conjunction - case qot_and: - case qot_or: - query_free(query->left.node); - query_free(query->right.node); - break; - - // negation - case qot_not: - query_free(query->left.node); - break; - - // arithmetic - case qot_eq: - case qot_ne: - case qot_le: - case qot_lt: - case qot_ge: - case qot_gt: - break; - - // string - case qot_is: - case qot_begins: - case qot_ends: - case qot_contains: - free(query->right.str); - break; - - // constants - case qot_const: - break; - - default: - DPRINTF(E_LOG,L_QRY,"Illegal query type: %d\n", query->type); - break; - } - - free(query); - } -} - -static const query_field_t* find_field(const char* name, const query_field_t* fields) { - while(fields->name && strcasecmp(fields->name, name)) - fields++; - - if(fields->name == 0) { - DPRINTF(E_LOG,L_QRY,"Illegal query field: %s\n", name); - return NULL; - } - - return fields; -} - -int query_add_string(char **current, int *size, char *fmt, ...) { - va_list ap; - int write_size; - - va_start(ap, fmt); - write_size=vsnprintf(*current, *size, fmt, ap); - va_end(ap); - - if(write_size > *size) { - *size=0; - return 0; - } - - *size = *size - write_size; - return write_size; -} - -int query_build_clause(query_node_t *query, char **current, int *size) { - char* labels[] = { - "NOP", - "AND", - "OR", - "NOT", - "=", - "<>", - "<=", - "<", - ">=", - ">", - "=", - " (%s LIKE '%s%%') ", - " (%s LIKE '%%%s') ", - " (%s LIKE '%%%s%%') ", - "constant" - }; - - switch(query->type) { - case qot_and: - case qot_or: - if(*size) (*current) += query_add_string(current,size," ("); - if(query_build_clause(query->left.node,current,size)) return 1; - if(*size) (*current) += query_add_string(current,size," %s ", labels[query->type]); - if(query_build_clause(query->right.node,current,size)) return 1; - if(*size) (*current) += query_add_string(current,size,") "); - break; - - case qot_not: - if(*size) (*current) += query_add_string(current,size," (NOT "); - if(query_build_clause(query->left.node,current,size)) return 1; - if(*size) (*current) += query_add_string(current,size,") "); - break; - - case qot_eq: - case qot_ne: - case qot_le: - case qot_lt: - case qot_ge: - case qot_gt: - if(*size) (*current) += query_add_string(current,size," (%s %s ", - query->left.field->fieldname, - labels[query->type]); - if(query->left.field->type == qft_i32) { - if(*size) (*current) += query_add_string(current,size," %d) ",query->right.i32); - } else { - if(*size) (*current) += query_add_string(current,size," %ll) ",query->right.i64); - } - break; - - - case qot_is: - if(*size)(*current) += query_add_string(current,size," (%s='%s') ", - query->left.field->fieldname, - query->right.str); - break; - case qot_begins: - case qot_ends: - case qot_contains: - if(*size)(*current) += query_add_string(current,size,labels[query->type], - query->left.field->fieldname, - query->right.str); - break; - case qot_const: /* Not sure what this would be for */ - break; - default: - break; - } - - return 0; -} - -char* query_unescape(const char* src) { - - char* copy = malloc(strlen(src) + 1); - char* dst = copy; - - while(*src) - { - if(*src == '%') - { - int val = 0; - - if(*++src) - { - if(isdigit(*src)) - val = val * 16 + *src - '0'; - else - val = val * 16 + tolower(*src) - 'a' + 10; - } - - if(*++src) - { - if(isdigit(*src)) - val = val * 16 + *src - '0'; - else - val = val * 16 + tolower(*src) - 'a' + 10; - } - - src++; - *dst++ = val; - } - else - *dst++ = *src++; - } - - *dst++ = 0; - - return copy; -} diff --git a/src/query.h b/src/query.h deleted file mode 100644 index 637c636b..00000000 --- a/src/query.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef __query__ -#define __query__ - -typedef enum -{ - /* node opcodes */ - qot_empty, - - /* conjunctions */ - qot_and, - qot_or, - - /* negation */ - qot_not, - - /* arithmetic */ - qot_eq, - qot_ne, - qot_le, - qot_lt, - qot_ge, - qot_gt, - - /* string */ - qot_is, - qot_begins, - qot_ends, - qot_contains, - - /* constant opcode */ - qot_const, - - /* field types */ - qft_i32, - qft_i64, - qft_string - -} query_type_t; - -typedef struct query_field_ query_field_t; -struct query_field_ -{ - query_type_t type; - const char* name; - const char* fieldname; -}; - -typedef struct query_node_ query_node_t; -struct query_node_ -{ - query_type_t type; - union { - query_node_t* node; - const query_field_t* field; - int constant; - } left; - union { - query_node_t* node; - int i32; - long long i64; - char* str; - } right; -}; - -extern char *query_build_sql(char *query); - -#endif