From a4dbed1c3c7fb8b1b334aa62edcd8d673b3d18b2 Mon Sep 17 00:00:00 2001
From: Andre <maan@p133.(none)>
Date: Thu, 13 Apr 2006 02:42:51 +0200
Subject: [PATCH] mysql selector commands: escape argv[] when used in a query.

---
 error.h          |   1 +
 mysql_selector.c | 221 ++++++++++++++++++++++++++++++-----------------
 2 files changed, 142 insertions(+), 80 deletions(-)

diff --git a/error.h b/error.h
index d62781c4..478aec1f 100644
--- a/error.h
+++ b/error.h
@@ -240,6 +240,7 @@ extern const char **para_errlist[];
 	PARA_ERROR(GET_AUDIO_FILE, "can not get current audio file"), \
 	PARA_ERROR(GET_STREAM, "can not get current stream"), \
 	PARA_ERROR(NO_STREAM, "no such stream"), \
+	PARA_ERROR(AUDIO_FILE, "no such audio file"), \
 	PARA_ERROR(GET_QUERY, "can not get query for specified stream"), \
 	PARA_ERROR(TMPFILE, "error while writing temporary file"), \
 	PARA_ERROR(META, "can not get meta data"), \
diff --git a/mysql_selector.c b/mysql_selector.c
index 98db17dc..edd2c085 100644
--- a/mysql_selector.c
+++ b/mysql_selector.c
@@ -695,13 +695,17 @@ static char *escaped_basename(const char *name)
  */
 static int com_na(__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;
@@ -712,12 +716,16 @@ static int com_na(__unused int fd, int argc, char *argv[])
  */
 static int com_da(__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 +734,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 +754,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,17 +767,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;
 }
@@ -806,10 +823,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 < 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;
@@ -864,14 +886,23 @@ 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 < 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)
@@ -943,8 +974,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;
 	}
@@ -1105,6 +1140,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,7 +1160,7 @@ static char *get_query(char *streamname, char *filename, int with_path)
 	if (!streamname)
 		tmp = get_current_stream();
 	else
-		tmp = para_strdup(streamname);
+		tmp = escape_str(streamname);
 	if (!strcmp(tmp, "(none)")) {
 		free(tmp);
 		if (filename) {
@@ -1260,7 +1296,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);
@@ -1327,25 +1363,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(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);
@@ -1381,7 +1409,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 */
@@ -1466,45 +1494,51 @@ static int com_mv(__unused int fd, int argc, char *argv[])
 
 	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 out;
+	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 */
 	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;
-
 }
 
 /*
@@ -1543,14 +1577,17 @@ static int com_picch(__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;
@@ -1663,20 +1700,18 @@ out:
 /* strdel */
 static int com_strdel(__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;
 }
 
@@ -1690,10 +1725,16 @@ static int com_ls(int fd, int argc, char *argv[])
 	int ret;
 	unsigned int num_rows;
 
-	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);
@@ -1706,6 +1747,7 @@ static int com_ls(int fd, int argc, char *argv[])
 	mysql_free_result(result);
 	return ret;
 }
+
 /*
  * summary
  */
@@ -1821,9 +1863,17 @@ static void update_audio_file_server_handler(char *name)
 
 static int com_us(__unused int fd, int argc, char *argv[])
 {
+	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_selector_info(void)
@@ -1986,7 +2036,7 @@ 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");
 
@@ -1998,14 +2048,18 @@ static int com_cs(int fd, int argc, char *argv[])
 		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();
@@ -2020,6 +2074,7 @@ static int com_cs(int fd, int argc, char *argv[])
 	ret = 1;
 out:
 	free(old_stream);
+	free(stream);
 	return ret;
 }
 
@@ -2039,7 +2094,7 @@ 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]);
+	stream = (argc == 2)?  get_current_stream() : escape_str(argv[2]);
 	tmp = get_query(stream, NULL, 0);
 	query = make_message("%s limit %d", tmp, num);
 	free(tmp);
@@ -2112,7 +2167,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 +2183,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);
 	}
@@ -2447,8 +2506,10 @@ 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
+		conf.mysql_database_arg = escape_str(argv[1]);
 	query = make_message("create database %s", conf.mysql_database_arg);
 	ret = real_query(query);
 	free(query);
-- 
2.39.5