mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-11-03 04:17:18 -05:00
Rework quoting around metadata
* Do not shell-quote strings in the metadata token expansion function * This fixes https://github.com/xiph/ezstream/issues/6 * Expand all tokens at the same time in a way that ensures metadata is not additional input to expansion * Simplify several functions and remove unused functionality
This commit is contained in:
parent
c21d47b8a9
commit
77f794a41e
195
src/ezstream.c
195
src/ezstream.c
@ -53,7 +53,7 @@ volatile sig_atomic_t queryMetadata;
|
||||
volatile sig_atomic_t quit;
|
||||
|
||||
void sig_handler(int);
|
||||
char * buildReencodeCommand(const char *, const char *, metadata_t);
|
||||
char * _build_reencode_cmd(const char *, const char *, metadata_t);
|
||||
metadata_t getMetadata(const char *);
|
||||
FILE * openResource(stream_t, const char *, int *, metadata_t *,
|
||||
int *, long *);
|
||||
@ -89,15 +89,19 @@ sig_handler(int sig)
|
||||
}
|
||||
|
||||
char *
|
||||
buildReencodeCommand(const char *extension, const char *fileName,
|
||||
_build_reencode_cmd(const char *extension, const char *fileName,
|
||||
metadata_t mdata)
|
||||
{
|
||||
cfg_decoder_t decoder;
|
||||
cfg_encoder_t encoder;
|
||||
char *dec_str, *enc_str;
|
||||
char *commandString;
|
||||
size_t commandStringLen;
|
||||
char *localTitle, *localArtist, *localMetaString, *localAlbum;
|
||||
cfg_decoder_t decoder;
|
||||
cfg_encoder_t encoder;
|
||||
char *artist, *album, *title, *songinfo, *tmp;
|
||||
char *filename_quoted;
|
||||
char *custom_songinfo;
|
||||
struct util_dict dicts[6];
|
||||
char *dec_str;
|
||||
char *enc_str;
|
||||
char *cmd_str;
|
||||
size_t cmd_str_size;
|
||||
|
||||
decoder = cfg_decoder_find(extension);
|
||||
if (!decoder) {
|
||||
@ -112,124 +116,89 @@ buildReencodeCommand(const char *extension, const char *fileName,
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
localTitle = util_utf82char(metadata_get_title(mdata), ICONV_REPLACE);
|
||||
localArtist = util_utf82char(metadata_get_artist(mdata), ICONV_REPLACE);
|
||||
localAlbum = util_utf82char(metadata_get_album(mdata), ICONV_REPLACE);
|
||||
localMetaString = util_utf82char(metadata_get_string(mdata),
|
||||
ICONV_REPLACE);
|
||||
tmp = util_utf82char(metadata_get_artist(mdata));
|
||||
artist = util_shellquote(tmp);
|
||||
xfree(tmp);
|
||||
|
||||
tmp = util_utf82char(metadata_get_album(mdata));
|
||||
album = util_shellquote(tmp);
|
||||
xfree(tmp);
|
||||
|
||||
tmp = util_utf82char(metadata_get_title(mdata));
|
||||
title = util_shellquote(tmp);
|
||||
xfree(tmp);
|
||||
|
||||
tmp = util_utf82char(metadata_get_string(mdata));
|
||||
songinfo = util_shellquote(tmp);
|
||||
xfree(tmp);
|
||||
|
||||
filename_quoted = util_shellquote(fileName);
|
||||
|
||||
dec_str = util_replacestring(cfg_decoder_get_program(decoder),
|
||||
PLACEHOLDER_TRACK, fileName);
|
||||
if (strstr(dec_str, PLACEHOLDER_ARTIST) != NULL) {
|
||||
char *tmpStr = util_replacestring(dec_str, PLACEHOLDER_ARTIST,
|
||||
localArtist);
|
||||
xfree(dec_str);
|
||||
dec_str = tmpStr;
|
||||
}
|
||||
if (strstr(dec_str, PLACEHOLDER_TITLE) != NULL) {
|
||||
char *tmpStr = util_replacestring(dec_str, PLACEHOLDER_TITLE,
|
||||
localTitle);
|
||||
xfree(dec_str);
|
||||
dec_str = tmpStr;
|
||||
}
|
||||
if (strstr(dec_str, PLACEHOLDER_ALBUM) != NULL) {
|
||||
char *tmpStr = util_replacestring(dec_str, PLACEHOLDER_ALBUM,
|
||||
localAlbum);
|
||||
xfree(dec_str);
|
||||
dec_str = tmpStr;
|
||||
}
|
||||
/*
|
||||
* if meta
|
||||
* if (prog && format)
|
||||
* metatoformat
|
||||
* if (prog && format)
|
||||
* metatoformat
|
||||
* else
|
||||
* if (!prog && title)
|
||||
* emptymeta
|
||||
* else
|
||||
* if (!prog && title)
|
||||
* emptymeta
|
||||
* else
|
||||
* replacemeta
|
||||
* replacemeta
|
||||
*/
|
||||
if (strstr(dec_str, PLACEHOLDER_METADATA) != NULL) {
|
||||
if (cfg_get_metadata_program() &&
|
||||
cfg_get_metadata_format_str()) {
|
||||
char *mdataString = metadata_format_string(mdata,
|
||||
cfg_get_metadata_format_str());
|
||||
char *tmpStr = util_replacestring(dec_str,
|
||||
PLACEHOLDER_METADATA, mdataString);
|
||||
xfree(dec_str);
|
||||
xfree(mdataString);
|
||||
dec_str = tmpStr;
|
||||
if (cfg_get_metadata_program() &&
|
||||
cfg_get_metadata_format_str()) {
|
||||
char *utf8, *unquoted;
|
||||
|
||||
utf8 = metadata_format_string(mdata,
|
||||
cfg_get_metadata_format_str());
|
||||
unquoted = util_utf82char(utf8);
|
||||
xfree(utf8);
|
||||
custom_songinfo = util_shellquote(unquoted);
|
||||
xfree(unquoted);
|
||||
} else {
|
||||
if (!cfg_get_metadata_program() &&
|
||||
strstr(cfg_decoder_get_program(decoder),
|
||||
PLACEHOLDER_TITLE) != NULL) {
|
||||
custom_songinfo = xstrdup("");
|
||||
} else {
|
||||
if (!cfg_get_metadata_program() &&
|
||||
strstr(dec_str, PLACEHOLDER_TITLE) != NULL) {
|
||||
char *tmpStr = util_replacestring(dec_str,
|
||||
PLACEHOLDER_METADATA, "");
|
||||
xfree(dec_str);
|
||||
dec_str = tmpStr;
|
||||
} else {
|
||||
char *tmpStr = util_replacestring(dec_str,
|
||||
PLACEHOLDER_METADATA, localMetaString);
|
||||
xfree(dec_str);
|
||||
dec_str = tmpStr;
|
||||
}
|
||||
custom_songinfo = xstrdup(songinfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfg_encoder_get_program(encoder))
|
||||
return (dec_str);
|
||||
memset(dicts, 0, sizeof(dicts));
|
||||
dicts[0].from = PLACEHOLDER_ARTIST;
|
||||
dicts[0].to = artist;
|
||||
dicts[1].from = PLACEHOLDER_ALBUM;
|
||||
dicts[1].to = album;
|
||||
dicts[2].from = PLACEHOLDER_TITLE;
|
||||
dicts[2].to = title;
|
||||
dicts[3].from = PLACEHOLDER_TRACK;
|
||||
dicts[3].to = filename_quoted;
|
||||
dicts[4].from = PLACEHOLDER_METADATA;
|
||||
dicts[4].to = custom_songinfo;
|
||||
|
||||
enc_str = util_replacestring(cfg_encoder_get_program(encoder),
|
||||
PLACEHOLDER_ARTIST, localArtist);
|
||||
if (strstr(enc_str, PLACEHOLDER_TITLE) != NULL) {
|
||||
char *tmpStr = util_replacestring(enc_str, PLACEHOLDER_TITLE,
|
||||
localTitle);
|
||||
xfree(enc_str);
|
||||
enc_str = tmpStr;
|
||||
}
|
||||
if (strstr(enc_str, PLACEHOLDER_ALBUM) != NULL) {
|
||||
char *tmpStr = util_replacestring(enc_str, PLACEHOLDER_ALBUM,
|
||||
localAlbum);
|
||||
xfree(enc_str);
|
||||
enc_str = tmpStr;
|
||||
}
|
||||
if (strstr(enc_str, PLACEHOLDER_METADATA) != NULL) {
|
||||
if (cfg_get_metadata_program() &&
|
||||
cfg_get_metadata_format_str()) {
|
||||
char *mdataString = metadata_format_string(mdata,
|
||||
cfg_get_metadata_format_str());
|
||||
char *tmpStr = util_replacestring(enc_str,
|
||||
PLACEHOLDER_METADATA, mdataString);
|
||||
xfree(enc_str);
|
||||
xfree(mdataString);
|
||||
enc_str = tmpStr;
|
||||
} else {
|
||||
if (!cfg_get_metadata_program() &&
|
||||
strstr(enc_str, PLACEHOLDER_TITLE) != NULL) {
|
||||
char *tmpStr = util_replacestring(enc_str,
|
||||
PLACEHOLDER_METADATA, "");
|
||||
xfree(enc_str);
|
||||
enc_str = tmpStr;
|
||||
} else {
|
||||
char *tmpStr = util_replacestring(enc_str,
|
||||
PLACEHOLDER_METADATA, localMetaString);
|
||||
xfree(enc_str);
|
||||
enc_str = tmpStr;
|
||||
}
|
||||
}
|
||||
dec_str = util_expand_words(cfg_decoder_get_program(decoder), dicts);
|
||||
|
||||
if (!cfg_get_metadata_program() &&
|
||||
strstr(cfg_encoder_get_program(encoder),
|
||||
PLACEHOLDER_TITLE) != NULL) {
|
||||
xfree(custom_songinfo);
|
||||
dicts[4].to = custom_songinfo = xstrdup("");
|
||||
}
|
||||
|
||||
commandStringLen = strlen(dec_str) + strlen(" | ") +
|
||||
strlen(enc_str) + 1;
|
||||
commandString = xcalloc(commandStringLen, sizeof(char));
|
||||
snprintf(commandString, commandStringLen, "%s | %s", dec_str,
|
||||
enc_str);
|
||||
enc_str = util_expand_words(cfg_encoder_get_program(encoder), dicts);
|
||||
|
||||
xfree(localTitle);
|
||||
xfree(localArtist);
|
||||
xfree(localMetaString);
|
||||
cmd_str_size = strlen(dec_str) + strlen(" | ") + strlen(enc_str) + 1;
|
||||
cmd_str = xcalloc(cmd_str_size, sizeof(char));
|
||||
snprintf(cmd_str, cmd_str_size, "%s | %s", dec_str, enc_str);
|
||||
xfree(dec_str);
|
||||
xfree(enc_str);
|
||||
|
||||
return (commandString);
|
||||
xfree(artist);
|
||||
xfree(album);
|
||||
xfree(title);
|
||||
xfree(filename_quoted);
|
||||
xfree(custom_songinfo);
|
||||
|
||||
return (cmd_str);
|
||||
}
|
||||
|
||||
metadata_t
|
||||
@ -325,7 +294,7 @@ openResource(stream_t stream, const char *fileName, int *popenFlag,
|
||||
if (cfg_get_stream_encoder()) {
|
||||
int stderr_fd = -1;
|
||||
|
||||
pCommandString = buildReencodeCommand(extension, fileName,
|
||||
pCommandString = _build_reencode_cmd(extension, fileName,
|
||||
mdata);
|
||||
if (mdata_p != NULL)
|
||||
*mdata_p = mdata;
|
||||
@ -588,7 +557,7 @@ streamFile(stream_t stream, const char *fileName)
|
||||
char *tmp, *metaData;
|
||||
|
||||
tmp = metadata_assemble_string(mdata);
|
||||
if ((metaData = util_utf82char(tmp, ICONV_REPLACE)) == NULL)
|
||||
if ((metaData = util_utf82char(tmp)) == NULL)
|
||||
metaData = xstrdup("(unknown title)");
|
||||
xfree(tmp);
|
||||
log_notice("streaming: %s (%s)", metaData,
|
||||
|
@ -176,7 +176,7 @@ metadata_get_name(const char *file)
|
||||
if (strlen(p1) == 0)
|
||||
name = xstrdup("[unknown]");
|
||||
else
|
||||
name = util_char2utf8(p1, ICONV_REPLACE);
|
||||
name = util_char2utf8(p1);
|
||||
|
||||
xfree(filename);
|
||||
return (name);
|
||||
@ -371,16 +371,14 @@ metadata_program_update(struct metadata *md, enum metadata_request md_req)
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (fgets(buf, (int)sizeof(buf), filep) == NULL) {
|
||||
if (ferror(filep))
|
||||
log_error("%s: output read error: %s", md->filename,
|
||||
strerror(errno));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (fgets(buf, (int)sizeof(buf), filep) == NULL &&
|
||||
ferror(filep)) {
|
||||
log_alert("%s: output read error: %s", md->filename,
|
||||
strerror(errno));
|
||||
pclose(filep);
|
||||
log_alert("program not (or no longer) usable: %s",
|
||||
md->filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pclose(filep);
|
||||
|
||||
if (strlen(buf) == sizeof(buf) - 1)
|
||||
@ -520,43 +518,22 @@ metadata_assemble_string(struct metadata *md)
|
||||
char *
|
||||
metadata_format_string(struct metadata *md, const char *format)
|
||||
{
|
||||
char *tmp, *str;
|
||||
struct util_dict dicts[6];
|
||||
|
||||
if (format == NULL)
|
||||
return (NULL);
|
||||
|
||||
str = xstrdup(format);
|
||||
memset(dicts, 0, sizeof(dicts));
|
||||
dicts[0].from = PLACEHOLDER_ARTIST;
|
||||
dicts[0].to = metadata_get_artist(md);
|
||||
dicts[1].from = PLACEHOLDER_ALBUM;
|
||||
dicts[1].to = metadata_get_album(md);
|
||||
dicts[2].from = PLACEHOLDER_TITLE;
|
||||
dicts[2].to = metadata_get_title(md);
|
||||
dicts[3].from = PLACEHOLDER_TRACK;
|
||||
dicts[3].to = metadata_get_filename(md);
|
||||
dicts[4].from = PLACEHOLDER_STRING;
|
||||
dicts[4].to = metadata_get_string(md);
|
||||
|
||||
if (strstr(format, PLACEHOLDER_ARTIST) != NULL) {
|
||||
tmp = util_replacestring(str, PLACEHOLDER_ARTIST,
|
||||
metadata_get_artist(md));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_TITLE) != NULL) {
|
||||
tmp = util_replacestring(str, PLACEHOLDER_TITLE,
|
||||
metadata_get_title(md));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_STRING) != NULL) {
|
||||
tmp = util_replacestring(str, PLACEHOLDER_STRING,
|
||||
metadata_get_string(md));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_TRACK) != NULL) {
|
||||
tmp = util_replacestring(str, PLACEHOLDER_TRACK,
|
||||
metadata_get_filename(md));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_ALBUM) != NULL) {
|
||||
tmp = util_replacestring(str, PLACEHOLDER_ALBUM,
|
||||
metadata_get_album(md));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return (str);
|
||||
return (util_expand_words(format, dicts));
|
||||
}
|
||||
|
87
src/util.c
87
src/util.c
@ -53,11 +53,11 @@ static FILE *pidfile_file;
|
||||
static pid_t pidfile_pid;
|
||||
static unsigned int pidfile_numlocks;
|
||||
|
||||
static char * _iconvert(const char *, const char *, const char *, int);
|
||||
static char * _iconvert(const char *, const char *, const char *);
|
||||
static void _cleanup_pidfile(void);
|
||||
|
||||
static char *
|
||||
_iconvert(const char *in_str, const char *from, const char *to, int mode)
|
||||
_iconvert(const char *in_str, const char *from, const char *to)
|
||||
{
|
||||
#ifdef HAVE_ICONV
|
||||
iconv_t cd;
|
||||
@ -73,26 +73,7 @@ _iconvert(const char *in_str, const char *from, const char *to, int mode)
|
||||
if (NULL == in_str)
|
||||
return (xstrdup(""));
|
||||
|
||||
switch (mode) {
|
||||
size_t siz;
|
||||
|
||||
case ICONV_TRANSLIT:
|
||||
siz = strlen(to) + strlen("//TRANSLIT") + 1;
|
||||
tocode = xcalloc(siz, sizeof(char));
|
||||
snprintf(tocode, siz, "%s//TRANSLIT", to);
|
||||
break;
|
||||
case ICONV_IGNORE:
|
||||
siz = strlen(to) + strlen("//IGNORE") + 1;
|
||||
tocode = xcalloc(siz, sizeof(char));
|
||||
snprintf(tocode, siz, "%s//IGNORE", to);
|
||||
break;
|
||||
case ICONV_REPLACE:
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
tocode = xstrdup(to);
|
||||
break;
|
||||
}
|
||||
|
||||
tocode = xstrdup(to);
|
||||
if ((cd = iconv_open(tocode, from)) == (iconv_t)-1 &&
|
||||
(cd = iconv_open("", from)) == (iconv_t)-1 &&
|
||||
(cd = iconv_open(tocode, "")) == (iconv_t)-1) {
|
||||
@ -147,7 +128,6 @@ _iconvert(const char *in_str, const char *from, const char *to, int mode)
|
||||
#else
|
||||
(void)from;
|
||||
(void)to;
|
||||
(void)mode;
|
||||
|
||||
if (NULL == in_str)
|
||||
return (xstrdup(""));
|
||||
@ -249,7 +229,7 @@ util_strrcasecmp(const char *s, const char *sub)
|
||||
}
|
||||
|
||||
char *
|
||||
util_char2utf8(const char *in_str, int mode)
|
||||
util_char2utf8(const char *in_str)
|
||||
{
|
||||
char *codeset;
|
||||
|
||||
@ -257,11 +237,11 @@ util_char2utf8(const char *in_str, int mode)
|
||||
codeset = nl_langinfo((nl_item)CODESET);
|
||||
setlocale(LC_CTYPE, "C");
|
||||
|
||||
return (_iconvert(in_str, codeset, "UTF-8", mode));
|
||||
return (_iconvert(in_str, codeset, "UTF-8"));
|
||||
}
|
||||
|
||||
char *
|
||||
util_utf82char(const char *in_str, int mode)
|
||||
util_utf82char(const char *in_str)
|
||||
{
|
||||
char *codeset;
|
||||
|
||||
@ -269,32 +249,51 @@ util_utf82char(const char *in_str, int mode)
|
||||
codeset = nl_langinfo((nl_item)CODESET);
|
||||
setlocale(LC_CTYPE, "C");
|
||||
|
||||
return (_iconvert(in_str, "UTF-8", codeset, mode));
|
||||
return (_iconvert(in_str, "UTF-8", codeset));
|
||||
}
|
||||
|
||||
char *
|
||||
util_replacestring(const char *source, const char *from, const char *to)
|
||||
util_expand_words(const char *in, struct util_dict dicts[])
|
||||
{
|
||||
char *to_quoted, *dest;
|
||||
size_t dest_size;
|
||||
const char *p1, *p2;
|
||||
size_t i;
|
||||
char *out;
|
||||
size_t out_size = strlen(in) + 1;
|
||||
|
||||
to_quoted = util_shellquote(to);
|
||||
dest_size = strlen(source) + strlen(to_quoted) + 1;
|
||||
dest = xcalloc(dest_size, sizeof(char));
|
||||
/* empty input string? */
|
||||
if (1 == out_size)
|
||||
return (NULL);
|
||||
|
||||
p1 = source;
|
||||
p2 = strstr(p1, from);
|
||||
if (p2 != NULL) {
|
||||
strncat(dest, p1, (size_t)(p2 - p1));
|
||||
strlcat(dest, to_quoted, dest_size);
|
||||
p1 = p2 + strlen(from);
|
||||
out = xstrdup(in);
|
||||
i = out_size - 1;
|
||||
while (i--) {
|
||||
struct util_dict *d = dicts;
|
||||
|
||||
while (d && d->from) {
|
||||
if (0 == strncmp(&out[i], d->from, strlen(d->from))) {
|
||||
char *buf, *tmp;
|
||||
size_t buf_len;
|
||||
|
||||
buf_len = strlen(&out[i]) + strlen(d->to)
|
||||
- strlen(d->from);
|
||||
buf = xcalloc(buf_len + 1, sizeof(*buf));
|
||||
snprintf(buf, buf_len + 1, "%s%s",
|
||||
d->to, &out[i + strlen(d->from)]);
|
||||
|
||||
out_size += buf_len;
|
||||
tmp = xcalloc(out_size, sizeof(*tmp));
|
||||
snprintf(tmp, i + 1, "%s", out);
|
||||
snprintf(tmp + i, out_size - i, "%s", buf);
|
||||
free(buf);
|
||||
free(out);
|
||||
out = tmp;
|
||||
|
||||
break;
|
||||
}
|
||||
d++;
|
||||
}
|
||||
}
|
||||
strlcat(dest, p1, dest_size);
|
||||
|
||||
xfree(to_quoted);
|
||||
|
||||
return (dest);
|
||||
return (out);
|
||||
}
|
||||
|
||||
#define SHELLQUOTE_INLEN_MAX 8191UL
|
||||
|
13
src/util.h
13
src/util.h
@ -16,16 +16,17 @@
|
||||
#ifndef __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
#define ICONV_REPLACE 0
|
||||
#define ICONV_TRANSLIT 1
|
||||
#define ICONV_IGNORE 2
|
||||
struct util_dict {
|
||||
const char *from;
|
||||
const char *to;
|
||||
};
|
||||
|
||||
int util_write_pid_file(const char *);
|
||||
int util_strrcmp(const char *, const char *);
|
||||
int util_strrcasecmp(const char *, const char *);
|
||||
char * util_char2utf8(const char *, int);
|
||||
char * util_utf82char(const char *, int);
|
||||
char * util_replacestring(const char *, const char *, const char *);
|
||||
char * util_char2utf8(const char *);
|
||||
char * util_utf82char(const char *);
|
||||
char * util_expand_words(const char *, struct util_dict[]);
|
||||
char * util_shellquote(const char *);
|
||||
int util_urlparse(const char *, char **, unsigned short *, char **);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user