/*
- * Copyright (C) 1999-2006 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1999-2007 Andre Noll <maan@systemlinux.org>
*
- * 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 */
/** \endcond */
#include "server.cmdline.h"
#include "server.h"
+#include "vss.h"
#include "afs.h"
-#include "db.h"
#include <mysql/mysql.h>
#include <mysql/mysql_version.h>
+#include <regex.h>
#include "error.h"
#include "net.h"
#include "string.h"
+#include "user_list.h"
+#include "mysql_selector_command_list.h"
+#include "ipc.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 mysql_lock;
-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')"
}, {
}
};
-static int real_query(const char *query)
+/**
+ * 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 lockless_real_query(const char *query)
{
if (!mysql_ptr)
return -E_NOTCONN;
return 1;
}
+static int real_query(const char *query)
+{
+ int ret;
+
+ mutex_lock(mysql_lock);
+ ret = lockless_real_query(query);
+ mutex_unlock(mysql_lock);
+ return ret;
+}
+
/*
* 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(const char *query)
{
- void *result;
+ void *result = NULL;
- if (real_query(query) < 0)
- return NULL;
+ mutex_lock(mysql_lock);
+ if (lockless_real_query(query) < 0)
+ goto out;
result = mysql_store_result(mysql_ptr);
if (!result)
PARA_ERROR_LOG("%s", "store_result error\n");
+out:
+ mutex_unlock(mysql_lock);
return result;
}
/*
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));
}
/*
* 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 < 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;
/*
* 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 < 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;
/* 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;
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)
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;
/*
* 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 < 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;
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;
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)
+ 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;
}
/*
* 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 < 1)? "" : " where ",
- (argc < 1)? "" : argv[1],
- (argc < 1)? "" : " = '1'");
+ "data %s order by lastplayed", atts);
+ free(atts);
result = get_result(q);
free(q);
if (!result)
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;
}
/*
* 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 < 1)
+ if (argc < 2)
num = 10;
else
num = atoi(argv[1]);
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");
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;
"\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;
}
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)
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++) {
if (verbose)
atts = para_strcat(atts, is_set? "=\"1\"" : "=\"0\"");
}
- ret = 1;
out:
if (result2)
mysql_free_result(result2);
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
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);
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 ");
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);
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;
}
*/
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;
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);
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)
{
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';
+}
+
+static void refresh_selector_info(void)
+{
+ char *name = get_current_audio_file();
+ char *info;
-/* print database info */
-static int com_info(int fd, int argc, char *argv[])
+ 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 < 1) {
+ if (argc < 2) {
ret = -E_GET_AUDIO_FILE;
if (!(name = get_current_audio_file()))
goto out;
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);
{
char *q, *ebn;
void *result = NULL;
- long unsigned ret;
+ int ret;
MYSQL_ROW row;
if (!(ebn = escaped_basename(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;
}
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 */
/*
* 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;
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 != 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
+ * 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;
int i, ret;
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;
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 != 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;
/*
* 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;
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)
/*
* 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;
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);
/*
* 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;
int ret;
char *q, *name = NULL;
- if (argc < 1) {
+ if (argc < 2) {
ret = -E_GET_AUDIO_FILE;
name = get_current_audio_file();
} else {
}
/* 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 < 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;
}
/*
* 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 > 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);
free(q);
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;
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)
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)
{
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[])
{
- if (argc != 1)
- return -E_MYSQL_SYNTAX;
- return update_audio_file(argv[1]);
-}
-
-static void refresh_selector_info(void)
-{
- char *name = get_current_audio_file();
- char *info;
+ char *tmp;
+ int ret;
- if (!name)
- return;
- info = get_selector_info(name);
- free(name);
- mmd_lock();
- update_mmd(info);
- mmd_unlock();
- free(info);
+ if (argc != 2)
+ return -E_MYSQL_SYNTAX;
+ 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;
- if (argc)
- return -E_MYSQL_SYNTAX;
+ ret = -E_MYSQL_SYNTAX;
+ if (argc != 1)
+ goto out;
ret = -E_NORESULT;
if (!result)
goto out;
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
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;
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;
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);
}
/* 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;
void *result;
int ret;
- if (argc < 1) {
+ if (argc < 2) {
ret = -E_GET_STREAM;
name = get_current_stream();
} else {
}
/* 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");
- 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_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;
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);
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
*/
/*
* 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;
- 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)
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;
}
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;
- if (argc < 2)
+ if (argc < 3)
return -E_MYSQL_SYNTAX;
if (!(name = escaped_basename(argv[1])))
return -E_ESCAPE;
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])))
/*
* 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)
+ if (argc != 1)
return -E_MYSQL_SYNTAX;
ret = -E_NORESULT;
result = get_result("select data.name from data left join dir on "
goto out;
}
if (vrfy_mode) {
- send_va_buffer(fd, "found %i invalid entr%s\n", num_rows,
+ send_va_buffer(fd, "found %lli 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))) {
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);
/*
* 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;
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");
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);
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)
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);
free(list);
list = NULL;
success:
- if (query)
- free(query);
if (result)
mysql_free_result(result);
return list;
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);
/*
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;
}
}
/* 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;
ret = -E_MYSQL_INIT;
if (init_mysql_server() < 0 || !mysql_ptr)
goto out;
- conf.mysql_database_arg = para_strdup((argc < 1)?
- "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);
static void shutdown_connection(void)
{
+ int ret;
+
if (mysql_ptr) {
PARA_NOTICE_LOG("%s", "shutting down mysql connection\n");
mysql_close(mysql_ptr);
mysql_ptr = NULL;
}
+ if (mysql_lock) {
+ ret = mutex_destroy(mysql_lock);
+ if (ret < 0)
+ PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
+ mysql_lock = 0;
+ }
}
/**
* 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
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;
+ ret = mutex_new();
+ if (ret < 0)
+ return ret;
+ mysql_lock = ret;
ret = init_mysql_server();
if (ret < 0)
PARA_WARNING_LOG("%s\n", PARA_STRERROR(-ret));