add documentation for struct osx_buffer
[paraslash.git] / mysql_selector.c
index 85d16ee..5127364 100644 (file)
 #include "net.h"
 #include "string.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 **);
@@ -93,7 +91,7 @@ static struct server_command cmds[] = {
 
 "\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 database tool for the very first time.\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"
 
@@ -212,7 +210,7 @@ static struct server_command cmds[] = {
 .help =
 
 "\tIf pattern was not given, print list of all audio files known\n"
-"\tto the mysql database tool. See the documentation of mysql\n"
+"\tto the mysql selector. See the documentation of mysql\n"
 "\tfor the definition of LIKE patterns.\n"
 
 },
@@ -600,7 +598,7 @@ static struct para_macro macro_list[] = {
        }
 };
 
-static int real_query(char *query)
+static int real_query(const char *query)
 {
        if (!mysql_ptr)
                return -E_NOTCONN;
@@ -617,7 +615,7 @@ static int real_query(char *query)
  * Use open connection given by mysql_ptr to query server. Returns a
  * result pointer on succes and NULL on errors
  */
-static struct MYSQL_RES *get_result(char *query)
+static struct MYSQL_RES *get_result(const char *query)
 {
        void *result;
 
@@ -664,7 +662,7 @@ out:
        return ret;
 }
 
-static char *escape_blob(char* old, int size)
+static char *escape_blob(const char* old, int size)
 {
        char *new;
 
@@ -675,7 +673,7 @@ static char *escape_blob(char* old, int size)
        return new;
 }
 
-static char *escape_str(char* old)
+static char *escape_str(const char* old)
 {
        return escape_blob(old, strlen(old));
 }
@@ -694,15 +692,19 @@ static char *escaped_basename(const char *name)
 /*
  * new attribute
  */
-static int com_na(__unused int fd, int argc, char *argv[])
+static int com_na(__a_unused int fd, int argc, char *argv[])
 {
-       char *q;
+       char *q, *tmp;
        int ret;
 
-       if (argc < 1)
+       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;
@@ -711,14 +713,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[])
+static int com_da(__a_unused int fd, int argc, char *argv[])
 {
-       char *q;
+       char *q, *tmp;
        int ret;
 
-       if (argc < 1)
+       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;
@@ -727,12 +733,12 @@ 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;
 
-       if (argc < 1)
+       if (argc < 2)
                return -E_MYSQL_SYNTAX;
        if (strlen(argv[1]) >= MAXLINE - 1)
                return -E_NAMETOOLONG;
@@ -747,7 +753,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)
@@ -756,17 +766,22 @@ 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;
 }
@@ -807,10 +822,15 @@ static int com_verb(int fd, int argc, char *argv[])
        void *result = NULL;
        int ret;
        unsigned int num_rows, num_fields;
+       char *tmp;
 
-       if (argc < 1)
+       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;
@@ -837,19 +857,19 @@ static void *get_all_attributes(void)
                mysql_free_result(result);
                return NULL;
        }
-        mysql_data_seek(result, 4); /* skip Lastplayed, Numplayed... */
+       mysql_data_seek(result, 4); /* skip Lastplayed, Numplayed... */
        return result;
 }
 
 /*
  * list all attributes
  */
-static int com_laa(int fd, int argc, __unused char *argv[])
+static int com_laa(int fd, int argc, __a_unused char *argv[])
 {
        void *result;
        int ret;
 
-       if (argc)
+       if (argc != 1)
                return -E_MYSQL_SYNTAX;
        result = get_all_attributes();
        if (!result)
@@ -865,15 +885,24 @@ static int com_laa(int fd, int argc, __unused char *argv[])
 static int com_hist(int fd, int argc, char *argv[]) {
        int ret;
        void *result = NULL;
-       char *q;
+       char *q, *atts;
        unsigned int num_rows;
 
+       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 < 1)? "" : " where ",
-               (argc < 1)? "" : argv[1], 
-               (argc < 1)? "" : " = '1'");
-        result = get_result(q);
+               "data %s order by lastplayed", atts);
+       free(atts);
+       result = get_result(q);
        free(q);
        if (!result)
                return -E_NORESULT;
