X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=mysql_selector.c;h=8148fabe04310739a577b99add1783de94f0f14d;hp=98db17dc758762b1b16feb2a2cae3da4f38b8571;hb=f0cb2c2e49b4353a998d6fdf823972d2516c5dcf;hpb=69f0930489da9ace5af8aee104ad21d188aed387 diff --git a/mysql_selector.c b/mysql_selector.c index 98db17dc..8148fabe 100644 --- a/mysql_selector.c +++ b/mysql_selector.c @@ -1,19 +1,7 @@ /* - * Copyright (C) 1999-2006 Andre Noll + * Copyright (C) 1999-2007 Andre Noll * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file mysql_selector.c para_server's mysql-based audio file selector */ @@ -24,558 +12,35 @@ /** \endcond */ #include "server.cmdline.h" #include "server.h" +#include "vss.h" #include "afs.h" -#include "db.h" #include #include +#include #include "error.h" #include "net.h" #include "string.h" +#include "user_list.h" +#include "mysql_selector_command_list.h" -extern struct gengetopt_args_info conf; /** pointer to the shared memory area */ extern struct misc_meta_data *mmd; static void *mysql_ptr = NULL; -static int com_cam(int, int, char **); -static int com_cdb(int, int, char **); -static int com_cs(int, int, char **); -static int com_da(int, int, char **); -static int com_hist(int, int, char **); -static int com_info(int, int, char **); -static int com_laa(int, int, char **); -static int com_last(int, int, char **); -static int com_ls(int, int, char **); -static int com_mbox(int, int, char **); -static int com_mv(int, int, char **); -static int com_na(int, int, char **); -static int com_pic(int, int, char **); -static int com_picch(int, int, char **); -static int com_picdel(int, int, char **); -static int com_piclist(int, int, char **); -static int com_ps(int, int, char **); -static int com_rm_ne(int, int, char **); -static int com_sa(int, int, char **); -static int com_set(int, int, char **); -static int com_sl(int, int, char **); -static int com_stradd_picadd(int, int, char **); -static int com_streams(int, int, char **); -static int com_strdel(int, int, char **); -static int com_strq(int, int, char **); -static int com_summary(int, int, char **); -static int com_upd(int, int, char **); -static int com_us(int, int, char **); -static int com_verb(int, int, char **); -static int com_vrfy(int, int, char **); - -static struct server_command cmds[] = { -{ -.name = "cam", -.handler = com_cam, -.perms = DB_READ|DB_WRITE, -.description = "copy all metadata", -.synopsis = "cam source dest1 [dest2 ...]", -.help = - -"Copy attributes and other meta data from source file to destination\n" -"file(s). Useful for files that have been renamed.\n" - -}, -{ -.name = "cdb", -.handler = com_cdb, -.perms = DB_READ|DB_WRITE, -.description = "create database", -.synopsis = "cdb [name]", -.help = - -"\tCreate database name containing the initial columns for basic\n" -"\tinteroperation with server. This command has to be used only once\n" -"\twhen you use the mysql audio file selector for the very first time.\n" -"\n" -"\tThe optional name defaults to 'paraslash' if not given.\n" - -}, -{ -.name = "clean", -.handler = com_vrfy, -.perms = DB_READ | DB_WRITE, -.description = "nuke invalid entries in database", -.synopsis = "clean", -.help = - -"If the vrfy command shows you any invalid entries in your database,\n" -"you can get rid of them with clean. Always run 'upd' and 'vrfy'\n" -"before running this command. Use with caution!\n" - -}, -{ -.name = "cs", -.handler = com_cs, -.perms = AFS_WRITE | DB_READ | DB_WRITE, -.description = "change stream", -.synopsis = "cs [s]", -.help = - -"Selects stream s or prints current stream when s was not given.\n" - -}, -{ -.name = "csp", -.handler = com_cs, -.perms = AFS_WRITE | DB_READ, -.description = "change stream and play", -.synopsis = "csp s", -.help = - -"Select stream s and start playing. If this results in a stream-change,\n" -"skip rest of current audio file.\n" - -}, -{ -.name = "da", -.handler = com_da, -.perms = DB_READ | DB_WRITE, -.description = "drop attribute from database", -.synopsis = "da att", -.help = - -"Use with caution. All info on attribute att will be lost.\n" - -}, -{ -.name = "hist", -.handler = com_hist, -.perms = DB_READ, -.description = "print history", -.synopsis = "hist", -.help = - -"Print list of all audio files together with number of days since each\n" -"file was last played.\n" - -}, -{ -.name = "info", -.handler = com_info, -.perms = DB_READ, -.description = "print database info", -.synopsis = "info [af]", -.help = - -"print database informations for audio file af. Current audio file is\n" -"used if af is not given.\n" - -}, -{ -.name = "la", -.handler = com_info, -.perms = DB_READ, -.description = "list attributes", -.synopsis = "la [af]", -.help = - -"List attributes of audio file af or of current audio file when invoked\n" -"without arguments.\n" - -}, -{ -.name = "laa", -.handler = com_laa, -.perms = DB_READ, -.description = "list available attributes", -.synopsis = "laa", -.help = - -"What should I say more?\n" - -}, -{ -.name = "last", -.handler = com_last, -.perms = DB_READ, -.description = "print list of audio files, ordered by lastplayed time", -.synopsis = "last [n]", -.help = - -"The optional number n defaults to 10 if not specified.\n" - -}, -{ -.name = "ls", -.handler = com_ls, -.perms = DB_READ, -.description = "list all audio files that match a LIKE pattern", -.synopsis = "ls [pattern]", -.help = - -"\tIf pattern was not given, print list of all audio files known\n" -"\tto the mysql selector. See the documentation of mysql\n" -"\tfor the definition of LIKE patterns.\n" - -}, -{ -.name = "mbox", -.handler = com_mbox, -.perms = DB_READ, -.description = "dump audio file list in mbox format", -.synopsis = "mbox [p]", -.help = - -"\tDump list of audio files in mbox format (email) to stdout. If\n" -"\tthe optional pattern p is given, only those audio files,\n" -"\twhose basename match p are going to be included. Otherwise,\n" -"\tall files are selected.\n" -"\n" -"EXAMPLE\n" -"\tThe mbox command can be used together with your favorite\n" -"\tmailer (this example uses mutt) for browsing the audio file\n" -"\tcollection:\n" -"\n" -"\t\tpara_client mbox > ~/para_mbox\n" -"\n" -"\t\tmutt -F ~/.muttrc.para -f ~/para_mbox\n" -"\n" -"\tFor playlists, you can use mutt's powerful pattern matching\n" -"\tlanguage to select files. If you like to tag all files\n" -"\tcontaining the pattern 'foo', type 'T', then '~s foo'.\n" -"\n" -"\tWhen ready with the list, type ';|' (i.e., hit the semicolon\n" -"\tkey to apply the next mutt command to all tagged messages,\n" -"\tthen the pipe key) to pipe the selected \"mails\" to a\n" -"\tsuitable script which adds a paraslash stream where exactly\n" -"\tthese files are admissable or does whatever thou wilt.\n" - -}, -{ -.name = "mv", -.handler = com_mv, -.perms = DB_READ | DB_WRITE, -.description = "rename entry in database", -.synopsis = "mv oldname newname", -.help = - -"Rename oldname to newname. This updates the data table to reflect the\n" -"new name. All internal data (numplayed, lastplayed, picid,..) is kept.\n" -"If newname is a full path, the dir table is updated as well.\n" - -}, -{ -.name = "na", -.handler = com_na, -.perms = DB_READ | DB_WRITE, -.description = "add new attribute to database", -.synopsis = "na att", -.help = - -"This adds a column named att to your mysql database. att should only\n" -"contain letters and numbers, in paricular, '+' and '-' are not allowed.\n" - -}, -{ -.name = "ne", -.handler = com_rm_ne, -.perms = DB_READ | DB_WRITE, -.description = "add new database entries", -.synopsis = "ne file1 [file2 [...]]", -.help = - -"Add the given filename(s) to the database, where file1,... must\n" -"be full path names. This command might be much faster than 'upd'\n" -"if the number of given files is small.\n" - -}, -{ -.name = "ns", -.handler = com_ps, -.perms = AFS_WRITE | DB_READ | DB_WRITE, -.description = "change to next stream", -.synopsis = "ns", -.help = - -"Cycle forwards through stream list.\n" - -}, -{ -.name = "pic", -.handler = com_pic, -.perms = DB_READ, -.description = "get picture by name or by identifier", -.synopsis = "pic [name]", -.help = - -"\tDump jpg image that is associated to given audio file (current\n" -"\taudio file if not specified) to stdout. If name starts with\n" -"\t'#' it is interpreted as an identifier instead and the picture\n" -"\thaving that identifier is dumped to stdout.\n" -"\n" -"EXAMPLE\n" -"\n" -"\tpara_client pic '#123' > pic123.jpg\n" - -}, -{ -.name = "picadd", -.handler = com_stradd_picadd, -.perms = DB_READ | DB_WRITE, -.description = "add picture to database", -.synopsis = "picadd [picname]", -.help = - -"\tRead jpeg file from stdin and store it as picname in database.\n" -"\n" -"EXAMPLE\n" -"\n" -"\tpara_client picadd foo.jpg < foo.jpg\n" - -}, -{ -.name = "picass", -.handler = com_set, -.perms = DB_READ | DB_WRITE, -.description = "associate a picture to file(s)", -.synopsis = "picass pic_id file1 [file2...]", -.help = - -"Associate the picture given by pic_id to all given files.\n" - -}, -{ -.name = "picch", -.handler = com_picch, -.perms = DB_READ | DB_WRITE, -.description = "change name of picture", -.synopsis = "picch id new_name", -.help = - -"Asign new_name to picture with identifier id.\n" - -}, -{ -.name = "picdel", -.handler = com_picdel, -.perms = DB_READ | DB_WRITE, -.description = "delete picture from database", -.synopsis = "picdel id1 [id2...]", -.help = - -"Delete each given picture from database.\n" - -}, -{ -.name = "piclist", -.handler = com_piclist, -.perms = DB_READ, -.description = "print list of pictures", -.synopsis = "piclist", -.help = - -"Print id, name and length of each picture contained in the database.\n" - -}, -{ -.name = "ps", -.handler = com_ps, -.perms = AFS_WRITE | DB_READ | DB_WRITE, -.description = "change to previous stream", -.synopsis = "ps", -.help = - -"Cycle backwards through stream list.\n" - -}, -{ -.name = "rm", -.handler = com_rm_ne, -.perms = DB_READ | DB_WRITE, -.description = "remove entries from database", -.synopsis = "rm name1 [name2 [...]]", -.help = - -"Remove name1, name2, ... from the data table. Use with caution\n" - -}, -{ -.name = "sa", -.handler = com_sa, -.perms = DB_READ | DB_WRITE, -.description = "set/unset attributes", -.synopsis = "sa at1<'+' | '-'> [at2<'+' | '-'> ] [af1 ...]", -//.synopsis = "foo", -.help = - -"Set ('+') or unset ('-') attribute at1, at2 etc. for given list of\n" -"audio files. If no audio files were given the current audio file is\n" -"used. Example:\n" -"\n" -"sa rock+ punk+ classic- LZ__Waldsterben.mp3\n" -"\n" -"sets the 'rock' and the 'punk' attribute but unsets the 'classic'\n" -"attribute.\n" - -}, -{ -.name = "skip", -.handler = com_sl, -.perms = DB_READ | DB_WRITE, -.description = "skip subsequent audio files(s)", -.synopsis = "skip n [s]", -.help = - -"Skip the next n audio files of stream s. This is equivalent to the\n" -"command 'sl n s', followed by 'us name' for each name the output of sl.\n" - -}, -{ -.name = "sl", -.handler = com_sl, -.perms = DB_READ, -.description = "print score list", -.synopsis = "sl n [s]", -.help = - -"Print sorted list of maximal n lines. Each line is an admissible entry\n" -"with respect to stream s. The list is sorted by score-value which is\n" -"given by the definition of s. If s is not given, the current stream\n" -"is used. Example:\n" -"\n" -" sl 1\n" -"\n" -"shows you the audio file the server would select right now.\n" - -}, -{ -.name = "snp", -.handler = com_set, -.perms = DB_READ | DB_WRITE, -.description = "set numplayed", -.synopsis = "snp number af1 [af2 ...]", -.help = - -"Update the numplayed field in the data table for all given audio files.\n" - -}, -{ -.name = "stradd", -.handler = com_stradd_picadd, -.perms = DB_READ | DB_WRITE, -.description = "add stream", -.synopsis = "stradd s", -.help = - -"Add stream s to the list of available streams. The stream definition\n" -"for s is read from stdin and is then sent to para_server. Example:\n" -"\n" -" echo 'deny: NAME_LIKE(%Madonna%)' | para_client stradd no_madonna\n" -"\n" -"adds the new stream 'no_madonna' to the list of available streams. A given\n" -"audio file is admissible for this stream iff its basename does not contain the\n" -"string 'Madonna'.\n" - - -}, -{ -.name = "strdel", -.handler = com_strdel, -.perms = DB_READ | DB_WRITE, -.description = "delete stream", -.synopsis = "strdel s", -.help = - -"Remove stream s from database.\n" - -}, -{ -.name = "streams", -.handler = com_streams, -.perms = DB_READ, -.description = "list streams", -.synopsis = "streams", -.help = - -"Print list of available streams. Use 'cs' to switch to any of these.\n" - -}, -{ -.name = "strq", -.handler = com_strq, -.perms = DB_READ, -.description = "query stream definition", -.synopsis = "strq [s]", -.help = - -"Print definition of stream s to stdout. Use current stream if s was\n" -"not given.\n" - -}, -{ -.name = "summary", -.handler = com_summary, -.perms = DB_READ, -.description = "list attributes", -.synopsis = "summary", -.help = - -"\tPrint a list of attributes together with number of audio\n" -"\tfiles having that attribute set.\n" - -}, -{ -.name = "upd", -.handler = com_upd, -.perms = DB_READ | DB_WRITE, -.description = "update database", -.synopsis = "upd", -.help = - -"This command uses the --audio_file_dir option of para_server to locate\n" -"your audio files. New files are then added to the mysql database. Use\n" -"this command if you got new files or if you have moved some files\n" -"around.\n" - -}, -{ -.name = "us", -.handler = com_us, -.perms = DB_READ | DB_WRITE, -.description = "update lastplayed time", -.synopsis = "us name", -.help = - -"Update lastplayed time without actually playing the thing.\n" - -}, -{ -.name = "verb", -.handler = com_verb, -.perms = DB_READ | DB_WRITE, -.description = "send verbatim sql query", -.synopsis = "verb cmd", -.help = - -"Send cmd to mysql server. For expert/debugging only. Note that cmd\n" -"usually must be escaped. Use only if you know what you are doing!\n" - -}, -{ -.name = "vrfy", -.handler = com_vrfy, -.perms = DB_READ, -.description = "list invalid entries in database", -.synopsis = "vrfy", -.help = - -"Show what clean would delete. Run 'upd' before this command to make\n" -"sure your database is up to date.\n" - -}, -{ -.name = NULL, -} +/** + * contains name/replacement pairs used by s_a_r_list() + * + * \sa s_a_r() + */ +struct para_macro { + /** the name of the macro */ + const char *name; + /** the replacement text */ + const char *replacement; }; -static struct para_macro macro_list[] = { +static const struct para_macro mysql_macro_list[] = { { .name = "IS_N_SET", .replacement = "(data.%s != '1')" }, { @@ -599,6 +64,94 @@ static struct para_macro macro_list[] = { } }; +/** + * simple search and replace routine + * + * \param src source string + * \param macro_name the name of the macro + * \param replacement the replacement format string + * + * In \p src, replace each occurence of \p macro_name(arg) by the string + * determined by the \p replacement format string. \p replacement may (but + * needs not) contain a single string conversion specifier (%s) which gets + * replaced by \p arg. + * + * \return A string in which all matches in \p src are replaced, or \p NULL if + * an syntax error was encountered. Caller must free the result. + * + * \sa regcomp(3) + */ +__must_check __malloc static char *s_a_r(const char *src, const char* macro_name, + const char *replacement) +{ + regex_t preg; + size_t nmatch = 1; + regmatch_t pmatch[1]; + int eflags = 0; + char *dest = NULL; + const char *bufptr = src; + + if (!macro_name || !replacement || !src) + return para_strdup(src); + regcomp(&preg, macro_name, 0); + while (regexec(&preg, bufptr, nmatch, pmatch, eflags) + != REG_NOMATCH) { + char *tmp, *arg, *o_bracket, *c_bracket; + + o_bracket = strchr(bufptr + pmatch[0].rm_so, '('); + c_bracket = o_bracket? strchr(o_bracket, ')') : NULL; + if (!c_bracket) + goto out; + tmp = para_strdup(bufptr); + tmp[pmatch[0].rm_so] = '\0'; + dest = para_strcat(dest, tmp); + free(tmp); + + arg = para_strdup(o_bracket + 1); + arg[c_bracket - o_bracket - 1] = '\0'; + tmp = make_message(replacement, arg); + free(arg); + dest = para_strcat(dest, tmp); + free(tmp); + bufptr = c_bracket; + bufptr++; + } + dest = para_strcat(dest, bufptr); +// PARA_DEBUG_LOG("%s: returning %s\n", __func__, dest); +out: + regfree(&preg); + return dest; +} + +/** + * replace a string according to a list of macros + * + * \param macro_list the array containing a macro/replacement pairs. + * \param src the source string + * + * This function just calls s_a_r() for each element of \p macro_list. + * + * \return \p NULL if one of the underlying calls to \p s_a_r returned \p NULL. + * Otherwise the completely expanded version of \p src is returned. + */ +__must_check __malloc static char *s_a_r_list(const struct para_macro *macro_list, + char *src) +{ + const struct para_macro *mp = macro_list; + char *ret = NULL, *tmp = para_strdup(src); + + while (mp->name) { + ret = s_a_r(tmp, mp->name, mp->replacement); + free(tmp); + if (!ret) /* syntax error */ + return NULL; + tmp = ret; + mp++; + } + //PARA_DEBUG_LOG("%s: returning %s\n", __func__, dest); + return ret; +} + static int real_query(const char *query) { if (!mysql_ptr) @@ -663,18 +216,18 @@ out: return ret; } -static char *escape_blob(char* old, int size) +static char *escape_blob(const char* old, size_t size) { char *new; - if (!mysql_ptr || size < 0) + if (!mysql_ptr) return NULL; new = para_malloc(2 * size * sizeof(char) + 1); mysql_real_escape_string(mysql_ptr, new, old, size); return new; } -static char *escape_str(char* old) +static char *escape_str(const char* old) { return escape_blob(old, strlen(old)); } @@ -693,15 +246,19 @@ static char *escaped_basename(const char *name) /* * new attribute */ -static int com_na(__unused int fd, int argc, char *argv[]) +int com_na(__a_unused int fd, int argc, char *argv[]) { - char *q; + char *q, *tmp; int ret; if (argc < 2) return -E_MYSQL_SYNTAX; + tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; q = make_message("alter table data add %s char(1) " - "not null default 0", argv[1]); + "not null default 0", tmp); + free(tmp); ret = real_query(q); free(q); return ret; @@ -710,14 +267,18 @@ static int com_na(__unused int fd, int argc, char *argv[]) /* * delete attribute */ -static int com_da(__unused int fd, int argc, char *argv[]) +int com_da(__a_unused int fd, int argc, char *argv[]) { - char *q; + char *q, *tmp; int ret; if (argc < 2) return -E_MYSQL_SYNTAX; - q = make_message("alter table data drop %s", argv[1]); + tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + q = make_message("alter table data drop %s", tmp); + free(tmp); ret = real_query(q); free(q); return ret; @@ -726,7 +287,7 @@ static int com_da(__unused int fd, int argc, char *argv[]) /* stradd/pic_add */ static int com_stradd_picadd(int fd, int argc, char *argv[]) { - char *blob = NULL, *esc_blob = NULL, *q; + char *blob = NULL, *esc_blob = NULL, *q = NULL, *tmp = NULL; const char *fmt, *del_fmt; int ret, stradd = strcmp(argv[0], "picadd"); size_t size; @@ -746,7 +307,11 @@ static int com_stradd_picadd(int fd, int argc, char *argv[]) fmt = "insert into pics (name, pic) values ('%s','%s')"; del_fmt="delete from pics where pic='%s'"; } - q = make_message(del_fmt, argv[1]); + tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + q = make_message(del_fmt, tmp); + free(tmp); ret = real_query(q); free(q); if (ret < 0) @@ -755,27 +320,44 @@ static int com_stradd_picadd(int fd, int argc, char *argv[]) return ret; if ((ret = fd2buf(fd, &blob, size)) < 0) return ret; - PARA_DEBUG_LOG("length: %i\n", ret); size = ret; if (stradd) blob[size] = '\0'; - esc_blob = escape_blob(blob, ret); - free(blob); + ret = -E_ESCAPE; + esc_blob = escape_blob(blob, size); if (!esc_blob) - return -E_TOOBIG; - q = make_message(fmt, argv[1], esc_blob); - free(esc_blob); + goto out; + tmp = escape_str(argv[1]); + if (!tmp) + goto out; + q = make_message(fmt, tmp, esc_blob); ret = real_query(q); +out: + free(blob); + free(esc_blob); + free(tmp); free(q); return ret; } +/* stradd */ +int com_stradd(int fd, int argc, char *argv[]) +{ + return com_stradd_picadd(fd, argc, argv); +} + +/* pic_add */ +int com_picadd(int fd, int argc, char *argv[]) +{ + return com_stradd_picadd(fd, argc, argv); +} + /* * print results to fd */ static int print_results(int fd, void *result, - unsigned int top, unsigned int left, - unsigned int bottom, unsigned int right) + my_ulonglong top, my_ulonglong left, + my_ulonglong bottom, my_ulonglong right) { unsigned int i,j; int ret; @@ -801,15 +383,20 @@ static int print_results(int fd, void *result, /* * verbatim */ -static int com_verb(int fd, int argc, char *argv[]) +int com_verb(int fd, int argc, char *argv[]) { void *result = NULL; int ret; - unsigned int num_rows, num_fields; + my_ulonglong num_rows, num_fields, top = 0, left = 0; + char *tmp; if (argc < 2) return -E_MYSQL_SYNTAX; - result = get_result(argv[1]); + tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + result = get_result(tmp); + free(tmp); if (!result) /* return success, because it's ok to have no results */ return 1; @@ -817,7 +404,7 @@ static int com_verb(int fd, int argc, char *argv[]) num_rows = mysql_num_rows(result); ret = 1; if (num_fields && num_rows) - ret = print_results(fd, result, 0, 0, num_rows - 1, + ret = print_results(fd, result, top, left, num_rows - 1, num_fields - 1); mysql_free_result(result); return ret; @@ -836,24 +423,29 @@ static void *get_all_attributes(void) mysql_free_result(result); return NULL; } - mysql_data_seek(result, 4); /* skip Lastplayed, Numplayed... */ + mysql_data_seek(result, (my_ulonglong)4); /* skip Lastplayed, Numplayed... */ return result; } /* * list all attributes */ -static int com_laa(int fd, int argc, __unused char *argv[]) +int com_laa(int fd, int argc, __a_unused char *argv[]) { void *result; int ret; + my_ulonglong top = 0, left = 0, bottom, right = 0; if (argc != 1) return -E_MYSQL_SYNTAX; result = get_all_attributes(); if (!result) return -E_NOATTS; - ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 5, 0); + bottom = mysql_num_rows(result); + if (bottom < 5) + return -E_MYSQL_SYNTAX; + bottom -= 5; + ret = print_results(fd, result, top, left, bottom, right); mysql_free_result(result); return ret; } @@ -861,17 +453,27 @@ static int com_laa(int fd, int argc, __unused char *argv[]) /* * history */ -static int com_hist(int fd, int argc, char *argv[]) { +int com_hist(int fd, int argc, char *argv[]) +{ int ret; void *result = NULL; - char *q; - unsigned int num_rows; + char *q, *atts; + my_ulonglong num_rows, top = 0, left = 0, right = 1; + + if (argc > 3) + return -E_MYSQL_SYNTAX; + if (argc > 1) { + char *tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + atts = make_message("where %s = '1'", tmp); + free(tmp); + } else + atts = para_strdup(NULL); q = make_message("select name, to_days(now()) - to_days(lastplayed) from " - "data%s%s%s order by lastplayed", - (argc < 2)? "" : " where ", - (argc < 2)? "" : argv[1], - (argc < 2)? "" : " = '1'"); + "data %s order by lastplayed", atts); + free(atts); result = get_result(q); free(q); if (!result) @@ -879,7 +481,7 @@ static int com_hist(int fd, int argc, char *argv[]) { num_rows = mysql_num_rows(result); ret = 1; if (num_rows) - ret = print_results(fd, result, 0, 0, num_rows - 1, 1); + ret = print_results(fd, result, top, left, num_rows - 1, right); mysql_free_result(result); return ret; } @@ -887,11 +489,12 @@ static int com_hist(int fd, int argc, char *argv[]) { /* * get last num audio files */ -static int com_last(int fd, int argc, char *argv[]) +int com_last(int fd, int argc, char *argv[]) { void *result = NULL; char *q; int num, ret; + my_ulonglong top = 0, left = 0, right = 0; if (argc < 2) num = 10; @@ -905,17 +508,18 @@ static int com_last(int fd, int argc, char *argv[]) free(q); if (!result) return -E_NORESULT; - ret = print_results(fd, result, 0, 0, mysql_num_rows(result) - 1, 0); + ret = print_results(fd, result, top, left, mysql_num_rows(result) - 1, + right); mysql_free_result(result); return ret; } -static int com_mbox(int fd, int argc, char *argv[]) +int com_mbox(int fd, int argc, char *argv[]) { void *result; MYSQL_ROW row; int ret; - unsigned int num_rows, num_fields; + my_ulonglong num_rows, num_fields, top = 0, left = 0; char *query = para_strdup("select concat('From foo@localhost ', " "date_format(Lastplayed, '%a %b %e %T %Y'), " "'\nReceived: from\nTo: bar\n"); @@ -930,7 +534,7 @@ static int com_mbox(int fd, int argc, char *argv[]) if (!row[0]) goto out; - tmp = make_message("%s X-Attribute-%s: ', %s, '\n", query, + tmp = make_message("%sX-Attribute-%s: ', %s, '\n", query, row[0], row[0]); free(query); query = tmp; @@ -943,8 +547,12 @@ static int com_mbox(int fd, int argc, char *argv[]) "') from data" ); if (argc >= 2) { - char *tmp = make_message("%s where name LIKE '%s'", query, - argv[1]); + char *esc = escape_str(argv[1]), *tmp; + ret = -E_ESCAPE; + if (!esc) + goto out; + tmp = make_message("%s where name LIKE '%s'", query, esc); + free(esc); free(query); query = tmp; } @@ -958,7 +566,8 @@ static int com_mbox(int fd, int argc, char *argv[]) num_rows = mysql_num_rows(result); if (!num_fields || !num_rows) goto out; - ret = print_results(fd, result, 0, 0, num_rows - 1, num_fields - 1); + ret = print_results(fd, result, top, left, num_rows - 1, + num_fields - 1); out: free(query); if (result) @@ -966,40 +575,39 @@ out: return ret; } -/* get attributes by name. If verbose is not 0, get_a writes a string - * into atts of the form 'att1="0",att2="1"', which is used in com_cam - * for contructing a mysql update query. - * never returns NULL in *NON VERBOSE* mode +/* + * get attributes by name. If verbose is not 0, this function returns a string + * of the form 'att1="0",att2="1"'... which is used in com_cam() for + * constructing a mysql update query. Otherwise the space-separated list of all + * attributes which are set in the audio file given by name is returned. Never + * returns NULL in *NON VERBOSE* mode. */ static char *get_atts(char *name, int verbose) { char *atts = NULL, *buf, *ebn; void *result = NULL, *result2 = NULL; MYSQL_ROW row, row2; - int i, ret; - unsigned int num_fields; + int i; + my_ulonglong num_fields, offset = 4; /* skip Lastplayed, Numplayed... */ + - ret = -E_NOATTS; result2 = get_all_attributes(); if (!result2) goto out; - ret = -E_ESCAPE; - if (!(ebn = escaped_basename(name))) + ebn = escaped_basename(name); + if (!ebn) goto out; buf = make_message("select * from data where name='%s'", ebn); free(ebn); - ret = -E_NORESULT; result = get_result(buf); free(buf); if (!result) goto out; - ret = -E_EMPTY_RESULT; num_fields = mysql_num_fields(result); if (num_fields < 5) goto out; - mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */ + mysql_data_seek(result2, offset); row = mysql_fetch_row(result); - ret = -E_NOROW; if (!row) goto out; for (i = 4; i < num_fields; i++) { @@ -1014,7 +622,6 @@ static char *get_atts(char *name, int verbose) if (verbose) atts = para_strcat(atts, is_set? "=\"1\"" : "=\"0\""); } - ret = 1; out: if (result2) mysql_free_result(result2); @@ -1105,6 +712,7 @@ err_out: mysql_free_result(result); return para_strdup("(none)"); } + /* * Read stream definition of stream streamname and construct mysql * query. Return NULL on errors. If streamname is NULL, use current @@ -1123,12 +731,17 @@ static char *get_query(char *streamname, char *filename, int with_path) char *select_clause = NULL; if (!streamname) tmp = get_current_stream(); - else - tmp = para_strdup(streamname); + else { + tmp = escape_str(streamname); + if (!tmp) + return NULL; + } if (!strcmp(tmp, "(none)")) { free(tmp); if (filename) { char *ret, *ebn = escaped_basename(filename); + if (!ebn) + return NULL; ret = make_message("select to_days(now()) - " "to_days(lastplayed) from data " "where name = '%s'", ebn); @@ -1170,7 +783,7 @@ static char *get_query(char *streamname, char *filename, int with_path) continue; arg = line + n; if (!strcmp(command, "accept:")) { - char *tmp2 = s_a_r_list(macro_list, arg); + char *tmp2 = s_a_r_list(mysql_macro_list, arg); if (accept_opts) accept_opts = para_strcat( accept_opts, " or "); @@ -1179,7 +792,7 @@ static char *get_query(char *streamname, char *filename, int with_path) continue; } if (!strcmp(command, "deny:")) { - char *tmp2 = s_a_r_list(macro_list, arg); + char *tmp2 = s_a_r_list(mysql_macro_list, arg); if (deny_opts) deny_opts = para_strcat(deny_opts, " or "); deny_opts = para_strcat(deny_opts, tmp2); @@ -1187,10 +800,10 @@ static char *get_query(char *streamname, char *filename, int with_path) continue; } if (!score && !strcmp(command, "score:")) - score = s_a_r_list(macro_list, arg); + score = s_a_r_list(mysql_macro_list, arg); } if (!score) { - score = s_a_r_list(macro_list, conf.mysql_default_score_arg); + score = s_a_r_list(mysql_macro_list, conf.mysql_default_score_arg); if (!score) goto out; } @@ -1249,7 +862,7 @@ out: */ static char *get_selector_info(char *name) { - char *meta = NULL, *atts = NULL, *info, *dir = NULL, *query, *stream = NULL; + char *meta, *atts, *info, *dir, *query, *stream; void *result = NULL; MYSQL_ROW row = NULL; @@ -1260,7 +873,7 @@ static char *get_selector_info(char *name) atts = get_atts(name, 0); dir = get_dir(name); /* get score */ - query = get_query(stream, name, 0); + query = get_query(stream, name, 0); /* FIXME: pass stream == NULL instead? */ if (!query) goto write; result = get_result(query); @@ -1275,20 +888,15 @@ write: stream, meta, (result && row && row[0])? row[0] : "(no score)", atts); - if (dir) - free(dir); - if (meta) - free(meta); - if (atts) - free(atts); - if (stream) - free(stream); + free(dir); + free(meta); + free(atts); + free(stream); if (result) mysql_free_result(result); return info; } - /* might return NULL */ static char *get_current_audio_file(void) { @@ -1299,12 +907,34 @@ static char *get_current_audio_file(void) return name; } +/* If called as child, mmd_lock must be held */ +static void update_mmd(char *info) +{ + PARA_DEBUG_LOG("%s", "updating shared memory area\n"); + strncpy(mmd->selector_info, info, MMD_INFO_SIZE - 1); + mmd->selector_info[MMD_INFO_SIZE - 1] = '\0'; +} -/* print database info */ -static int com_info(int fd, int argc, char *argv[]) +static void refresh_selector_info(void) +{ + char *name = get_current_audio_file(); + char *info; + + if (!name) + return; + info = get_selector_info(name); + free(name); + mmd_lock(); + update_mmd(info); + mmd_unlock(); + free(info); +} + +/* list attributes / print database info */ +static int com_la_info(int fd, int argc, char *argv[]) { char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL; - int ret, com_la = strcmp(argv[0], "info"); + int ret, la = strcmp(argv[0], "info"); if (argc < 2) { ret = -E_GET_AUDIO_FILE; @@ -1321,31 +951,35 @@ static int com_info(int fd, int argc, char *argv[]) meta = get_meta(name, 1); atts = get_atts(name, 0); dir = get_dir(name); - if (com_la) + if (la) ret = send_va_buffer(fd, "%s\n", atts); else ret = send_va_buffer(fd, "dir: %s\n" "%s\n" "attributes: %s\n", dir? dir : "(not contained in table)", meta, atts); out: - if (meta) - free(meta); - if (atts) - free(atts); - if (dir) - free(dir); - if (name) - free(name); + free(meta); + free(atts); + free(dir); + free(name); return ret; } + +/* list attributes */ +int com_la(int fd, int argc, char *argv[]) +{ + return com_la_info(fd, argc, argv); +} + +/* print database info */ +int com_info(int fd, int argc, char *argv[]) +{ + return com_la_info(fd, argc, argv); +} + static int change_stream(const char *stream) { char *query; int ret; - /* try to insert if it does not exist (compatibility) */ -// query = make_message("insert into streams (name, def) values " -// "('current_stream', '%s')", stream); -// real_query(query); /* ignore return value */ -// free(query); query = make_message("update streams set def='%s' " "where name = 'current_stream'", stream); ret = real_query(query); @@ -1357,7 +991,7 @@ static int get_pic_id_by_name(char *name) { char *q, *ebn; void *result = NULL; - long unsigned ret; + int ret; MYSQL_ROW row; if (!(ebn = escaped_basename(name))) @@ -1371,7 +1005,7 @@ static int get_pic_id_by_name(char *name) row = mysql_fetch_row(result); ret = -E_NOROW; if (row && row[0]) - ret = atol(row[0]); + ret = atoi(row[0]); mysql_free_result(result); return ret; } @@ -1381,7 +1015,7 @@ static int remove_entry(const char *name) char *q, *ebn = escaped_basename(name); int ret = -E_ESCAPE; - if (!ebn || !*ebn) + if (!ebn) goto out; q = make_message("delete from data where name = '%s'", ebn); real_query(q); /* ignore errors */ @@ -1437,7 +1071,7 @@ out: /* * remove/add entries */ -static int com_rm_ne(__unused int fd, int argc, char *argv[]) +static int com_rm_ne(__a_unused int fd, int argc, char *argv[]) { int ne = !strcmp(argv[0], "ne"); int i, ret; @@ -1456,62 +1090,84 @@ static int com_rm_ne(__unused int fd, int argc, char *argv[]) return 1; } +/* + * rm + */ +int com_rm(int fd, int argc, char *argv[]) +{ + return com_rm_ne(fd, argc, argv); +} + +/* + * ne + */ +int com_ne(int fd, int argc, char *argv[]) +{ + return com_ne(fd, argc, argv); +} + /* * mv: rename entry */ -static int com_mv(__unused int fd, int argc, char *argv[]) +int com_mv(__a_unused int fd, int argc, char *argv[]) { char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL; int ret; if (argc != 3) return -E_MYSQL_SYNTAX; + ret = -E_ESCAPE; ebn1 = escaped_basename(argv[1]); ebn2 = escaped_basename(argv[2]); - dn = para_dirname(argv[2]); - edn = escape_str(dn); - free(dn); - ret = -E_ESCAPE; - if (!ebn1 || !ebn2) + if (!ebn1 || !ebn2 || !*ebn1 || !*ebn2) goto out; - remove_entry(ebn2); + ret = -E_MYSQL_SYNTAX; + if (!strcmp(ebn1, ebn2)) + goto update_dir; + remove_entry(argv[2]); /* no need to escape, ignore error */ q = make_message("update data set name = '%s' where name = '%s'", ebn2, ebn1); ret = real_query(q); free(q); if (ret < 0) goto out; + ret = -E_AUDIO_FILE; + if (!mysql_affected_rows(mysql_ptr)) + goto out; q = make_message("update dir set name = '%s' where name = '%s'", ebn2, ebn1); ret = real_query(q); free(q); if (ret < 0) goto out; - /* do not touch table dir, return success if argv[2] is no full path */ +update_dir: ret = 1; - if (!edn || !*edn) + dn = para_dirname(argv[2]); + if (!dn) + goto out; + ret = -E_ESCAPE; + edn = escape_str(dn); + free(dn); + if (!edn) + goto out; + ret = 1; + if (!*edn) goto out; q = make_message("update dir set dir = '%s' where name = '%s'", edn, ebn2); -// PARA_DEBUG_LOG("q: %s\n", q); ret = real_query(q); free(q); out: - if (ebn1) - free(ebn1); - if (ebn2) - free(ebn2); - if (edn) - free(edn); + free(edn); + free(ebn1); + free(ebn2); return ret; - } /* - * picass: associate pic to audio file - * snp: set numplayed + * set field */ -static int com_set(__unused int fd, int argc, char *argv[]) +static int com_set(__a_unused int fd, int argc, char *argv[]) { char *q, *ebn; long unsigned id; @@ -1536,21 +1192,43 @@ static int com_set(__unused int fd, int argc, char *argv[]) return 1; } +/* + * snp: set numplayed + */ +int com_picass(int fd, int argc, char *argv[]) +{ + return com_set(fd, argc, argv); +} + +/* + * snp: set numplayed + */ +int com_snp(int fd, int argc, char *argv[]) +{ + int ret = com_set(fd, argc, argv); + if (ret >= 0) + refresh_selector_info(); + return ret; +} + /* * picch: change entry's name in pics table */ -static int com_picch(__unused int fd, int argc, char *argv[]) +int com_picch(__a_unused int fd, int argc, char *argv[]) { int ret; long unsigned id; - char *q; + char *q, *tmp; if (argc != 3) return -E_MYSQL_SYNTAX; id = atol(argv[1]); - if (strlen(argv[2]) > MAXLINE) - return -E_NAMETOOLONG; - q = make_message("update pics set name = '%s' where id = %lu", argv[2], id); + ret = -E_ESCAPE; + tmp = escape_str(argv[2]); + if (!tmp) + return -E_ESCAPE; + q = make_message("update pics set name = '%s' where id = %lu", tmp, id); + free(tmp); ret = real_query(q); free(q); return ret; @@ -1559,7 +1237,7 @@ static int com_picch(__unused int fd, int argc, char *argv[]) /* * piclist: print list of pics in db */ -static int com_piclist(__unused int fd, int argc, __unused char *argv[]) +int com_piclist(__a_unused int fd, int argc, __a_unused char *argv[]) { void *result = NULL; MYSQL_ROW row; @@ -1588,7 +1266,7 @@ out: /* * picdel: delete picture from database */ -static int com_picdel(int fd, int argc, char *argv[]) +int com_picdel(int fd, int argc, char *argv[]) { char *q; long unsigned id; @@ -1620,7 +1298,7 @@ static int com_picdel(int fd, int argc, char *argv[]) /* * pic: get picture by name or by number */ -static int com_pic(int fd, int argc, char *argv[]) +int com_pic(int fd, int argc, char *argv[]) { void *result = NULL; MYSQL_ROW row; @@ -1661,39 +1339,43 @@ out: } /* strdel */ -static int com_strdel(__unused int fd, int argc, char *argv[]) +int com_strdel(__a_unused int fd, int argc, char *argv[]) { - char *tmp; - int ret = -1; + char *q, *tmp; + int ret; if (argc < 2) return -E_MYSQL_SYNTAX; - tmp = make_message("delete from streams where name='%s'", argv[1]); - ret = real_query(tmp); + tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + q = make_message("delete from streams where name='%s'", tmp); free(tmp); - if (ret < 0) - return ret; - tmp = get_current_stream(); - ret = 1; - if (strcmp(tmp, "(none)") && !strcmp(tmp, argv[1])) - ret = change_stream("(none)"); + ret = real_query(q); + free(q); return ret; } /* * ls */ -static int com_ls(int fd, int argc, char *argv[]) +int com_ls(int fd, int argc, char *argv[]) { char *q; void *result; int ret; - unsigned int num_rows; + my_ulonglong num_rows, top = 0, left = 0, right = 0; - if (argc > 1) - q = make_message("select name from data where name LIKE '%s'", - argv[1]); - else + if (argc > 2) + return -E_MYSQL_SYNTAX; + if (argc > 1) { + char *tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + q = make_message("select name from data where name like '%s'", + tmp); + free(tmp); + } else q = para_strdup("select name from data"); result = get_result(q); free(q); @@ -1702,14 +1384,15 @@ static int com_ls(int fd, int argc, char *argv[]) num_rows = mysql_num_rows(result); ret = 1; if (num_rows) - ret = print_results(fd, result, 0, 0, num_rows - 1, 0); + ret = print_results(fd, result, top, left, num_rows - 1, right); mysql_free_result(result); return ret; } + /* * summary */ -static int com_summary(__unused int fd, int argc, __unused char *argv[]) +int com_summary(__a_unused int fd, int argc, __a_unused char *argv[]) { MYSQL_ROW row; MYSQL_ROW row2; @@ -1798,17 +1481,9 @@ static int update_audio_file(char *name) ret = real_query(q); free(q); out: - if (ebn) - free(ebn); + free(ebn); return ret; } -/* If called as child, mmd_lock must be held */ -static void update_mmd(char *info) -{ - PARA_DEBUG_LOG("%s", "updating shared memory area\n"); - strncpy(mmd->selector_info, info, MMD_INFO_SIZE - 1); - mmd->selector_info[MMD_INFO_SIZE - 1] = '\0'; -} static void update_audio_file_server_handler(char *name) { @@ -1819,39 +1494,35 @@ static void update_audio_file_server_handler(char *name) update_audio_file(name); } -static int com_us(__unused int fd, int argc, char *argv[]) +int com_us(__a_unused int fd, int argc, char *argv[]) { + char *tmp; + int ret; + if (argc != 2) return -E_MYSQL_SYNTAX; - return update_audio_file(argv[1]); -} - -static void refresh_selector_info(void) -{ - char *name = get_current_audio_file(); - char *info; - - if (!name) - return; - info = get_selector_info(name); - free(name); - mmd_lock(); - update_mmd(info); - mmd_unlock(); - free(info); + tmp = escape_str(argv[1]); + if (!tmp) + return -E_ESCAPE; + ret = update_audio_file(argv[1]); + free(tmp); + if (ret >= 0) + refresh_selector_info(); + return ret; } -/* select previous/next stream */ -static int com_ps(__unused int fd, int argc, char *argv[]) +/* select previous / next stream */ +static int com_ps_ns(__a_unused int fd, int argc, char *argv[]) { char *query, *stream = get_current_stream(); void *result = get_result("select name from streams"); MYSQL_ROW row; - int match = -1, ret, i; - unsigned int num_rows; + int ret; + my_ulonglong num_rows, match, i; + ret = -E_MYSQL_SYNTAX; if (argc != 1) - return -E_MYSQL_SYNTAX; + goto out; ret = -E_NORESULT; if (!result) goto out; @@ -1866,14 +1537,13 @@ static int com_ps(__unused int fd, int argc, char *argv[]) goto out; if (!strcmp(row[0], "current_stream")) continue; - if (!strcmp(row[0], stream)) { - match = i; + if (!strcmp(row[0], stream)) break; - } } ret = -E_NO_STREAM; - if (match < 0) + if (i == num_rows) goto out; + match = i; if (!strcmp(argv[0], "ps")) i = match > 0? match - 1 : num_rows - 1; else @@ -1885,8 +1555,7 @@ static int com_ps(__unused int fd, int argc, char *argv[]) goto out; if (!strcmp(row[0], "current_stream")) { if (!strcmp(argv[0], "ps")) { - i = match - 2; - i = i < 0? i + num_rows : i; + i = match < 2? match + num_rows - 2 : match - 2; } else { i = match + 2; i = i > num_rows - 1? i - num_rows : i; @@ -1908,8 +1577,20 @@ out: return ret; } +/* select previous stream */ +int com_ps(int fd, int argc, char *argv[]) +{ + return com_ps_ns(fd, argc, argv); +} + +/* select next stream */ +int com_ns(int fd, int argc, char *argv[]) +{ + return com_ps_ns(fd, argc, argv); +} + /* streams */ -static int com_streams(int fd, int argc, __unused char *argv[]) +int com_streams(int fd, int argc, __a_unused char *argv[]) { unsigned int num_rows; int i, ret = -E_NORESULT; @@ -1947,7 +1628,7 @@ out: } /* query stream definition */ -static int com_strq(int fd, int argc, char *argv[]) +int com_strq(int fd, int argc, char *argv[]) { MYSQL_ROW row; char *query, *name; @@ -1983,50 +1664,67 @@ out: } /* change stream / change stream and play */ -static int com_cs(int fd, int argc, char *argv[]) +static int com_cs_csp(int fd, int argc, char *argv[]) { int ret, stream_change; - char *query; + char *query, *stream = NULL; char *old_stream = get_current_stream(); int csp = !strcmp(argv[0], "csp"); + ret = -E_MYSQL_SYNTAX; + if (argc > 2) + goto out; if (argc == 1) { - ret = -E_MYSQL_SYNTAX; if (csp) goto out; ret = send_va_buffer(fd, "%s\n", old_stream); goto out; } ret = -E_GET_QUERY; - query = get_query(argv[1], NULL, 0); /* test if stream is valid */ + /* test if stream is valid, no need to escape argv[1] */ + query = get_query(argv[1], NULL, 0); if (!query) goto out; free(query); /* stream is ok */ - stream_change = strcmp(argv[1], old_stream); + stream = escape_str(argv[1]); + if (!stream) + goto out; + stream_change = strcmp(stream, old_stream); if (stream_change) { - ret = change_stream(argv[1]); + ret = change_stream(stream); if (ret < 0) goto out; refresh_selector_info(); } if (csp) { mmd_lock(); - mmd->new_afs_status_flags |= AFS_PLAYING; + mmd->new_vss_status_flags |= VSS_PLAYING; if (stream_change) - mmd->new_afs_status_flags |= AFS_NEXT; + mmd->new_vss_status_flags |= VSS_NEXT; mmd_unlock(); } ret = 1; out: free(old_stream); + free(stream); return ret; } -/* - * sl/skip - */ -static int com_sl(int fd, int argc, char *argv[]) +/* change stream */ +int com_cs(int fd, int argc, char *argv[]) +{ + return com_cs_csp(fd, argc, argv); +} + +/* change stream and play */ +int com_csp(int fd, int argc, char *argv[]) +{ + return com_cs_csp(fd, argc, argv); +} + +/* score list / skip */ +static int com_sl_skip(int fd, int argc, char *argv[]) { void *result = NULL; MYSQL_ROW row; @@ -2039,14 +1737,21 @@ static int com_sl(int fd, int argc, char *argv[]) num = atoi(argv[1]); if (!num) return -E_MYSQL_SYNTAX; - stream = (argc == 2)? get_current_stream() : para_strdup(argv[2]); + if (argc == 2) { + stream = get_current_stream(); + if (!stream) + return -E_GET_STREAM; + } else { + stream = escape_str(argv[2]); + if (!stream) + return -E_ESCAPE; + } tmp = get_query(stream, NULL, 0); + free(stream); + if (!tmp) + return -E_GET_QUERY; query = make_message("%s limit %d", tmp, num); free(tmp); - ret = -E_GET_QUERY; - free(stream); - if (!query) - goto out; ret = -E_NORESULT; result = get_result(query); free(query); @@ -2071,6 +1776,18 @@ out: return ret; } +/* score list */ +int com_sl(int fd, int argc, char *argv[]) +{ + return com_sl_skip(fd, argc, argv); +} + +/* skip */ +int com_skip(int fd, int argc, char *argv[]) +{ + return com_sl_skip(fd, argc, argv); +} + /* * update attributes of name */ @@ -2103,7 +1820,7 @@ out: /* * set attributes */ -static int com_sa(int fd, int argc, char *argv[]) +int com_sa(int fd, int argc, char *argv[]) { int i, ret; char *atts = NULL, *name; @@ -2112,7 +1829,7 @@ static int com_sa(int fd, int argc, char *argv[]) return -E_MYSQL_SYNTAX; for (i = 1; i < argc; i++) { int unset = 0; - char *tmp, *p =argv[i]; + char *esc, *tmp, *p =argv[i]; int len = strlen(p); if (!len) @@ -2128,8 +1845,12 @@ static int com_sa(int fd, int argc, char *argv[]) goto no_more_atts; } p[len - 1] = '\0'; - tmp = make_message("%s%s='%s'", atts? "," : "", p, + esc = escape_str(p); + if (!esc) + return -E_ESCAPE; + tmp = make_message("%s%s='%s'", atts? "," : "", esc, unset? "0" : "1"); + free(esc); atts = para_strcat(atts, tmp); free(tmp); } @@ -2149,13 +1870,14 @@ no_more_atts: } refresh_selector_info(); out: + free(atts); return ret; } /* * copy attributes */ -static int com_cam(int fd, int argc, char *argv[]) +int com_cam(int fd, int argc, char *argv[]) { char *name = NULL, *meta = NULL, *atts = NULL; int i, ret; @@ -2203,14 +1925,14 @@ out: /* * verify / clean */ -static int com_vrfy(int fd, int argc, __unused char *argv[]) +static int com_vrfy_clean(int fd, int argc, __a_unused char *argv[]) { char *query; int ret, vrfy_mode = strcmp(argv[0], "clean"); void *result = NULL; - unsigned int num_rows; MYSQL_ROW row; char *escaped_name; + my_ulonglong num_rows, top = 0, left = 0, right = 0; if (argc != 1) return -E_MYSQL_SYNTAX; @@ -2227,7 +1949,7 @@ static int com_vrfy(int fd, int argc, __unused char *argv[]) if (vrfy_mode) { send_va_buffer(fd, "found %i invalid entr%s\n", num_rows, num_rows == 1? "y" : "ies"); - ret = print_results(fd, result, 0, 0, num_rows - 1, 0); + ret = print_results(fd, result, top, left, num_rows - 1, right); goto out; } while ((row = mysql_fetch_row(result))) { @@ -2253,13 +1975,28 @@ out: return ret; } +/* + * verify + */ +int com_vrfy(int fd, int argc, char **argv) +{ + return com_vrfy_clean(fd, argc, argv); +} + +/* + * clean + */ +int com_clean(int fd, int argc, char **argv) +{ + return com_vrfy_clean(fd, argc, argv); +} + static FILE *out_file; static int mysql_write_tmp_file(const char *dir, const char *name) { int ret = -E_TMPFILE; char *msg = make_message("%s\t%s\n", dir, name); - if (fputs(msg, out_file) != EOF) ret = 1; free(msg); @@ -2269,7 +2006,7 @@ static int mysql_write_tmp_file(const char *dir, const char *name) /* * update database */ -static int com_upd(int fd, int argc, __unused char *argv[]) +int com_upd(int fd, int argc, __a_unused char *argv[]) { char *tempname = NULL, *query = NULL; int ret, out_fd = -1, num = 0; @@ -2304,7 +2041,7 @@ static int com_upd(int fd, int argc, __unused char *argv[]) goto out; if ((ret = real_query("delete from dir")) < 0) goto out; - query = make_message("load data infile '%s' into table dir " + query = make_message("load data infile '%s' ignore into table dir " "fields terminated by '\t' lines terminated by '\n' " "(dir, name)", tempname); ret = real_query(query); @@ -2322,12 +2059,17 @@ static int com_upd(int fd, int argc, __unused char *argv[]) goto out; } while ((row = mysql_fetch_row(result))) { + char *erow; ret = -E_NOROW; if (!row[0]) goto out; send_va_buffer(fd, "new entry: %s\n", row[0]); + erow = escape_str(row[0]); + if (!erow) + goto out; query = make_message("insert into data (name, pic_id) values " - "('%s','%s')", row[0], "1"); + "('%s','%s')", erow, "1"); + free(erow); ret = real_query(query); free(query); if (ret < 0) @@ -2356,11 +2098,12 @@ static char **server_get_audio_file_list(unsigned int num) tmp = get_query(stream, NULL, 1); free(stream); + if (!tmp) + goto err_out; query = make_message("%s limit %d", tmp, num); free(tmp); - if (!query) - goto err_out; result = get_result(query); + free(query); if (!result) goto err_out; num_rows = mysql_num_rows(result); @@ -2382,8 +2125,6 @@ err_out: free(list); list = NULL; success: - if (query) - free(query); if (result) mysql_free_result(result); return list; @@ -2396,14 +2137,17 @@ success: static int init_mysql_server(void) { char *u = conf.mysql_user_arg? conf.mysql_user_arg : para_logname(); + unsigned int port; mysql_ptr = mysql_init(NULL); if (!mysql_ptr) { PARA_CRIT_LOG("%s", "mysql init error\n"); return -E_NOTCONN; } - PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg, - conf.mysql_port_arg); + if (conf.mysql_port_arg < 0) + return -E_MYSQL_SYNTAX; + port = conf.mysql_port_arg; + PARA_DEBUG_LOG("connecting: %s@%s:%d\n", u, conf.mysql_host_arg, port); if (!conf.mysql_user_arg) free(u); /* @@ -2415,7 +2159,7 @@ static int init_mysql_server(void) conf.mysql_user_arg, conf.mysql_passwd_arg, conf.mysql_database_arg, - conf.mysql_port_arg, NULL, 0))) { + port, NULL, 0))) { PARA_CRIT_LOG("%s", "connect error\n"); return -E_NOTCONN; } @@ -2433,7 +2177,7 @@ static void write_msg2mmd(int success) } /* create database */ -static int com_cdb(int fd, int argc, char *argv[]) +int com_cdb(int fd, int argc, char *argv[]) { char *query; int ret; @@ -2447,8 +2191,14 @@ static int com_cdb(int fd, int argc, char *argv[]) ret = -E_MYSQL_INIT; if (init_mysql_server() < 0 || !mysql_ptr) goto out; - conf.mysql_database_arg = para_strdup((argc < 2)? - "paraslash" : argv[1]); + if (argc < 2) + conf.mysql_database_arg = para_strdup("paraslash"); + else { + ret = -E_ESCAPE; + conf.mysql_database_arg = escape_str(argv[1]); + if (!conf.mysql_database_arg) + goto out; + } query = make_message("create database %s", conf.mysql_database_arg); ret = real_query(query); free(query); @@ -2504,8 +2254,15 @@ static void shutdown_connection(void) /** * the init function of the mysql-based audio file selector * - * Check the command line options and initialize all function pointers of \a db. - * Connect to the mysql server and initialize the info string. + * \param db pointer to the struct to initialize + * + * Check the command line options and initialize all function pointers of \a + * db. Connect to the mysql server and initialize the info string. + * + * \return This function returns success even if it could not connect + * to the mysql server. This is because the connect is expected to fail + * if there the paraslash database is not yet created. This gives the + * user a chance to send the "cdb" to create the database. * * \sa struct audio_file_selector, misc_meta_data::selector_info, * random_selector.c @@ -2519,7 +2276,7 @@ int mysql_selector_init(struct audio_file_selector *db) if (!conf.mysql_audio_file_dir_given) return -E_NO_AF_DIR; db->name = "mysql"; - db->cmd_list = cmds; + db->cmd_list = mysql_selector_cmds; db->get_audio_file_list = server_get_audio_file_list; db->update_audio_file = update_audio_file_server_handler; db->shutdown = shutdown_connection;