1
0
mirror of https://gitlab.xiph.org/xiph/ezstream.git synced 2024-06-02 06:01:10 +00:00

ezstream.c/libshout untangle 1/2

This commit is contained in:
Moritz Grimm 2016-03-24 18:16:00 +01:00
parent 1fe0ab595c
commit a9728c54be
8 changed files with 581 additions and 411 deletions

View File

@ -75,20 +75,16 @@ typedef struct tag_ID3Tag {
} ID3Tag;
int urlParse(const char *, char **, unsigned short *, char **);
char * shellQuote(const char *);
char * replaceString(const char *, const char *, const char *);
char * buildReencodeCommand(const char *, const char *, metadata_t *);
char * getMetadataString(const char *, metadata_t *);
metadata_t * getMetadata(const char *);
int setMetadata(shout_t *, metadata_t *, char **);
FILE * openResource(shout_t *, const char *, int *, metadata_t **,
char * buildReencodeCommand(const char *, const char *, metadata_t);
metadata_t getMetadata(const char *);
FILE * openResource(stream_t, const char *, int *, metadata_t *,
int *, long *);
int reconnectServer(shout_t *, int);
const char * getTimeString(long);
int sendStream(shout_t *, FILE *, const char *, int, const char *,
int sendStream(stream_t, FILE *, const char *, int, const char *,
struct timespec *);
int streamFile(shout_t *, const char *);
int streamPlaylist(shout_t *);
int streamFile(stream_t, const char *);
int streamPlaylist(stream_t);
int ez_shutdown(int);
#ifdef HAVE_SIGNALS
@ -173,72 +169,9 @@ urlParse(const char *url, char **hostname, unsigned short *port,
return (1);
}
#define SHELLQUOTE_INLEN_MAX 8191UL
char *
shellQuote(const char *in)
{
char *out, *out_p;
size_t out_len;
const char *in_p;
out_len = (strlen(in) > SHELLQUOTE_INLEN_MAX)
? SHELLQUOTE_INLEN_MAX
: strlen(in);
out_len = out_len * 2 + 2;
out = xcalloc(out_len + 1, sizeof(char));
out_p = out;
in_p = in;
*out_p++ = '\'';
out_len--;
while (*in_p && out_len > 2) {
switch (*in_p) {
case '\'':
case '\\':
*out_p++ = '\\';
out_len--;
break;
default:
break;
}
*out_p++ = *in_p++;
out_len--;
}
*out_p++ = '\'';
return (out);
}
char *
replaceString(const char *source, const char *from, const char *to)
{
char *to_quoted, *dest;
size_t dest_size;
const char *p1, *p2;
to_quoted = shellQuote(to);
dest_size = strlen(source) + strlen(to_quoted) + 1;
dest = xcalloc(dest_size, sizeof(char));
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);
}
strlcat(dest, p1, dest_size);
xfree(to_quoted);
return (dest);
}
char *
buildReencodeCommand(const char *extension, const char *fileName,
metadata_t *mdata)
metadata_t mdata)
{
cfg_decoder_t decoder;
cfg_encoder_t encoder;
@ -292,7 +225,7 @@ buildReencodeCommand(const char *extension, const char *fileName,
if (strstr(dec_str, PLACEHOLDER_METADATA) != NULL) {
if (cfg_get_metadata_program() &&
cfg_get_metadata_format_str()) {
char *mdataString = getMetadataString(cfg_get_metadata_format_str(),
char *mdataString = stream_get_metadata_str(cfg_get_metadata_format_str(),
mdata);
char *tmpStr = replaceString(dec_str,
PLACEHOLDER_METADATA, mdataString);
@ -329,7 +262,7 @@ buildReencodeCommand(const char *extension, const char *fileName,
if (strstr(enc_str, PLACEHOLDER_METADATA) != NULL) {
if (cfg_get_metadata_program() &&
cfg_get_metadata_format_str()) {
char *mdataString = getMetadataString(cfg_get_metadata_format_str(),
char *mdataString = stream_get_metadata_str(cfg_get_metadata_format_str(),
mdata);
char *tmpStr = replaceString(enc_str,
PLACEHOLDER_METADATA, mdataString);
@ -367,48 +300,10 @@ buildReencodeCommand(const char *extension, const char *fileName,
return (commandString);
}
char *
getMetadataString(const char *format, metadata_t *mdata)
{
char *tmp, *str;
if (format == NULL)
return (NULL);
str = xstrdup(format);
if (strstr(format, PLACEHOLDER_ARTIST) != NULL) {
tmp = replaceString(str, PLACEHOLDER_ARTIST,
metadata_get_artist(mdata));
xfree(str);
str = tmp;
}
if (strstr(format, PLACEHOLDER_TITLE) != NULL) {
tmp = replaceString(str, PLACEHOLDER_TITLE,
metadata_get_title(mdata));
xfree(str);
str = tmp;
}
if (strstr(format, PLACEHOLDER_STRING) != NULL) {
tmp = replaceString(str, PLACEHOLDER_STRING,
metadata_get_string(mdata));
xfree(str);
str = tmp;
}
if (strstr(format, PLACEHOLDER_TRACK) != NULL) {
tmp = replaceString(str, PLACEHOLDER_TRACK,
metadata_get_filename(mdata));
xfree(str);
str = tmp;
}
return (str);
}
metadata_t *
metadata_t
getMetadata(const char *fileName)
{
metadata_t *mdata;
metadata_t mdata;
if (cfg_get_metadata_program()) {
if (NULL == (mdata = metadata_program(fileName,
@ -433,92 +328,15 @@ getMetadata(const char *fileName)
return (mdata);
}
int
setMetadata(shout_t *shout, metadata_t *mdata, char **mdata_copy)
{
shout_metadata_t *shout_mdata = NULL;
char *songInfo;
const char *artist, *title;
int ret = SHOUTERR_SUCCESS;
if (cfg_get_metadata_no_updates())
return (SHOUTERR_SUCCESS);
if (mdata == NULL)
return 1;
if ((shout_mdata = shout_metadata_new()) == NULL) {
log_syserr(ALERT, ENOMEM, "shout_metadata_new");
exit(1);
}
artist = metadata_get_artist(mdata);
title = metadata_get_title(mdata);
/*
* We can do this, because we know how libshout works. This adds
* "charset=UTF-8" to the HTTP metadata update request and has the
* desired effect of letting newer-than-2.3.1 versions of Icecast know
* which encoding we're using.
*/
if (shout_metadata_add(shout_mdata, "charset", "UTF-8") != SHOUTERR_SUCCESS) {
/* Assume SHOUTERR_MALLOC */
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
exit(1);
}
songInfo = getMetadataString(cfg_get_metadata_format_str(), mdata);
if (songInfo == NULL) {
if (artist[0] == '\0' && title[0] == '\0')
songInfo = xstrdup(metadata_get_string(mdata));
else
songInfo = metadata_assemble_string(mdata);
if (artist[0] != '\0' && title[0] != '\0') {
if (shout_metadata_add(shout_mdata, "artist", artist) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM,
"shout_metadata_add");
exit(1);
}
if (shout_metadata_add(shout_mdata, "title", title) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM,
"shout_metadata_add");
exit(1);
}
} else {
if (shout_metadata_add(shout_mdata, "song", songInfo) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM,
"shout_metadata_add");
exit(1);
}
}
} else if (shout_metadata_add(shout_mdata, "song", songInfo) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
exit(1);
}
if ((ret = shout_set_metadata(shout, shout_mdata)) != SHOUTERR_SUCCESS)
log_warning("shout_set_metadata: %s", shout_get_error(shout));
shout_metadata_free(shout_mdata);
if (ret == SHOUTERR_SUCCESS) {
if (mdata_copy != NULL && *mdata_copy == NULL)
*mdata_copy = xstrdup(songInfo);
}
xfree(songInfo);
return (ret);
}
FILE *
openResource(shout_t *shout, const char *fileName, int *popenFlag,
metadata_t **mdata_p, int *isStdin, long *songLen)
openResource(stream_t stream, const char *fileName, int *popenFlag,
metadata_t *mdata_p, int *isStdin, long *songLen)
{
FILE *filep = NULL;
char extension[25];
char *p = NULL;
char *pCommandString = NULL;
metadata_t *mdata;
metadata_t mdata;
if (mdata_p != NULL)
*mdata_p = NULL;
@ -529,7 +347,7 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag,
if (cfg_get_metadata_program()) {
if ((mdata = getMetadata(cfg_get_metadata_program())) == NULL)
return (NULL);
if (setMetadata(shout, mdata, NULL) != SHOUTERR_SUCCESS) {
if (stream_set_metadata(stream, mdata, NULL) != SHOUTERR_SUCCESS) {
metadata_free(&mdata);
return (NULL);
}
@ -699,15 +517,16 @@ getTimeString(long seconds)
}
int
sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
sendStream(stream_t stream, FILE *filepstream, const char *fileName,
int isStdin, const char *songLenStr, struct timespec *tv)
{
unsigned char buff[4096];
size_t bytes_read, total, oldTotal;
int ret;
double kbps = -1.0;
struct timespec timeStamp, *startTime = tv;
struct timespec callTime, currentTime;
unsigned char buff[4096];
size_t bytes_read, total, oldTotal;
int ret;
double kbps = -1.0;
struct timespec timeStamp, *startTime = tv;
struct timespec callTime, currentTime;
shout_t *shout = stream_get_shout(stream);
clock_gettime(CLOCK_MONOTONIC, &callTime);
@ -820,7 +639,7 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
}
int
streamFile(shout_t *shout, const char *fileName)
streamFile(stream_t stream, const char *fileName)
{
FILE *filepstream = NULL;
int popenFlag = 0;
@ -828,10 +647,10 @@ streamFile(shout_t *shout, const char *fileName)
int isStdin = 0;
int ret, retval = 0;
long songLen;
metadata_t *mdata;
metadata_t mdata;
struct timespec startTime;
if ((filepstream = openResource(shout, fileName, &popenFlag, &mdata, &isStdin, &songLen))
if ((filepstream = openResource(stream, fileName, &popenFlag, &mdata, &isStdin, &songLen))
== NULL) {
if (++resource_errors > 100) {
log_error("too many errors; giving up");
@ -854,7 +673,7 @@ streamFile(shout_t *shout, const char *fileName)
/* MP3 streams are special, so set the metadata explicitly: */
if (CFG_STREAM_MP3 == cfg_get_stream_format())
setMetadata(shout, mdata, NULL);
stream_set_metadata(stream, mdata, NULL);
metadata_free(&mdata);
} else if (isStdin)
@ -864,7 +683,7 @@ streamFile(shout_t *shout, const char *fileName)
songLenStr = xstrdup(getTimeString(songLen));
clock_gettime(CLOCK_MONOTONIC, &startTime);
do {
ret = sendStream(shout, filepstream, fileName, isStdin,
ret = sendStream(stream, filepstream, fileName, isStdin,
songLenStr, &startTime);
if (quit)
break;
@ -891,7 +710,7 @@ streamFile(shout_t *shout, const char *fileName)
continue;
if (cfg_get_metadata_program()) {
char *mdataStr = NULL;
metadata_t *prog_mdata;
metadata_t prog_mdata;
log_info("running metadata program: %s",
cfg_get_metadata_program());
@ -900,7 +719,7 @@ streamFile(shout_t *shout, const char *fileName)
ret = STREAM_DONE;
continue;
}
if (setMetadata(shout, prog_mdata, &mdataStr) != SHOUTERR_SUCCESS) {
if (stream_set_metadata(stream, prog_mdata, &mdataStr) != SHOUTERR_SUCCESS) {
retval = 0;
ret = STREAM_DONE;
continue;
@ -930,7 +749,7 @@ streamFile(shout_t *shout, const char *fileName)
}
int
streamPlaylist(shout_t *shout)
streamPlaylist(stream_t stream)
{
const char *song;
char lastSong[PATH_MAX];
@ -968,7 +787,7 @@ streamPlaylist(shout_t *shout)
while ((song = playlist_get_next(playlist)) != NULL) {
strlcpy(lastSong, song, sizeof(lastSong));
if (!streamFile(shout, song))
if (!streamFile(stream, song))
return (0);
if (quit)
break;
@ -1010,6 +829,7 @@ main(int argc, char *argv[])
{
int ret;
const char *errstr;
stream_t stream;
shout_t *shout;
extern char *optarg;
extern int optind;
@ -1034,8 +854,10 @@ main(int argc, char *argv[])
return (ez_shutdown(2));
}
if (NULL == (shout = stream_setup()))
stream = stream_get(STREAM_DEFAULT);
if (0 > stream_setup(stream))
return (ez_shutdown(1));
shout = stream_get_shout(stream);
#ifdef HAVE_SIGNALS
memset(&act, 0, sizeof(act));
@ -1078,9 +900,9 @@ main(int argc, char *argv[])
do {
if (playlistMode) {
cont = streamPlaylist(shout);
cont = streamPlaylist(stream);
} else {
cont = streamFile(shout,
cont = streamFile(stream,
cfg_get_media_filename());
}
if (quit)

View File

@ -50,10 +50,6 @@
#include "cfg.h"
#include "log.h"
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif /* !STDIN_FILENO */
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif /* !_PATH_DEVNULL */

View File

@ -73,21 +73,22 @@ struct ID3Tag {
char genre;
};
static metadata_t * metadata_create(const char *);
static void metadata_use_taglib(metadata_t *, FILE **);
static void metadata_use_self(metadata_t *, FILE **);
static void metadata_clean_md(metadata_t *);
static void metadata_get_extension(char *, size_t, const char *);
static char * metadata_get_name(const char *);
static void metadata_process_md(metadata_t *);
static void metadata_normalize_string(char **);
static struct metadata *
metadata_create(const char *);
static void metadata_use_taglib(struct metadata *, FILE **);
static void metadata_use_self(struct metadata *, FILE **);
static void metadata_clean_md(struct metadata *);
static void metadata_get_extension(char *, size_t, const char *);
static char * metadata_get_name(const char *);
static void metadata_process_md(struct metadata *);
static void metadata_normalize_string(char **);
static metadata_t *
static struct metadata *
metadata_create(const char *filename)
{
metadata_t *md;
metadata_t md;
md = xcalloc(1UL, sizeof(metadata_t));
md = xcalloc(1UL, sizeof(*md));
md->filename = xstrdup(filename);
md->songLen = -1;
@ -95,7 +96,7 @@ metadata_create(const char *filename)
}
static void
metadata_use_taglib(metadata_t *md, FILE **filep)
metadata_use_taglib(struct metadata *md, FILE **filep)
#ifdef HAVE_TAGLIB
{
TagLib_File *tf;
@ -156,7 +157,7 @@ metadata_use_taglib(metadata_t *md, FILE **filep)
#endif /* HAVE_TAGLIB */
static void
metadata_use_self(metadata_t *md, FILE **filep)
metadata_use_self(struct metadata *md, FILE **filep)
#ifdef HAVE_TAGLIB
{
(void)md;
@ -245,7 +246,7 @@ metadata_use_self(metadata_t *md, FILE **filep)
#endif /* HAVE_TAGLIB */
static void
metadata_clean_md(metadata_t *md)
metadata_clean_md(struct metadata *md)
{
if (md->string != NULL) {
xfree(md->string);
@ -299,7 +300,7 @@ metadata_get_name(const char *file)
}
static void
metadata_process_md(metadata_t *md)
metadata_process_md(struct metadata *md)
{
if (md->string == NULL)
md->string = metadata_assemble_string(md);
@ -342,10 +343,10 @@ metadata_normalize_string(char **s)
*s = xreallocarray(tmpstr, strlen(tmpstr) + 1, sizeof(char));
}
metadata_t *
struct metadata *
metadata_file(const char *filename, int normalize)
{
metadata_t *md;
struct metadata *md;
md = metadata_create(filename);
if (!metadata_file_update(md)) {
@ -358,10 +359,10 @@ metadata_file(const char *filename, int normalize)
return (md);
}
metadata_t *
struct metadata *
metadata_program(const char *program, int normalize)
{
metadata_t *md;
struct metadata *md;
struct stat st;
if (stat(program, &st) == -1) {
@ -386,9 +387,9 @@ metadata_program(const char *program, int normalize)
}
void
metadata_free(metadata_t **md_p)
metadata_free(struct metadata **md_p)
{
metadata_t *md;
struct metadata *md;
if (md_p == NULL || (md = *md_p) == NULL)
return;
@ -404,7 +405,7 @@ metadata_free(metadata_t **md_p)
int
metadata_file_update(metadata_t *md)
metadata_file_update(struct metadata *md)
{
FILE *filep;
@ -427,7 +428,7 @@ metadata_file_update(metadata_t *md)
}
int
metadata_program_update(metadata_t *md, enum metadata_request md_req)
metadata_program_update(struct metadata *md, enum metadata_request md_req)
{
FILE *filep;
char buf[METADATA_MAX + 1];
@ -534,14 +535,14 @@ metadata_program_update(metadata_t *md, enum metadata_request md_req)
}
const char *
metadata_get_string(metadata_t *md)
metadata_get_string(struct metadata *md)
{
assert(md->string);
return (md->string);
}
const char *
metadata_get_artist(metadata_t *md)
metadata_get_artist(struct metadata *md)
{
if (md->artist == NULL)
return (blankString);
@ -550,7 +551,7 @@ metadata_get_artist(metadata_t *md)
}
const char *
metadata_get_title(metadata_t *md)
metadata_get_title(struct metadata *md)
{
if (md->title == NULL)
return (blankString);
@ -559,7 +560,7 @@ metadata_get_title(metadata_t *md)
}
const char *
metadata_get_filename(metadata_t *md)
metadata_get_filename(struct metadata *md)
{
if (md->filename == NULL)
/* Should never happen: */
@ -569,13 +570,13 @@ metadata_get_filename(metadata_t *md)
}
int
metadata_get_length(metadata_t *md)
metadata_get_length(struct metadata *md)
{
return (md->songLen);
}
char *
metadata_assemble_string(metadata_t *md)
metadata_assemble_string(struct metadata *md)
{
size_t len;
char *str;

View File

@ -26,14 +26,14 @@ enum metadata_request {
METADATA_TITLE
};
typedef struct metadata metadata_t;
typedef struct metadata * metadata_t;
/*
* Read the metadata of a media file and return a new metadata handle on
* success, or NULL on failure. The returned handle is "branded" for reading
* metadata from media files.
*/
metadata_t * metadata_file(const char * /* filename */,
metadata_t metadata_file(const char * /* filename */,
int /* normalize strings */);
/*
@ -53,26 +53,26 @@ metadata_t * metadata_file(const char * /* filename */,
* metadata, or an empty string if no artist information is available.
* - Return at most METADATA_MAX characters, or the result will be truncated.
*/
metadata_t * metadata_program(const char * /* program name */,
metadata_t metadata_program(const char * /* program name */,
int /* normalize strings */);
/*
* Free all memory used by a metadata handle that has been created with
* metadata_file() or metadata_program().
*/
void metadata_free(metadata_t **);
void metadata_free(metadata_t *);
/*
* Update/read the metadata for the given handle. Returns 1 on success, and 0
* on failure.
*/
int metadata_file_update(metadata_t *);
int metadata_file_update(metadata_t);
/*
* Update/read the specified metadata for the given program-handle. Returns 1
* on success, and 0 on failure.
*/
int metadata_program_update(metadata_t *, enum metadata_request);
int metadata_program_update(metadata_t, enum metadata_request);
/*
* Returns a pointer to a metadata string ``artist - title'', or just
@ -81,34 +81,34 @@ int metadata_program_update(metadata_t *, enum metadata_request);
* is returned for metadata_program() handles that didn't supply any generic
* information.
*/
const char * metadata_get_string(metadata_t *);
const char * metadata_get_string(metadata_t);
/*
* Returns a pointer to the artist string, which may be empty.
*/
const char * metadata_get_artist(metadata_t *);
const char * metadata_get_artist(metadata_t);
/*
* Returns a pointer to the title string, which may be empty.
*/
const char * metadata_get_title(metadata_t *);
const char * metadata_get_title(metadata_t);
/*
* Returns a pointer to the filename used in the metadata handle.
*/
const char * metadata_get_filename(metadata_t *);
const char * metadata_get_filename(metadata_t);
/*
* Returns the length of the song, in seconds, or -1 if the information is not
* available.
*/
int metadata_get_length(metadata_t *);
int metadata_get_length(metadata_t);
/*
* Allocates and returns a meaningful string based on a metadata handle's
* content. The result is what metadata_get_string() defaults to if an external
* program is not used.
*/
char * metadata_assemble_string(metadata_t *);
char * metadata_assemble_string(metadata_t);
#endif /* __METADATA_H__ */

View File

@ -18,14 +18,252 @@
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <sys/queue.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <shout/shout.h>
#include "cfg.h"
#include "log.h"
#include "stream.h"
#include "util.h"
#include "xalloc.h"
struct stream {
TAILQ_ENTRY(stream) entry;
char *name;
shout_t *shout;
};
TAILQ_HEAD(stream_list, stream);
static struct stream_list streams;
static int _stream_cfg_server(struct stream *);
static int _stream_cfg_tls(struct stream *);
static int _stream_cfg_stream(struct stream *);
static int
_stream_cfg_server(struct stream *s)
{
switch (cfg_get_server_protocol()) {
case CFG_PROTO_HTTP:
if (SHOUTERR_SUCCESS !=
shout_set_protocol(s->shout, SHOUT_PROTOCOL_HTTP)) {
log_error("%s: protocol: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
break;
default:
log_error("%s: protocol: unsupported: %s",
s->name, cfg_get_server_protocol_str());
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_host(s->shout, cfg_get_server_hostname())) {
log_error("%s: hostname: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_port(s->shout, (unsigned short)cfg_get_server_port())) {
log_error("%s: port: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_user(s->shout, cfg_get_server_user())) {
log_error("%s: user: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_password(s->shout, cfg_get_server_password())) {
log_error("%s: password: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
return (0);
}
static int
_stream_cfg_tls(struct stream *s)
{
#ifdef SHOUT_TLS_AUTO
int tls_req;
switch (cfg_get_server_tls()) {
case CFG_TLS_NONE:
tls_req = SHOUT_TLS_DISABLED;
break;
case CFG_TLS_MAY:
tls_req = SHOUT_TLS_AUTO;
break;
case CFG_TLS_REQUIRED:
tls_req = SHOUT_TLS_AUTO_NO_PLAIN;
break;
default:
log_error("%s: tls: invalid", s->name);
return (-1);
}
if (SHOUTERR_SUCCESS != shout_set_tls(s->shout, tls_req)) {
log_error("%s: tls: %s", s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_ca_dir() &&
SHOUTERR_SUCCESS !=
shout_set_ca_directory(s->shout, cfg_get_server_ca_dir())) {
log_error("%s: ca_dir: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_ca_file() &&
SHOUTERR_SUCCESS !=
shout_set_ca_file(s->shout, cfg_get_server_ca_file())) {
log_error("%s: ca_file: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_tls_cipher_suite() &&
SHOUTERR_SUCCESS !=
shout_set_allowed_ciphers(s->shout, cfg_get_server_tls_cipher_suite())) {
log_error("%s: tls_cipher_suite: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_client_cert() &&
SHOUTERR_SUCCESS !=
shout_set_client_certificate(s->shout, cfg_get_server_client_cert())) {
log_error("%s: client_cert: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
#else /* SHOUT_TLS_AUTO */
# warning "libshout library does not support TLS"
switch (cfg_get_server_tls()) {
case CFG_TLS_MAY:
log_warning("%s: TLS optional but not supported by libshout",
s->name);
break;
case CFG_TLS_REQUIRED:
log_error("%s: TLS required by not supported by libshout",
s->name);
return (-1);
default:
break;
}
#endif /* SHOUT_TLS_AUTO */
return (0);
}
static int
_stream_cfg_stream(struct stream *s)
{
if (SHOUTERR_SUCCESS !=
shout_set_mount(s->shout, cfg_get_stream_mountpoint())) {
log_error("%s: mountpoint: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
switch (cfg_get_stream_format()) {
case CFG_STREAM_VORBIS:
case CFG_STREAM_THEORA:
if (SHOUTERR_SUCCESS !=
shout_set_format(s->shout, SHOUT_FORMAT_OGG)) {
log_error("%s: format_ogg: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
break;
case CFG_STREAM_MP3:
if (SHOUTERR_SUCCESS !=
shout_set_format(s->shout, SHOUT_FORMAT_MP3)) {
log_error("%s: format_mp3: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
break;
default:
log_error("%s: format: unsupported: %s",
s->name, cfg_get_stream_format_str());
return (-1);
}
if (cfg_get_stream_name() &&
SHOUTERR_SUCCESS !=
shout_set_name(s->shout, cfg_get_stream_name())) {
log_error("%s: name: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_url() &&
SHOUTERR_SUCCESS !=
shout_set_url(s->shout, cfg_get_stream_url())) {
log_error("%s: url: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_genre() &&
SHOUTERR_SUCCESS !=
shout_set_genre(s->shout, cfg_get_stream_genre())) {
log_error("%s: genre: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_description() &&
SHOUTERR_SUCCESS !=
shout_set_description(s->shout, cfg_get_stream_description())) {
log_error("%s: description: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_quality() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_QUALITY, cfg_get_stream_quality())) {
log_error("%s: ai_quality: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_bitrate() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_BITRATE, cfg_get_stream_bitrate())) {
log_error("%s: ai_bitrate: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_samplerate() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_SAMPLERATE, cfg_get_stream_samplerate())) {
log_error("%s: ai_samplerate: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_channels() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_CHANNELS, cfg_get_stream_channels())) {
log_error("%s: ai_channels: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_public(s->shout, (unsigned int)cfg_get_stream_server_public())) {
log_error("%s: public: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
return (0);
}
int
stream_init(void)
{
TAILQ_INIT(&streams);
shout_init();
return (0);
}
@ -33,5 +271,181 @@ stream_init(void)
void
stream_exit(void)
{
struct stream *e;
while (NULL != (e = TAILQ_FIRST(&streams))) {
TAILQ_REMOVE(&streams, e, entry);
xfree(e->name);
shout_free(e->shout);
xfree(e);
}
shout_shutdown();
}
struct stream *
stream_get(const char *name)
{
struct stream *e;
if (!name || !name[0]) {
log_alert("stream_get: empty name");
exit(1);
}
TAILQ_FOREACH(e, &streams, entry) {
if (0 == strcasecmp(e->name, name))
return (e);
}
e = xcalloc(1UL, sizeof(*e));
e->name = xstrdup(name);
e->shout = shout_new();
if (NULL == e->shout) {
log_syserr(ALERT, ENOMEM, "shout_new");
exit(1);
}
TAILQ_INSERT_TAIL(&streams, e, entry);
return (e);
}
int
stream_setup(struct stream *s)
{
if (0 != _stream_cfg_server(s) ||
0 != _stream_cfg_tls(s) ||
0 != _stream_cfg_stream(s)) {
/* Reset handle on error */
shout_free(s->shout);
s->shout = shout_new();
if (NULL == s->shout) {
log_syserr(ALERT, ENOMEM, "shout_new");
exit(1);
}
return (-1);
}
return (0);
}
int
stream_set_metadata(struct stream *s, metadata_t md, char **md_str)
{
shout_metadata_t *shout_md = NULL;
char *songInfo;
const char *artist, *title;
int ret = SHOUTERR_SUCCESS;
if (cfg_get_metadata_no_updates())
return (SHOUTERR_SUCCESS);
if (md == NULL)
return 1;
if ((shout_md = shout_metadata_new()) == NULL) {
log_syserr(ALERT, ENOMEM, "shout_metadata_new");
exit(1);
}
artist = metadata_get_artist(md);
title = metadata_get_title(md);
/*
* We can do this, because we know how libshout works. This adds
* "charset=UTF-8" to the HTTP metadata update request and has the
* desired effect of letting newer-than-2.3.1 versions of Icecast know
* which encoding we're using.
*/
if (shout_metadata_add(shout_md, "charset", "UTF-8") != SHOUTERR_SUCCESS) {
/* Assume SHOUTERR_MALLOC */
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
exit(1);
}
songInfo = stream_get_metadata_str(cfg_get_metadata_format_str(), md);
if (songInfo == NULL) {
if (artist[0] == '\0' && title[0] == '\0')
songInfo = xstrdup(metadata_get_string(md));
else
songInfo = metadata_assemble_string(md);
if (artist[0] != '\0' && title[0] != '\0') {
if (shout_metadata_add(shout_md, "artist", artist) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM,
"shout_metadata_add");
exit(1);
}
if (shout_metadata_add(shout_md, "title", title) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM,
"shout_metadata_add");
exit(1);
}
} else {
if (shout_metadata_add(shout_md, "song", songInfo) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM,
"shout_metadata_add");
exit(1);
}
}
} else if (shout_metadata_add(shout_md, "song", songInfo) != SHOUTERR_SUCCESS) {
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
exit(1);
}
if ((ret = shout_set_metadata(s->shout, shout_md)) != SHOUTERR_SUCCESS)
log_warning("shout_set_metadata: %s", shout_get_error(s->shout));
shout_metadata_free(shout_md);
if (ret == SHOUTERR_SUCCESS) {
if (md_str != NULL && *md_str == NULL)
*md_str = xstrdup(songInfo);
}
xfree(songInfo);
return (ret);
}
char *
stream_get_metadata_str(const char *format, metadata_t mdata)
{
char *tmp, *str;
if (format == NULL)
return (NULL);
str = xstrdup(format);
if (strstr(format, PLACEHOLDER_ARTIST) != NULL) {
tmp = replaceString(str, PLACEHOLDER_ARTIST,
metadata_get_artist(mdata));
xfree(str);
str = tmp;
}
if (strstr(format, PLACEHOLDER_TITLE) != NULL) {
tmp = replaceString(str, PLACEHOLDER_TITLE,
metadata_get_title(mdata));
xfree(str);
str = tmp;
}
if (strstr(format, PLACEHOLDER_STRING) != NULL) {
tmp = replaceString(str, PLACEHOLDER_STRING,
metadata_get_string(mdata));
xfree(str);
str = tmp;
}
if (strstr(format, PLACEHOLDER_TRACK) != NULL) {
tmp = replaceString(str, PLACEHOLDER_TRACK,
metadata_get_filename(mdata));
xfree(str);
str = tmp;
}
return (str);
}
shout_t *
stream_get_shout(struct stream *s)
{
return (s->shout);
}

View File

@ -17,7 +17,24 @@
#ifndef __STREAM_H__
#define __STREAM_H__
#include <shout/shout.h>
#include "metadata.h"
#define STREAM_DEFAULT "default"
typedef struct stream * stream_t;
int stream_init(void);
void stream_exit(void);
stream_t
stream_get(const char *);
int stream_setup(stream_t);
int stream_set_metadata(stream_t, metadata_t, char **);
char * stream_get_metadata_str(const char *, metadata_t);
shout_t *
stream_get_shout(stream_t);
#endif /* __STREAM_H__ */

View File

@ -22,14 +22,18 @@
# include "config.h"
#endif
#include "ezstream.h"
#include "compat.h"
#include <ctype.h>
#include <errno.h>
#ifdef HAVE_LANGINFO_H
# include <langinfo.h>
#endif
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#include <stdio.h>
#include <string.h>
#ifdef HAVE_ICONV
# include <iconv.h>
@ -81,152 +85,6 @@ strrcasecmp(const char *s, const char *sub)
return (ret);
}
shout_t *
stream_setup(void)
{
shout_t *shout;
if ((shout = shout_new()) == NULL) {
log_syserr(ERROR, ENOMEM, "shout_new");
return (NULL);
}
switch (cfg_get_server_protocol()) {
case CFG_PROTO_HTTP:
if (SHOUTERR_SUCCESS !=
shout_set_protocol(shout, SHOUT_PROTOCOL_HTTP)) {
log_error("shout_set_protocol: %s",
shout_get_error(shout));
goto error;
}
break;
default:
log_error("unsupported protocol: %s",
cfg_get_server_protocol_str());
goto error;
}
if (SHOUTERR_SUCCESS !=
shout_set_host(shout, cfg_get_server_hostname())) {
log_error("shout_set_host: %s", shout_get_error(shout));
goto error;
}
if (SHOUTERR_SUCCESS !=
shout_set_port(shout, (unsigned short)cfg_get_server_port())) {
log_error("shout_set_port: %s", shout_get_error(shout));
goto error;
}
if (SHOUTERR_SUCCESS !=
shout_set_user(shout, cfg_get_server_user())) {
log_error("shout_set_user: %s", shout_get_error(shout));
goto error;
}
if (SHOUTERR_SUCCESS !=
shout_set_password(shout, cfg_get_server_password())) {
log_error("shout_set_password: %s", shout_get_error(shout));
goto error;
}
if (SHOUTERR_SUCCESS !=
shout_set_mount(shout, cfg_get_stream_mountpoint())) {
log_error("shout_set_mount: %s", shout_get_error(shout));
goto error;
}
switch (cfg_get_stream_format()) {
case CFG_STREAM_VORBIS:
case CFG_STREAM_THEORA:
if (SHOUTERR_SUCCESS !=
shout_set_format(shout, SHOUT_FORMAT_OGG)) {
log_error("shout_set_format: OGG: %s",
shout_get_error(shout));
goto error;
}
break;
case CFG_STREAM_MP3:
if (SHOUTERR_SUCCESS !=
shout_set_format(shout, SHOUT_FORMAT_MP3)) {
log_error("shout_set_format: MP3: %s",
shout_get_error(shout));
goto error;
}
break;
default:
log_error("unsupported stream format: %s",
cfg_get_stream_format_str());
goto error;
}
if (cfg_get_stream_name() &&
SHOUTERR_SUCCESS !=
shout_set_name(shout, cfg_get_stream_name())) {
log_error("shout_set_name: %s", shout_get_error(shout));
goto error;
}
if (cfg_get_stream_url() &&
SHOUTERR_SUCCESS !=
shout_set_url(shout, cfg_get_stream_url())) {
log_error("shout_set_url: %s", shout_get_error(shout));
goto error;
}
if (cfg_get_stream_genre() &&
SHOUTERR_SUCCESS !=
shout_set_genre(shout, cfg_get_stream_genre())) {
log_error("shout_set_genre: %s", shout_get_error(shout));
goto error;
}
if (cfg_get_stream_description() &&
SHOUTERR_SUCCESS !=
shout_set_description(shout, cfg_get_stream_description())) {
log_error("shout_set_description: %s",
shout_get_error(shout));
goto error;
}
if (cfg_get_stream_quality() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(shout, SHOUT_AI_QUALITY,
cfg_get_stream_quality())) {
log_error("shout_set_audio_info: QUALITY: %s",
shout_get_error(shout));
goto error;
}
if (cfg_get_stream_bitrate() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(shout, SHOUT_AI_BITRATE,
cfg_get_stream_bitrate())) {
log_error("shout_set_audio_info: BITRATE: %s",
shout_get_error(shout));
goto error;
}
if (cfg_get_stream_samplerate() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(shout, SHOUT_AI_SAMPLERATE,
cfg_get_stream_samplerate())) {
log_error("shout_set_audio_info: SAMPLERATE: %s",
shout_get_error(shout));
goto error;
}
if (cfg_get_stream_channels() &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(shout, SHOUT_AI_CHANNELS,
cfg_get_stream_channels())) {
log_error("shout_set_audio_info: CHANNELS: %s",
shout_get_error(shout));
goto error;
}
if (SHOUTERR_SUCCESS !=
shout_set_public(shout, (unsigned int)cfg_get_stream_server_public())) {
log_error("shout_set_public: %s", shout_get_error(shout));
goto error;
}
return (shout);
error:
shout_free(shout);
return (NULL);
}
char *
CHARtoUTF8(const char *in_str, int mode)
{
@ -358,3 +216,66 @@ iconvert(const char *in_str, const char *from, const char *to, int mode)
return (xstrdup(in_str));
#endif /* HAVE_ICONV */
}
char *
replaceString(const char *source, const char *from, const char *to)
{
char *to_quoted, *dest;
size_t dest_size;
const char *p1, *p2;
to_quoted = shellQuote(to);
dest_size = strlen(source) + strlen(to_quoted) + 1;
dest = xcalloc(dest_size, sizeof(char));
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);
}
strlcat(dest, p1, dest_size);
xfree(to_quoted);
return (dest);
}
#define SHELLQUOTE_INLEN_MAX 8191UL
char *
shellQuote(const char *in)
{
char *out, *out_p;
size_t out_len;
const char *in_p;
out_len = (strlen(in) > SHELLQUOTE_INLEN_MAX)
? SHELLQUOTE_INLEN_MAX
: strlen(in);
out_len = out_len * 2 + 2;
out = xcalloc(out_len + 1, sizeof(char));
out_p = out;
in_p = in;
*out_p++ = '\'';
out_len--;
while (*in_p && out_len > 2) {
switch (*in_p) {
case '\'':
case '\\':
*out_p++ = '\\';
out_len--;
break;
default:
break;
}
*out_p++ = *in_p++;
out_len--;
}
*out_p++ = '\'';
return (out);
}

View File

@ -16,16 +16,15 @@
#ifndef __UTIL_H__
#define __UTIL_H__
#include <shout/shout.h>
#define ICONV_REPLACE 0
#define ICONV_TRANSLIT 1
#define ICONV_IGNORE 2
int strrcmp(const char *, const char *);
int strrcasecmp(const char *, const char *);
shout_t * stream_setup(void);
char * CHARtoUTF8(const char *, int);
char * UTF8toCHAR(const char *, int);
char * replaceString(const char *, const char *, const char *);
char * shellQuote(const char *);
#endif /* __UTIL_H__ */