@@ -894,7 +923,7 @@ static int com_last(int fd, int argc, char *argv[])
        char *q;
        int num, ret;
 
-       if (argc < 1)
+       if (argc < 2)
                num = 10;
        else
                num = atoi(argv[1]);
@@ -943,15 +972,19 @@ static int com_mbox(int fd, int argc, char *argv[])
                "\n\n\n"
                "') from data"
        );
-       if (argc >= 1) {
-               char *tmp = make_message("%s where name LIKE '%s'", query,
-                       argv[1]);
+       if (argc >= 2) {
+               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;
        }
        mysql_free_result(result);
        ret = -E_NORESULT;
-        result = get_result(query);
+       result = get_result(query);
        if (!result)
                goto out;
        ret = -E_EMPTY_RESULT;
@@ -998,7 +1031,7 @@ static char *get_atts(char *name, int verbose)
        num_fields = mysql_num_fields(result);
        if (num_fields < 5)
                goto out;
-        mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
+       mysql_data_seek(result2, 4); /* skip Lastplayed, Numplayed... */
        row = mysql_fetch_row(result);
        ret = -E_NOROW;
        if (!row)
@@ -1010,11 +1043,11 @@ static char *get_atts(char *name, int verbose)
                        goto out;
                if (atts && (verbose || is_set))
                        atts = para_strcat(atts, verbose? "," : " ");
-                if (is_set || verbose)
+               if (is_set || verbose)
                        atts = para_strcat(atts, row2[0]);
                if (verbose)
                        atts = para_strcat(atts, is_set? "=\"1\"" : "=\"0\"");
-        }
+       }
        ret = 1;
 out:
        if (result2)
@@ -1048,7 +1081,7 @@ static char *get_meta(char *name, int verbose)
                goto out;
        q = make_message(verbose? verbose_fmt : fmt, ebn);
        free(ebn);
-        result = get_result(q);
+       result = get_result(q);
        free(q);
        if (!result)
                goto out;
@@ -1106,6 +1139,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
@@ -1124,12 +1158,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);
@@ -1187,7 +1226,7 @@ static char *get_query(char *streamname, char *filename, int with_path)
                        free(tmp2);
                        continue;
                }
-               if (!strcmp(command, "score:"))
+               if (!score && !strcmp(command, "score:"))
                        score = s_a_r_list(macro_list, arg);
        }
        if (!score) {
@@ -1207,10 +1246,10 @@ static char *get_query(char *streamname, char *filename, int with_path)
                goto write_query;
        }
        select_clause = para_strdup(with_path?
-                "select concat(dir.dir, '/', dir.name) from data, dir "
-                "where dir.name = data.name "
-                :
-                "select name from data where name is not NULL");
+               "select concat(dir.dir, '/', dir.name) from data, dir "
+               "where dir.name = data.name "
+               :
+               "select name from data where name is not NULL");
        order = make_message("order by -(%s)", score);
        free(score);
        if (accept_opts && deny_opts) {
@@ -1248,7 +1287,7 @@ out:
  * This is called from server and from some commands. Name must not be NULL
  * Never returns NULL.
  */
-static char *get_dbinfo(char *name)
+static char *get_selector_info(char *name)
 {
        char *meta = NULL, *atts = NULL, *info, *dir = NULL, *query, *stream = NULL;
        void *result = NULL;
@@ -1261,7 +1300,7 @@ static char *get_dbinfo(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);
@@ -1307,7 +1346,7 @@ static int com_info(int fd, int argc, char *argv[])
        char *name = NULL, *meta = NULL, *atts = NULL, *dir = NULL;
        int ret, com_la = strcmp(argv[0], "info");
 
-       if (argc < 1) {
+       if (argc < 2) {
                ret = -E_GET_AUDIO_FILE;
                if (!(name = get_current_audio_file()))
                        goto out;
@@ -1328,25 +1367,17 @@ static int com_info(int fd, int argc, char *argv[])
                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;
 }
-static int change_stream(char *stream)
+
+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);
@@ -1382,7 +1413,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 */
@@ -1438,13 +1469,13 @@ 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;
-       if (argc < 1)
+       if (argc < 2)
                return -E_MYSQL_SYNTAX;
-       for (i = 1; i <= argc; i++) {
+       for (i = 1; i < argc; i++) {
                ret = remove_entry(argv[i]);
                if (ret < 0)
                        return ret;
@@ -1460,69 +1491,76 @@ static int com_rm_ne(__unused int fd, int argc, char *argv[])
 /*
  * mv: rename entry
  */
-static int com_mv(__unused int fd, int argc, char *argv[])
+static int com_mv(__a_unused int fd, int argc, char *argv[])
 {
        char *q, *dn, *ebn1 = NULL, *ebn2 = NULL, *edn = NULL;
        int ret;
 
-       if (argc != 2)
+       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
  */
-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;
        int i, ret;
-       char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
+       const char *field = strcmp(argv[0], "picass")? "numplayed" : "pic_id";
 
-       if (argc < 2)
+       if (argc < 3)
                return -E_MYSQL_SYNTAX;
        id = atol(argv[1]);
-       for (i = 2; i <= argc; i++) {
+       for (i = 2; i < argc; i++) {
                ebn = escaped_basename(argv[i]);
                if (!ebn)
                        return -E_ESCAPE;
@@ -1540,18 +1578,21 @@ static int com_set(__unused int fd, int argc, char *argv[])
 /*
  * picch: change entry's name in pics table
  */
-static int com_picch(__unused int fd, int argc, char *argv[])
+static int com_picch(__a_unused int fd, int argc, char *argv[])
 {
        int ret;
        long unsigned id;
-       char *q;
+       char *q, *tmp;
 
-       if (argc != 2)
+       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;
@@ -1560,14 +1601,14 @@ 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[])
+static int com_piclist(__a_unused int fd, int argc, __a_unused char *argv[])
 {
        void *result = NULL;
        MYSQL_ROW row;
        unsigned long *length;
        int ret;
 
-       if (argc)
+       if (argc != 1)
                return -E_MYSQL_SYNTAX;
        result = get_result("select id,name,pic from pics order by id");
        if (!result)
@@ -1596,9 +1637,9 @@ static int com_picdel(int fd, int argc, char *argv[])
        my_ulonglong aff;
        int i, ret;
 
-       if (argc < 1)
+       if (argc < 2)
                return -E_MYSQL_SYNTAX;
-       for (i = 1; i <= argc; i++) {
+       for (i = 1; i < argc; i++) {
                id = atol(argv[i]);
                q = make_message("delete from pics where id = %lu", id);
                ret = real_query(q);
@@ -1629,7 +1670,7 @@ static int com_pic(int fd, int argc, char *argv[])
        int ret;
        char *q, *name = NULL;
 
-       if (argc < 1) {
+       if (argc < 2) {
                ret = -E_GET_AUDIO_FILE;
                name = get_current_audio_file();
        } else {
@@ -1662,22 +1703,20 @@ out:
 }
 
 /* strdel */
-static int com_strdel(__unused int fd, int argc, char *argv[])
+static int com_strdel(__a_unused int fd, int argc, char *argv[])
 {
-       char *tmp;
-       int ret = -1;
+       char *q, *tmp;
+       int ret;
 
-       if (argc < 1)
+       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;
 }
 
@@ -1691,12 +1730,18 @@ static int com_ls(int fd, int argc, char *argv[])
        int ret;
        unsigned int num_rows;
 
-       if (argc > 0)
-               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);
+       result = get_result(q);
        free(q);
        if (!result)
                return -E_NORESULT;
@@ -1707,10 +1752,11 @@ static int com_ls(int fd, int argc, char *argv[])
        mysql_free_result(result);
        return ret;
 }
+
 /*
  * summary
  */
-static int com_summary(__unused int fd, int argc, __unused char *argv[])
+static int com_summary(__a_unused int fd, int argc, __a_unused char *argv[])
 {
        MYSQL_ROW row;
        MYSQL_ROW row2;
@@ -1719,7 +1765,7 @@ static int com_summary(__unused int fd, int argc, __unused char *argv[])
        const char *fmt = "select count(name) from data where %s='1'";
        int ret = -E_NORESULT;
 
-       if (argc)
+       if (argc != 1)
                return -E_MYSQL_SYNTAX;
        result = get_all_attributes();
        if (!result)
@@ -1761,7 +1807,7 @@ static int get_numplayed(char *name)
        char *buf = make_message(fmt, name);
        int ret = -E_NORESULT;
 
-        result = get_result(buf);
+       result = get_result(buf);
        free(buf);
        if (!result)
                goto out;
@@ -1799,42 +1845,50 @@ 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->dbinfo, info, MMD_INFO_SIZE - 1);
-       mmd->dbinfo[MMD_INFO_SIZE - 1] = '\0';
+       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)
 {
        char *info;
-       info = get_dbinfo(name);
+       info = get_selector_info(name);
        update_mmd(info);
        free(info);
        update_audio_file(name);
 }
 
-static int com_us(__unused int fd, int argc, char *argv[])
+static int com_us(__a_unused int fd, int argc, char *argv[])
 {
-       if (argc != 1)
+       char *tmp;
+       int ret;
+
+       if (argc != 2)
                return -E_MYSQL_SYNTAX;
-       return update_audio_file(argv[1]);
+       tmp = escape_str(argv[1]);
+       if (!tmp)
+               return -E_ESCAPE;
+       ret = update_audio_file(argv[1]);
+       free(tmp);
+       return ret;
 }
 
-static void refresh_mmd_dbinfo(void)
+static void refresh_selector_info(void)
 {
        char *name = get_current_audio_file();
        char *info;
 
        if (!name)
                return;
-       info = get_dbinfo(name);
+       info = get_selector_info(name);
        free(name);
        mmd_lock();
        update_mmd(info);
@@ -1843,7 +1897,7 @@ static void refresh_mmd_dbinfo(void)
 }
 
 /* select previous/next stream */
-static int com_ps(__unused int fd, int argc, char *argv[])
+static int com_ps(__a_unused int fd, int argc, char *argv[])
 {
        char *query, *stream = get_current_stream();
        void *result = get_result("select name from streams");
@@ -1851,7 +1905,7 @@ static int com_ps(__unused int fd, int argc, char *argv[])
        int match = -1, ret, i;
        unsigned int num_rows;
 
-       if (argc)
+       if (argc != 1)
                return -E_MYSQL_SYNTAX;
        ret = -E_NORESULT;
        if (!result)
@@ -1880,7 +1934,7 @@ static int com_ps(__unused int fd, int argc, char *argv[])
        else
                i = match < num_rows - 1? match + 1 : 0;
        ret = -E_NOROW;
-        mysql_data_seek(result, i);
+       mysql_data_seek(result, i);
        row = mysql_fetch_row(result);
        if (!row || !row[0])
                goto out;
@@ -1901,7 +1955,7 @@ static int com_ps(__unused int fd, int argc, char *argv[])
                "'current_stream'", row[0]);
        ret = real_query(query);
        free(query);
-       refresh_mmd_dbinfo();
+       refresh_selector_info();
 out:
        free(stream);
        if (result)
@@ -1910,16 +1964,16 @@ out:
 }
 
 /* streams */
-static int com_streams(int fd, int argc, __unused char *argv[])
+static int com_streams(int fd, int argc, __a_unused char *argv[])
 {
        unsigned int num_rows;
        int i, ret = -E_NORESULT;
        void *result;
        MYSQL_ROW row;
 
-       if (argc && strcmp(argv[1], "current_stream"))
+       if (argc > 1 && strcmp(argv[1], "current_stream"))
                return -E_MYSQL_SYNTAX;
-       if (argc) {
+       if (argc > 1) {
                char *cs = get_current_stream();
                ret = send_va_buffer(fd, "%s\n", cs);
                free(cs);
@@ -1955,7 +2009,7 @@ static int com_strq(int fd, int argc, char *argv[])
        void *result;
        int ret;
 
-       if (argc < 1) {
+       if (argc < 2) {
                ret = -E_GET_STREAM;
                name = get_current_stream();
        } else {
@@ -1987,29 +2041,35 @@ out:
 static int com_cs(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");
 
-       if (!argc) {
-               ret = -E_MYSQL_SYNTAX;
+       ret = -E_MYSQL_SYNTAX;
+       if (argc > 2)
+               goto out;
+       if (argc == 1) {
                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_mmd_dbinfo();
+               refresh_selector_info();
        }
        if (csp) {
                mmd_lock();
@@ -2021,6 +2081,7 @@ static int com_cs(int fd, int argc, char *argv[])
        ret = 1;
 out:
        free(old_stream);
+       free(stream);
        return ret;
 }
 
@@ -2035,19 +2096,26 @@ static int com_sl(int fd, int argc, char *argv[])
        char *query, *stream, *tmp;
        unsigned int num_rows, num;
 
-       if (argc < 1)
+       if (argc < 2)
                return -E_MYSQL_SYNTAX;
        num = atoi(argv[1]);
        if (!num)
                return -E_MYSQL_SYNTAX;
-       stream = (argc == 1)?  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);
@@ -2109,11 +2177,11 @@ static int com_sa(int fd, int argc, char *argv[])
        int i, ret;
        char *atts = NULL, *name;
 
-       if (argc < 1)
+       if (argc < 2)
                return -E_MYSQL_SYNTAX;
-       for (i = 1; i <= argc; i++) {
+       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)
@@ -2129,15 +2197,19 @@ 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);
        }
 no_more_atts:
        if (!atts)
                return -E_NOATTS;
-       if (i > argc) { /* no name given, use current af */
+       if (i >= argc) { /* no name given, use current af */
                ret = -E_GET_AUDIO_FILE;
                if (!(name = get_current_audio_file()))
                        goto out;
@@ -2148,8 +2220,9 @@ no_more_atts:
                for (; argv[i] && ret >= 0; i++)
                        ret = update_atts(fd, argv[i], atts);
        }
-       refresh_mmd_dbinfo();
+       refresh_selector_info();
 out:
+       free(atts);
        return ret;
 }
 
@@ -2161,7 +2234,7 @@ static int com_cam(int fd, int argc, char *argv[])
        char *name = NULL, *meta = NULL, *atts = NULL;
        int i, ret;
 
-       if (argc < 2)
+       if (argc < 3)
                return -E_MYSQL_SYNTAX;
        if (!(name = escaped_basename(argv[1])))
                return -E_ESCAPE;
@@ -2171,7 +2244,7 @@ static int com_cam(int fd, int argc, char *argv[])
        ret = -E_META;
        if (!(meta = get_meta(name, 0)))
                goto out;
-       for (i = 2; i <= argc; i++) {
+       for (i = 2; i < argc; i++) {
                char *ebn, *q;
                ret = -E_ESCAPE;
                if (!(ebn = escaped_basename(argv[i])))
@@ -2204,7 +2277,7 @@ out:
 /*
  * verify / clean
  */
-static int com_vrfy(int fd, int argc, __unused char *argv[])
+static int com_vrfy(int fd, int argc, __a_unused char *argv[])
 {
        char *query;
        int ret, vrfy_mode = strcmp(argv[0], "clean");
@@ -2213,7 +2286,7 @@ static int com_vrfy(int fd, int argc, __unused char *argv[])
        MYSQL_ROW row;
        char *escaped_name;
 
-       if (argc)
+       if (argc != 1)
                return -E_MYSQL_SYNTAX;
        ret = -E_NORESULT;
        result = get_result("select data.name from data left join dir on "
@@ -2260,7 +2333,6 @@ 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);
@@ -2270,7 +2342,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[])
+static int com_upd(int fd, int argc, __a_unused char *argv[])
 {
        char *tempname = NULL, *query = NULL;
        int ret, out_fd = -1, num = 0;
@@ -2278,7 +2350,7 @@ static int com_upd(int fd, int argc, __unused char *argv[])
        unsigned int num_rows;
        MYSQL_ROW row;
 
-       if (argc)
+       if (argc != 1)
                return -E_MYSQL_SYNTAX;
        out_file = NULL;
        tempname = para_strdup("/tmp/mysql.tmp.XXXXXX");
@@ -2305,7 +2377,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);
@@ -2323,12 +2395,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)
@@ -2357,11 +2434,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);
@@ -2383,8 +2461,6 @@ err_out:
        free(list);
        list = NULL;
 success:
-       if (query)
-               free(query);
        if (result)
                mysql_free_result(result);
        return list;
@@ -2427,7 +2503,7 @@ static int init_mysql_server(void)
 /* mmd lock must be held */
 static void write_msg2mmd(int success)
 {
-       sprintf(mmd->dbinfo, "dbinfo1:%s\ndbinfo2:mysql-%s\ndbinfo3:\n",
+       sprintf(mmd->selector_info, "dbinfo1:%s\ndbinfo2:mysql-%s\ndbinfo3:\n",
                success < 0? PARA_STRERROR(-success) :
                        "successfully connected to mysql server",
                success < 0? "" : mysql_get_server_info(mysql_ptr));
@@ -2436,17 +2512,9 @@ static void write_msg2mmd(int success)
 /* create database */
 static int com_cdb(int fd, int argc, char *argv[])
 {
-       char *query, *name;
+       char *query;
        int ret;
 
-       if (argc < 1)
-               name = "paraslash";
-       else {
-               ret = -E_NAMETOOLONG;
-               name = argv[1];
-               if (strlen(name) > MAXLINE)
-                       goto out;
-       }
        if (mysql_ptr) {
                PARA_INFO_LOG("%s", "closing database\n");
                mysql_close(mysql_ptr);
@@ -2456,14 +2524,21 @@ static int com_cdb(int fd, int argc, char *argv[])
        ret = -E_MYSQL_INIT;
        if (init_mysql_server() < 0 || !mysql_ptr)
                goto out;
-       query = make_message("create database %s", name);
+       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);
        if (ret < 0)
                goto out;
        /* reconnect with database just created */
        mysql_close(mysql_ptr);
-       conf.mysql_database_arg = para_strdup(name);
        ret = -E_MYSQL_INIT;
        if (init_mysql_server() < 0 || !mysql_ptr)
                goto out;
@@ -2494,7 +2569,8 @@ static int com_cdb(int fd, int argc, char *argv[])
        if (real_query("insert into streams (name, def) values "
                        "('current_stream', '(none)')") < 0)
                goto out;
-       ret = send_va_buffer(fd, "successfully created database %s\n", name);
+       ret = send_va_buffer(fd, "successfully created database %s\n",
+               conf.mysql_database_arg);
 out:
        return ret;
 }
@@ -2509,12 +2585,13 @@ static void shutdown_connection(void)
 }
 
 /**
- * the init function of the mysql-based database tool
+ * 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 dbinfo string.
+ * Connect to the mysql server and initialize the info string.
  *
- * \sa struct audio_file_selector, misc_meta_data::dbinfo, random_selector.c
+ * \sa struct audio_file_selector, misc_meta_data::selector_info,
+ * random_selector.c
  */
 int mysql_selector_init(struct audio_file_selector *db)
 {