From d81a3e23bb00af65286c4d41367977197d05148b Mon Sep 17 00:00:00 2001 From: Moritz Grimm Date: Tue, 23 Jan 2018 01:51:48 +0100 Subject: [PATCH] Add configuration migration tool --- NEWS | 4 +- configure.ac | 1 + doc/Makefile.am | 5 +- doc/ezstream-cfgmigrate.1.in.in | 61 ++ doc/ezstream-file.sh.1.in.in | 3 +- doc/ezstream.1.in.in | 1 + src/Makefile.am | 24 +- src/cfg.c | 4 +- src/cfg_decoder.c | 50 +- src/cfg_decoder.h | 7 + src/cfg_encoder.c | 39 +- src/cfg_encoder.h | 5 + src/cfg_intake.c | 42 + src/cfg_intake.h | 6 + src/cfg_private.h | 3 - src/cfg_server.c | 42 +- src/cfg_server.h | 31 +- src/cfg_stream.c | 24 + src/cfg_stream.h | 4 + src/{cfg_xmlfile.c => cfgfile_xml.c} | 300 ++++++- src/{cfg_xmlfile.h => cfgfile_xml.h} | 9 +- src/ezconfig0.c | 814 ++++++++++++++++++ src/ezconfig0.h | 75 ++ src/ezstream-cfgmigrate.c | 423 +++++++++ tests/Makefile.am | 8 +- tests/check_cfg_decoder.c | 27 + tests/check_cfg_encoder.c | 17 + tests/check_cfg_intake.c | 22 + tests/check_cfg_server.c | 24 +- tests/check_cfg_stream.c | 17 + ...heck_cfg_xmlfile.c => check_cfgfile_xml.c} | 56 +- 31 files changed, 2050 insertions(+), 98 deletions(-) create mode 100644 doc/ezstream-cfgmigrate.1.in.in rename src/{cfg_xmlfile.c => cfgfile_xml.c} (57%) rename src/{cfg_xmlfile.h => cfgfile_xml.h} (83%) create mode 100644 src/ezconfig0.c create mode 100644 src/ezconfig0.h create mode 100644 src/ezstream-cfgmigrate.c rename tests/{check_cfg_xmlfile.c => check_cfgfile_xml.c} (63%) diff --git a/NEWS b/NEWS index 359da32..91838e1 100644 --- a/NEWS +++ b/NEWS @@ -9,11 +9,13 @@ Changes in 1.0.0, released on XXXX-XX-XX: file settings have been added accordingly. * The real-time status information is now enabled explicitly with the new command line option -r. - * The configuration file structure has changed. * TagLib (its C wrapper library) is now a mandatory dependency * Support the new '@b@' placeholder for separate album metadata. * The command line option -p has been added, causing ezstream to write a locked PID file to a given location + * The configuration file structure has changed significantly. A configuration + migration tool (ezstream-cfgmigrate) has been added to assist in the + upgrade. Changes in 0.6.0, released on 2015-01-18: diff --git a/configure.ac b/configure.ac index eca1811..a634de4 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,7 @@ AC_CONFIG_FILES([ build-aux/Makefile compat/Makefile doc/Makefile + doc/ezstream-cfgmigrate.1.in doc/ezstream-file.sh.1.in doc/ezstream.1.in examples/Makefile diff --git a/doc/Makefile.am b/doc/Makefile.am index 266d43c..a24326e 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = 1.10 foreign subdir-objects -man_MANS = ezstream.1 ezstream-file.sh.1 +man_MANS = ezstream.1 ezstream-cfgmigrate.1 ezstream-file.sh.1 do_subst = sed \ -e 's|!!EXAMPLES_DIR!!|@EXAMPLES_DIR@|g' @@ -10,6 +10,9 @@ do_subst = sed \ ezstream.1: ezstream.1.in Makefile $(do_subst) < $(builddir)/ezstream.1.in > ezstream.1 +ezstream-cfgmigrate.1: ezstream-cfgmigrate.1.in Makefile + $(do_subst) < $(builddir)/ezstream-cfgmigrate.1.in > ezstream-cfgmigrate.1 + ezstream-file.sh.1: ezstream-file.sh.1.in Makefile $(do_subst) < $(builddir)/ezstream-file.sh.1.in > ezstream-file.sh.1 diff --git a/doc/ezstream-cfgmigrate.1.in.in b/doc/ezstream-cfgmigrate.1.in.in new file mode 100644 index 0000000..e61a50d --- /dev/null +++ b/doc/ezstream-cfgmigrate.1.in.in @@ -0,0 +1,61 @@ +.\" Copyright (c) 2018 Moritz Grimm +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd @BUILD_DATE@ +.Dt EZSTREAM-CFGMIGRATE 1 +.Os @PACKAGE_NAME@ @PACKAGE_VERSION@ +.Sh NAME +.Nm ezstream-cfgmigrate +.Nd migrate old ezstream configuration +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl hv +.Fl 0 Ar v0-cfgfile +.Ek +.Sh DESCRIPTION +The +.Nm +utility reads configuration files from older versions of +.Nm ezstream +and prints its current equivalent to standard output. +.Pp +It supports reading XML configuration from +.Nm ezstream +version 0.x +.Pq zero . +.Ss Command line arguments +.Bl -tag -width Ds +.It Fl 0 Ar v0-cfgfile +.Pq The number zero. +.Pp +Read the XML configuration in +.Ar v0-cfgfile +and print its migrated content to standard output. +.It Fl h +Print a summary of available command line arguments with short descriptions +and exit. +.It Fl v +Increase logging verbosity. +May be used up to three times to also include debug logging output. +.El +.Sh SEE ALSO +.Xr ezstream 1 , +.Xr ezstream-file.sh 1 +.Sh AUTHORS +.An -nosplit +The +.Nm +program and this manual were written by +.An Moritz Grimm . diff --git a/doc/ezstream-file.sh.1.in.in b/doc/ezstream-file.sh.1.in.in index 8805bdd..444dcb7 100644 --- a/doc/ezstream-file.sh.1.in.in +++ b/doc/ezstream-file.sh.1.in.in @@ -114,7 +114,8 @@ Directory containing example configuration files for various uses of as well as example playlist and metadata scripts. .El .Sh SEE ALSO -.Xr ezstream 1 +.Xr ezstream 1 , +.Xr ezstream-cfgmigrate 1 .Sh AUTHORS .An -nosplit The diff --git a/doc/ezstream.1.in.in b/doc/ezstream.1.in.in index 91452cc..f05c028 100644 --- a/doc/ezstream.1.in.in +++ b/doc/ezstream.1.in.in @@ -829,6 +829,7 @@ Directory containing example configuration files for various uses of as well as example playlist and metadata scripts. .El .Sh SEE ALSO +.Xr ezstream-cfgmigrate 1 , .Xr ezstream-file.sh 1 .Sh AUTHORS .Nm diff --git a/src/Makefile.am b/src/Makefile.am index 5344d1f..66f4361 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,8 +10,9 @@ noinst_HEADERS = \ cfg_private.h \ cfg_server.h \ cfg_stream.h \ - cfg_xmlfile.h \ + cfgfile_xml.h \ cmdline.h \ + ezconfig0.h \ ezstream.h \ log.h \ mdata.h \ @@ -21,18 +22,18 @@ noinst_HEADERS = \ xalloc.h libcommon_la_SOURCES = \ - log.c \ - util.c \ - xalloc.c - -libezstream_la_SOURCES = \ cfg.c \ cfg_decoder.c \ cfg_encoder.c \ cfg_intake.c \ cfg_server.c \ cfg_stream.c \ - cfg_xmlfile.c \ + cfgfile_xml.c \ + log.c \ + util.c \ + xalloc.c + +libezstream_la_SOURCES = \ cmdline.c \ mdata.c \ playlist.c \ @@ -44,12 +45,19 @@ libezstream_la_LIBADD = @EZ_LIBS@ \ $(libezstream_la_DEPENDENCIES) bin_SCRIPTS = ezstream-file.sh -bin_PROGRAMS = ezstream +bin_PROGRAMS = ezstream ezstream-cfgmigrate ezstream_SOURCES = ezstream.c ezstream_DEPENDENCIES = libezstream.la ezstream_LDADD = $(ezstream_DEPENDENCIES) +ezstream_cfgmigrate_SOURCES = ezstream-cfgmigrate.c ezconfig0.c +ezstream_cfgmigrate_DEPENDENCIES = \ + $(builddir)/libcommon.la \ + $(top_builddir)/compat/libcompat.la +ezstream_cfgmigrate_LDADD = @EZ_LIBS@ \ + $(ezstream_cfgmigrate_DEPENDENCIES) + AM_CPPFLAGS = @EZ_CPPFLAGS@ -I$(top_srcdir)/compat AM_CFLAGS = @EZ_CFLAGS@ AM_LDFLAGS = @EZ_LDFLAGS@ -avoid-version diff --git a/src/cfg.c b/src/cfg.c index 72004a6..6ed077f 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -27,7 +27,7 @@ #include #include "cfg_private.h" -#include "cfg_xmlfile.h" +#include "cfgfile_xml.h" #include "log.h" #include "xalloc.h" @@ -73,7 +73,7 @@ _cfg_load(void) { switch (cfg_program.config_type) { case CFG_TYPE_XMLFILE: - if (0 > cfg_xmlfile_parse(cfg_program.config_file)) + if (0 > cfgfile_xml_parse(cfg_program.config_file)) return (-1); break; default: diff --git a/src/cfg_decoder.c b/src/cfg_decoder.c index 27e74aa..726aec5 100644 --- a/src/cfg_decoder.c +++ b/src/cfg_decoder.c @@ -54,7 +54,7 @@ cfg_decoder_list_create(void) } void -cfg_decoder_list_destroy(cfg_decoder_list_t *dl_p) +cfg_decoder_list_destroy(struct cfg_decoder_list **dl_p) { struct cfg_decoder_list *dl = *dl_p; struct cfg_decoder *d; @@ -62,15 +62,26 @@ cfg_decoder_list_destroy(cfg_decoder_list_t *dl_p) if (!dl) return; - while (NULL != (d = TAILQ_FIRST(dl))) { - TAILQ_REMOVE(dl, d, entry); - cfg_decoder_destroy(&d); - } + while (NULL != (d = TAILQ_FIRST(dl))) + cfg_decoder_list_remove(dl, &d); xfree(dl); *dl_p = NULL; } +unsigned int +cfg_decoder_list_nentries(struct cfg_decoder_list *dl) +{ + struct cfg_decoder *d; + unsigned int n = 0; + + TAILQ_FOREACH(d, dl, entry) { + n++; + } + + return (n); +} + struct cfg_decoder * cfg_decoder_list_find(struct cfg_decoder_list *dl, const char *name) { @@ -115,6 +126,24 @@ cfg_decoder_list_get(struct cfg_decoder_list *dl, const char *name) return (d); } +void +cfg_decoder_list_remove(struct cfg_decoder_list *dl, struct cfg_decoder **d_p) +{ + TAILQ_REMOVE(dl, *d_p, entry); + cfg_decoder_destroy(d_p); +} + +void +cfg_decoder_list_foreach(struct cfg_decoder_list *dl, + void (*cb)(cfg_decoder_t, void *), void *cb_arg) +{ + struct cfg_decoder *d; + + TAILQ_FOREACH(d, dl, entry) { + cb(d, cb_arg); + } +} + struct cfg_decoder * cfg_decoder_create(const char *name) { @@ -271,6 +300,17 @@ cfg_decoder_extsupport(struct cfg_decoder *d, const char *ext) return (0); } +void +cfg_decoder_ext_foreach(struct cfg_decoder *d, + void (*cb)(const char *, void *), void *cb_arg) +{ + struct file_ext *e; + + TAILQ_FOREACH(e, &d->exts, entry) { + cb(e->ext, cb_arg); + } +} + const char * cfg_decoder_get_name(struct cfg_decoder *d) { diff --git a/src/cfg_decoder.h b/src/cfg_decoder.h index fcc7ab5..9ad7ec6 100644 --- a/src/cfg_decoder.h +++ b/src/cfg_decoder.h @@ -23,6 +23,8 @@ typedef struct cfg_decoder_list * cfg_decoder_list_t; cfg_decoder_list_t cfg_decoder_list_create(void); void cfg_decoder_list_destroy(cfg_decoder_list_t *); +unsigned int + cfg_decoder_list_nentries(cfg_decoder_list_t); cfg_decoder_t cfg_decoder_list_find(cfg_decoder_list_t, const char *); @@ -30,6 +32,9 @@ cfg_decoder_t cfg_decoder_list_findext(cfg_decoder_list_t, const char *); cfg_decoder_t cfg_decoder_list_get(cfg_decoder_list_t, const char *); +void cfg_decoder_list_remove(cfg_decoder_list_t, cfg_decoder_t *); +void cfg_decoder_list_foreach(cfg_decoder_list_t, void (*)(cfg_decoder_t, + void *), void *); cfg_decoder_t cfg_decoder_create(const char *); @@ -45,6 +50,8 @@ int cfg_decoder_add_match(cfg_decoder_t, cfg_decoder_list_t, const char *, int cfg_decoder_validate(cfg_decoder_t, const char **); int cfg_decoder_extsupport(cfg_decoder_t, const char *); +void cfg_decoder_ext_foreach(cfg_decoder_t, void (*)(const char *, void *), + void *); const char * cfg_decoder_get_name(cfg_decoder_t); diff --git a/src/cfg_encoder.c b/src/cfg_encoder.c index 1585c2d..068595b 100644 --- a/src/cfg_encoder.c +++ b/src/cfg_encoder.c @@ -48,7 +48,7 @@ cfg_encoder_list_create(void) } void -cfg_encoder_list_destroy(cfg_encoder_list_t *el_p) +cfg_encoder_list_destroy(struct cfg_encoder_list **el_p) { struct cfg_encoder_list *el = *el_p; struct cfg_encoder *e; @@ -56,15 +56,26 @@ cfg_encoder_list_destroy(cfg_encoder_list_t *el_p) if (!el) return; - while (NULL != (e = TAILQ_FIRST(el))) { - TAILQ_REMOVE(el, e, entry); - cfg_encoder_destroy(&e); - } + while (NULL != (e = TAILQ_FIRST(el))) + cfg_encoder_list_remove(el, &e); xfree(el); *el_p = NULL; } +unsigned int +cfg_encoder_list_nentries(struct cfg_encoder_list *el) +{ + struct cfg_encoder *e; + unsigned int n = 0; + + TAILQ_FOREACH(e, el, entry) { + n++; + } + + return (n); +} + struct cfg_encoder * cfg_encoder_list_find(struct cfg_encoder_list *el, const char *name) { @@ -95,6 +106,24 @@ cfg_encoder_list_get(struct cfg_encoder_list *el, const char *name) return (e); } +void +cfg_encoder_list_foreach(struct cfg_encoder_list *el, + void (*cb)(cfg_encoder_t, void *), void *cb_arg) +{ + struct cfg_encoder *e; + + TAILQ_FOREACH(e, el, entry) { + cb(e, cb_arg); + } +} + +void +cfg_encoder_list_remove(struct cfg_encoder_list *el, struct cfg_encoder **e_p) +{ + TAILQ_REMOVE(el, *e_p, entry); + cfg_encoder_destroy(e_p); +} + struct cfg_encoder * cfg_encoder_create(const char *name) { diff --git a/src/cfg_encoder.h b/src/cfg_encoder.h index 8b0df84..4953cf8 100644 --- a/src/cfg_encoder.h +++ b/src/cfg_encoder.h @@ -25,11 +25,16 @@ typedef struct cfg_encoder_list * cfg_encoder_list_t; cfg_encoder_list_t cfg_encoder_list_create(void); void cfg_encoder_list_destroy(cfg_encoder_list_t *); +unsigned int + cfg_encoder_list_nentries(cfg_encoder_list_t); cfg_encoder_t cfg_encoder_list_find(cfg_encoder_list_t, const char *); cfg_encoder_t cfg_encoder_list_get(cfg_encoder_list_t, const char *); +void cfg_encoder_list_remove(cfg_encoder_list_t, cfg_encoder_t *o); +void cfg_encoder_list_foreach(cfg_encoder_list_t, void (*)(cfg_encoder_t, + void *), void *); cfg_encoder_t cfg_encoder_create(const char *); diff --git a/src/cfg_intake.c b/src/cfg_intake.c index 5a9fc56..cc918d9 100644 --- a/src/cfg_intake.c +++ b/src/cfg_intake.c @@ -68,6 +68,19 @@ cfg_intake_list_destroy(cfg_intake_list_t *il_p) *il_p = NULL; } +unsigned int +cfg_intake_list_nentries(struct cfg_intake_list *il) +{ + struct cfg_intake *i; + unsigned int n = 0; + + TAILQ_FOREACH(i, il, entry) { + n++; + } + + return (n); +} + struct cfg_intake * cfg_intake_list_find(struct cfg_intake_list *il, const char *name) { @@ -98,6 +111,17 @@ cfg_intake_list_get(struct cfg_intake_list *il, const char *name) return (i); } +void +cfg_intake_list_foreach(struct cfg_intake_list *il, + void (*cb)(cfg_intake_t, void *), void *cb_arg) +{ + struct cfg_intake *i; + + TAILQ_FOREACH(i, il, entry) { + cb(i, cb_arg); + } +} + struct cfg_intake * cfg_intake_create(const char *name) { @@ -228,6 +252,24 @@ cfg_intake_get_type(struct cfg_intake *i) return (i->type); } +const char * +cfg_intake_get_type_str(struct cfg_intake *i) +{ + switch (i->type) { + case CFG_INTAKE_FILE: + return ("file"); + case CFG_INTAKE_PLAYLIST: + return ("playlist"); + case CFG_INTAKE_PROGRAM: + return ("program"); + case CFG_INTAKE_STDIN: + return ("stdin"); + case CFG_INTAKE_AUTODETECT: + default: + return ("autodetect"); + } +} + const char * cfg_intake_get_filename(struct cfg_intake *i) { diff --git a/src/cfg_intake.h b/src/cfg_intake.h index 0dd31a6..154f377 100644 --- a/src/cfg_intake.h +++ b/src/cfg_intake.h @@ -33,11 +33,15 @@ typedef struct cfg_intake_list * cfg_intake_list_t; cfg_intake_list_t cfg_intake_list_create(void); void cfg_intake_list_destroy(cfg_intake_list_t *); +unsigned int + cfg_intake_list_nentries(cfg_intake_list_t); cfg_intake_t cfg_intake_list_find(cfg_intake_list_t, const char *); cfg_intake_t cfg_intake_list_get(cfg_intake_list_t, const char *); +void cfg_intake_list_foreach(cfg_intake_list_t, void (*)(cfg_intake_t, + void *), void *); cfg_intake_t cfg_intake_create(const char *); @@ -60,6 +64,8 @@ const char * cfg_intake_get_name(cfg_intake_t); enum cfg_intake_type cfg_intake_get_type(cfg_intake_t); +const char * + cfg_intake_get_type_str(cfg_intake_t); const char * cfg_intake_get_filename(cfg_intake_t); int cfg_intake_get_shuffle(cfg_intake_t); diff --git a/src/cfg_private.h b/src/cfg_private.h index 0665a8d..9927bd2 100644 --- a/src/cfg_private.h +++ b/src/cfg_private.h @@ -30,9 +30,6 @@ #define UCREDS_SIZE 256 #define CSUITE_SIZE 2048 -#define DEFAULT_PORT 8000 -#define DEFAULT_USER "source" - struct cfg_program { char name[PATH_MAX]; enum cfg_config_type config_type; diff --git a/src/cfg_server.c b/src/cfg_server.c index 0945c7c..1ce9edd 100644 --- a/src/cfg_server.c +++ b/src/cfg_server.c @@ -76,6 +76,19 @@ cfg_server_list_destroy(cfg_server_list_t *sl_p) *sl_p = NULL; } +unsigned int +cfg_server_list_nentries(struct cfg_server_list *sl) +{ + struct cfg_server *s; + unsigned int n = 0; + + TAILQ_FOREACH(s, sl, entry) { + n++; + } + + return (n); +} + struct cfg_server * cfg_server_list_find(struct cfg_server_list *sl, const char *name) { @@ -106,6 +119,17 @@ cfg_server_list_get(struct cfg_server_list *sl, const char *name) return (s); } +void +cfg_server_list_foreach(struct cfg_server_list *sl, + void (*cb)(cfg_server_t, void *), void *cb_arg) +{ + struct cfg_server *s; + + TAILQ_FOREACH(s, sl, entry) { + cb(s, cb_arg); + } +} + struct cfg_server * cfg_server_create(const char *name) { @@ -355,13 +379,13 @@ cfg_server_get_hostname(struct cfg_server *s) unsigned int cfg_server_get_port(struct cfg_server *s) { - return (s->port ? s->port : DEFAULT_PORT); + return (s->port ? s->port : CFG_SERVER_DEFAULT_PORT); } const char * cfg_server_get_user(struct cfg_server *s) { - return (s->user[0] ? s->user : DEFAULT_USER); + return (s->user[0] ? s->user : CFG_SERVER_DEFAULT_USER); } const char * @@ -376,6 +400,20 @@ cfg_server_get_tls(struct cfg_server *s) return (s->tls); } +const char * +cfg_server_get_tls_str(struct cfg_server *s) +{ + switch (s->tls) { + case CFG_TLS_NONE: + return ("none"); + case CFG_TLS_REQUIRED: + return ("required"); + case CFG_TLS_MAY: + default: + return ("may"); + } +} + const char * cfg_server_get_tls_cipher_suite(struct cfg_server *s) { diff --git a/src/cfg_server.h b/src/cfg_server.h index 4895b9c..8018221 100644 --- a/src/cfg_server.h +++ b/src/cfg_server.h @@ -17,6 +17,9 @@ #ifndef __CFG_SERVER_H__ #define __CFG_SERVER_H__ +#define CFG_SERVER_DEFAULT_PORT 8000 +#define CFG_SERVER_DEFAULT_USER "source" + enum cfg_server_protocol { CFG_PROTO_HTTP = 0, CFG_PROTO_HTTPS, @@ -38,11 +41,15 @@ typedef struct cfg_server_list * cfg_server_list_t; cfg_server_list_t cfg_server_list_create(void); void cfg_server_list_destroy(cfg_server_list_t *); +unsigned int + cfg_server_list_nentries(cfg_server_list_t); cfg_server_t cfg_server_list_find(cfg_server_list_t, const char *); cfg_server_t cfg_server_list_get(cfg_server_list_t, const char *); +void cfg_server_list_foreach(cfg_server_list_t, void (*)(cfg_server_t, + void *), void *); cfg_server_t cfg_server_create(const char *); @@ -51,27 +58,27 @@ void cfg_server_destroy(cfg_server_t *); int cfg_server_set_name(cfg_server_t, cfg_server_list_t, const char *, const char **); int cfg_server_set_protocol(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_hostname(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_port(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_user(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_password(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_tls(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_tls_cipher_suite(cfg_server_t, cfg_server_list_t, - const char *, const char **); + const char *, const char **); int cfg_server_set_ca_dir(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_ca_file(cfg_server_t, cfg_server_list_t, const char *, - const char **); + const char **); int cfg_server_set_client_cert(cfg_server_t, cfg_server_list_t, - const char *, const char **); + const char *, const char **); int cfg_server_set_reconnect_attempts(cfg_server_t, cfg_server_list_t, - const char *, const char **); + const char *, const char **); int cfg_server_validate(cfg_server_t, const char **); @@ -91,6 +98,8 @@ const char * cfg_server_get_password(cfg_server_t); enum cfg_server_tls cfg_server_get_tls(cfg_server_t); +const char * + cfg_server_get_tls_str(cfg_server_t); const char * cfg_server_get_tls_cipher_suite(cfg_server_t); const char * diff --git a/src/cfg_stream.c b/src/cfg_stream.c index 6dd5545..892e5e4 100644 --- a/src/cfg_stream.c +++ b/src/cfg_stream.c @@ -76,6 +76,19 @@ cfg_stream_list_destroy(cfg_stream_list_t *sl_p) *sl_p = NULL; } +unsigned int +cfg_stream_list_nentries(struct cfg_stream_list *sl) +{ + struct cfg_stream *s; + unsigned int n = 0; + + TAILQ_FOREACH(s, sl, entry) { + n++; + } + + return (n); +} + struct cfg_stream * cfg_stream_list_find(struct cfg_stream_list *sl, const char *name) { @@ -106,6 +119,17 @@ cfg_stream_list_get(struct cfg_stream_list *sl, const char *name) return (s); } +void +cfg_stream_list_foreach(struct cfg_stream_list *sl, + void (*cb)(cfg_stream_t, void *), void *cb_arg) +{ + struct cfg_stream *s; + + TAILQ_FOREACH(s, sl, entry) { + cb(s, cb_arg); + } +} + struct cfg_stream * cfg_stream_create(const char *name) { diff --git a/src/cfg_stream.h b/src/cfg_stream.h index 81ae35d..bd9e6df 100644 --- a/src/cfg_stream.h +++ b/src/cfg_stream.h @@ -36,11 +36,15 @@ typedef struct cfg_stream_list * cfg_stream_list_t; cfg_stream_list_t cfg_stream_list_create(void); void cfg_stream_list_destroy(cfg_stream_list_t *); +unsigned int + cfg_stream_list_nentries(cfg_stream_list_t); cfg_stream_t cfg_stream_list_find(cfg_stream_list_t, const char *); cfg_stream_t cfg_stream_list_get(cfg_stream_list_t, const char *); +void cfg_stream_list_foreach(cfg_stream_list_t, void (*)(cfg_stream_t, + void *), void *); cfg_stream_t cfg_stream_create(const char *); diff --git a/src/cfg_xmlfile.c b/src/cfgfile_xml.c similarity index 57% rename from src/cfg_xmlfile.c rename to src/cfgfile_xml.c index ae52c53..2b821fc 100644 --- a/src/cfg_xmlfile.c +++ b/src/cfgfile_xml.c @@ -18,24 +18,33 @@ # include "config.h" #endif /* HAVE_CONFIG_H */ +#include +#include + #include "cfg.h" -#include "cfg_xmlfile.h" +#include "cfgfile_xml.h" #include "log.h" #include "xalloc.h" #include -static int _cfg_xmlfile_parse_server(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_servers(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_stream(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_streams(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_intake(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_intakes(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_metadata(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_decoder(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_decoders(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_encoder(xmlDocPtr, xmlNodePtr); -static int _cfg_xmlfile_parse_encoders(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_server(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_servers(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_stream(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_streams(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_intake(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_intakes(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_metadata(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_decoder(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_decoders(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_encoder(xmlDocPtr, xmlNodePtr); +static int _cfgfile_xml_parse_encoders(xmlDocPtr, xmlNodePtr); +static void _cfgfile_xml_print_server(cfg_server_t, void *); +static void _cfgfile_xml_print_stream(cfg_stream_t, void *); +static void _cfgfile_xml_print_intake(cfg_intake_t, void *); +static void _cfgfile_xml_print_decoder_ext(const char *, void *); +static void _cfgfile_xml_print_decoder(cfg_decoder_t, void *); +static void _cfgfile_xml_print_encoder(cfg_encoder_t, void *); #define XML_CHAR(s) (const xmlChar *)(s) #define STD_CHAR(s) (const char *)(s) @@ -60,7 +69,7 @@ static int _cfg_xmlfile_parse_encoders(xmlDocPtr, xmlNodePtr); } while (0) static int -_cfg_xmlfile_parse_server(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_server(xmlDocPtr doc, xmlNodePtr cur) { cfg_server_list_t sl; cfg_server_t s; @@ -95,11 +104,11 @@ _cfg_xmlfile_parse_server(xmlDocPtr doc, xmlNodePtr cur) } static int -_cfg_xmlfile_parse_servers(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_servers(xmlDocPtr doc, xmlNodePtr cur) { for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("server")) && - 0 > _cfg_xmlfile_parse_server(doc, cur)) + 0 > _cfgfile_xml_parse_server(doc, cur)) return (-1); } @@ -126,7 +135,7 @@ _cfg_xmlfile_parse_servers(xmlDocPtr doc, xmlNodePtr cur) } while (0) static int -_cfg_xmlfile_parse_stream(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_stream(xmlDocPtr doc, xmlNodePtr cur) { cfg_stream_list_t sl; cfg_stream_t s; @@ -163,11 +172,11 @@ _cfg_xmlfile_parse_stream(xmlDocPtr doc, xmlNodePtr cur) } static int -_cfg_xmlfile_parse_streams(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_streams(xmlDocPtr doc, xmlNodePtr cur) { for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("stream")) && - 0 > _cfg_xmlfile_parse_stream(doc, cur)) + 0 > _cfgfile_xml_parse_stream(doc, cur)) return (-1); } @@ -194,7 +203,7 @@ _cfg_xmlfile_parse_streams(xmlDocPtr doc, xmlNodePtr cur) } while (0) static int -_cfg_xmlfile_parse_intake(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_intake(xmlDocPtr doc, xmlNodePtr cur) { cfg_intake_list_t il; cfg_intake_t i; @@ -222,11 +231,11 @@ _cfg_xmlfile_parse_intake(xmlDocPtr doc, xmlNodePtr cur) } static int -_cfg_xmlfile_parse_intakes(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_intakes(xmlDocPtr doc, xmlNodePtr cur) { for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("intake")) && - 0 > _cfg_xmlfile_parse_intake(doc, cur)) + 0 > _cfgfile_xml_parse_intake(doc, cur)) return (-1); } @@ -249,7 +258,7 @@ _cfg_xmlfile_parse_intakes(xmlDocPtr doc, xmlNodePtr cur) } while (0) static int -_cfg_xmlfile_parse_metadata(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_metadata(xmlDocPtr doc, xmlNodePtr cur) { int error = 0; @@ -289,7 +298,7 @@ _cfg_xmlfile_parse_metadata(xmlDocPtr doc, xmlNodePtr cur) } while (0) static int -_cfg_xmlfile_parse_decoder(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_decoder(xmlDocPtr doc, xmlNodePtr cur) { cfg_decoder_list_t dl; cfg_decoder_t d; @@ -315,11 +324,11 @@ _cfg_xmlfile_parse_decoder(xmlDocPtr doc, xmlNodePtr cur) } static int -_cfg_xmlfile_parse_decoders(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_decoders(xmlDocPtr doc, xmlNodePtr cur) { for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("decoder")) && - 0 > _cfg_xmlfile_parse_decoder(doc, cur)) + 0 > _cfgfile_xml_parse_decoder(doc, cur)) return (-1); } @@ -346,7 +355,7 @@ _cfg_xmlfile_parse_decoders(xmlDocPtr doc, xmlNodePtr cur) } while (0) static int -_cfg_xmlfile_parse_encoder(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_encoder(xmlDocPtr doc, xmlNodePtr cur) { cfg_encoder_list_t el; cfg_encoder_t e; @@ -372,11 +381,11 @@ _cfg_xmlfile_parse_encoder(xmlDocPtr doc, xmlNodePtr cur) } static int -_cfg_xmlfile_parse_encoders(xmlDocPtr doc, xmlNodePtr cur) +_cfgfile_xml_parse_encoders(xmlDocPtr doc, xmlNodePtr cur) { for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("encoder")) && - 0 > _cfg_xmlfile_parse_encoder(doc, cur)) + 0 > _cfgfile_xml_parse_encoder(doc, cur)) return (-1); } @@ -395,20 +404,20 @@ _cfg_xmlfile_parse_encoders(xmlDocPtr doc, xmlNodePtr cur) * port * user * password - * reconnect_attempts * tls * tls_cipher_suite * ca_dir * ca_file * client_cert + * reconnect_attempts * ... * streams * stream * name * mountpoint - * public * intake * server + * public * format * encoder * stream_name @@ -448,7 +457,7 @@ _cfg_xmlfile_parse_encoders(xmlDocPtr doc, xmlNodePtr cur) * ... */ int -cfg_xmlfile_parse(const char *config_file) +cfgfile_xml_parse(const char *config_file) { xmlDocPtr doc = NULL; xmlNodePtr cur = NULL; @@ -478,32 +487,32 @@ cfg_xmlfile_parse(const char *config_file) for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("servers"))) { - if (0 > _cfg_xmlfile_parse_servers(doc, cur)) + if (0 > _cfgfile_xml_parse_servers(doc, cur)) error = 1; continue; } if (0 == xmlStrcasecmp(cur->name, XML_CHAR("streams"))) { - if (0 > _cfg_xmlfile_parse_streams(doc, cur)) + if (0 > _cfgfile_xml_parse_streams(doc, cur)) error = 1; continue; } if (0 == xmlStrcasecmp(cur->name, XML_CHAR("intakes"))) { - if (0 > _cfg_xmlfile_parse_intakes(doc, cur)) + if (0 > _cfgfile_xml_parse_intakes(doc, cur)) error = 1; continue; } if (0 == xmlStrcasecmp(cur->name, XML_CHAR("metadata"))) { - if (0 > _cfg_xmlfile_parse_metadata(doc, cur)) + if (0 > _cfgfile_xml_parse_metadata(doc, cur)) error = 1; continue; } if (0 == xmlStrcasecmp(cur->name, XML_CHAR("decoders"))) { - if (0 > _cfg_xmlfile_parse_decoders(doc, cur)) + if (0 > _cfgfile_xml_parse_decoders(doc, cur)) error = 1; continue; } if (0 == xmlStrcasecmp(cur->name, XML_CHAR("encoders"))) { - if (0 > _cfg_xmlfile_parse_encoders(doc, cur)) + if (0 > _cfgfile_xml_parse_encoders(doc, cur)) error = 1; continue; } @@ -521,3 +530,220 @@ error: return (-1); } + +static void +_cfgfile_xml_print_server(cfg_server_t s, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " \n"); + if (0 != strcasecmp(cfg_server_get_name(s), CFG_DEFAULT)) + fprintf(fp, " %s\n", + cfg_server_get_name(s)); + fprintf(fp, " %s\n", + cfg_server_get_protocol_str(s)); + if (cfg_server_get_hostname(s)) + fprintf(fp, " %s\n", + cfg_server_get_hostname(s)); + if (cfg_server_get_port(s) != CFG_SERVER_DEFAULT_PORT) + fprintf(fp, " %u\n", + cfg_server_get_port(s)); + if (0 != strcasecmp(cfg_server_get_user(s), CFG_SERVER_DEFAULT_USER)) + fprintf(fp, " %s\n", + cfg_server_get_user(s)); + if (cfg_server_get_password(s)) + fprintf(fp, " %s\n", + cfg_server_get_password(s)); + if (cfg_server_get_tls(s) != CFG_TLS_MAY) + fprintf(fp, " %s\n", + cfg_server_get_tls_str(s)); + if (cfg_server_get_tls_cipher_suite(s)) + fprintf(fp, " %s\n", + cfg_server_get_tls_cipher_suite(s)); + if (cfg_server_get_ca_dir(s)) + fprintf(fp, " %s\n", + cfg_server_get_ca_dir(s)); + if (cfg_server_get_ca_file(s)) + fprintf(fp, " %s\n", + cfg_server_get_ca_file(s)); + if (cfg_server_get_client_cert(s)) + fprintf(fp, " %s\n", + cfg_server_get_client_cert(s)); + if (cfg_server_get_reconnect_attempts(s)) + fprintf(fp, " %u\n", + cfg_server_get_reconnect_attempts(s)); + fprintf(fp, " \n"); +} + +static void +_cfgfile_xml_print_stream(cfg_stream_t s, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " \n"); + if (0 != strcasecmp(cfg_stream_get_name(s), CFG_DEFAULT)) + fprintf(fp, " %s\n", + cfg_stream_get_name(s)); + if (cfg_stream_get_mountpoint(s)) + fprintf(fp, " %s\n", + cfg_stream_get_mountpoint(s)); + if (cfg_stream_get_intake(s)) + fprintf(fp, " %s\n", + cfg_stream_get_intake(s)); + if (cfg_stream_get_server(s)) + fprintf(fp, " %s\n", + cfg_stream_get_server(s)); + if (cfg_stream_get_public(s)) + fprintf(fp, " yes\n"); + fprintf(fp, " %s\n", + cfg_stream_get_format_str(s)); + if (cfg_stream_get_encoder(s)) + fprintf(fp, " %s\n", + cfg_stream_get_encoder(s)); + if (cfg_stream_get_stream_name(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_name(s)); + if (cfg_stream_get_stream_url(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_url(s)); + if (cfg_stream_get_stream_genre(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_genre(s)); + if (cfg_stream_get_stream_description(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_description(s)); + if (cfg_stream_get_stream_quality(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_quality(s)); + if (cfg_stream_get_stream_bitrate(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_bitrate(s)); + if (cfg_stream_get_stream_samplerate(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_samplerate(s)); + if (cfg_stream_get_stream_channels(s)) + fprintf(fp, " %s\n", + cfg_stream_get_stream_channels(s)); + fprintf(fp, " \n"); +} + +static void +_cfgfile_xml_print_intake(cfg_intake_t i, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " \n"); + if (0 != strcasecmp(cfg_intake_get_name(i), CFG_DEFAULT)) + fprintf(fp, " %s\n", + cfg_intake_get_name(i)); + if (cfg_intake_get_type(i)) + fprintf(fp, " %s\n", + cfg_intake_get_type_str(i)); + if (cfg_intake_get_filename(i)) + fprintf(fp, " %s\n", + cfg_intake_get_filename(i)); + if (cfg_intake_get_shuffle(i)) + fprintf(fp, " yes\n"); + if (cfg_intake_get_stream_once(i)) + fprintf(fp, " yes\n"); + fprintf(fp, " \n"); +} + +static void +_cfgfile_xml_print_decoder_ext(const char *ext, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " %s\n", ext); +} + +static void +_cfgfile_xml_print_decoder(cfg_decoder_t d, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " \n"); + if (0 != strcasecmp(cfg_decoder_get_name(d), CFG_DEFAULT)) + fprintf(fp, " %s\n", + cfg_decoder_get_name(d)); + if (cfg_decoder_get_program(d)) + fprintf(fp, " %s\n", + cfg_decoder_get_program(d)); + cfg_decoder_ext_foreach(d, _cfgfile_xml_print_decoder_ext, fp); + fprintf(fp, " \n"); +} + +static void +_cfgfile_xml_print_encoder(cfg_encoder_t e, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " \n"); + if (0 != strcasecmp(cfg_encoder_get_name(e), CFG_DEFAULT)) + fprintf(fp, " %s\n", + cfg_encoder_get_name(e)); + fprintf(fp, " %s\n", + cfg_stream_fmt2str(cfg_encoder_get_format(e))); + if (cfg_encoder_get_program(e)) + fprintf(fp, " %s\n", + cfg_encoder_get_program(e)); + fprintf(fp, " \n"); +} + +void +cfgfile_xml_print(FILE *fp) +{ + fprintf(fp, "\n"); + fprintf(fp, "\n"); + fprintf(fp, " \n"); + cfg_server_list_foreach(cfg_get_servers(), _cfgfile_xml_print_server, + fp); + fprintf(fp, " \n"); + fprintf(fp, "\n"); + fprintf(fp, " \n"); + cfg_stream_list_foreach(cfg_get_streams(), _cfgfile_xml_print_stream, + fp); + fprintf(fp, " \n"); + fprintf(fp, "\n"); + fprintf(fp, " \n"); + cfg_intake_list_foreach(cfg_get_intakes(), _cfgfile_xml_print_intake, + fp); + fprintf(fp, " \n"); + if (cfg_decoder_list_nentries(cfg_get_decoders())) { + fprintf(fp, "\n"); + fprintf(fp, " \n"); + cfg_decoder_list_foreach(cfg_get_decoders(), + _cfgfile_xml_print_decoder, fp); + fprintf(fp, " \n"); + } + if (cfg_encoder_list_nentries(cfg_get_encoders())) { + fprintf(fp, "\n"); + fprintf(fp, " \n"); + cfg_encoder_list_foreach(cfg_get_encoders(), + _cfgfile_xml_print_encoder, fp); + fprintf(fp, " \n"); + } + if (cfg_get_metadata_program() || + cfg_get_metadata_format_str() || + 0 <= cfg_get_metadata_refresh_interval() || + cfg_get_metadata_normalize_strings() || + cfg_get_metadata_no_updates()) { + fprintf(fp, "\n"); + fprintf(fp, " \n"); + if (cfg_get_metadata_program()) + fprintf(fp, " %s\n", + cfg_get_metadata_program()); + if (cfg_get_metadata_format_str()) + fprintf(fp, " %s\n", + cfg_get_metadata_format_str()); + if (0 <= cfg_get_metadata_refresh_interval()) + fprintf(fp, " %d\n", + cfg_get_metadata_refresh_interval()); + if (cfg_get_metadata_normalize_strings()) + fprintf(fp, " yes\n"); + if (cfg_get_metadata_no_updates()) + fprintf(fp, " yes\n"); + fprintf(fp, " \n"); + } + fprintf(fp, "\n"); +} diff --git a/src/cfg_xmlfile.h b/src/cfgfile_xml.h similarity index 83% rename from src/cfg_xmlfile.h rename to src/cfgfile_xml.h index 6044f21..c67acc4 100644 --- a/src/cfg_xmlfile.h +++ b/src/cfgfile_xml.h @@ -14,9 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef __CFG_XMLFILE_H__ -#define __CFG_XMLFILE_H__ +#ifndef __CFGFILE_XML_H__ +#define __CFGFILE_XML_H__ -int cfg_xmlfile_parse(const char *); +int cfgfile_xml_parse(const char *); +void cfgfile_xml_print(FILE *); -#endif /* __CFG_XMLFILE_H__ */ +#endif /* __CFGFILE_XML_H__ */ diff --git a/src/ezconfig0.c b/src/ezconfig0.c new file mode 100644 index 0000000..335d4c3 --- /dev/null +++ b/src/ezconfig0.c @@ -0,0 +1,814 @@ +/* + * ezstream - source client for Icecast with external en-/decoder support + * Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski + * Copyright (C) 2007, 2009, 2015 Moritz Grimm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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-1307 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "compat.h" + +#include +#include +#include +#include + +#include "ezconfig0.h" +#include "log.h" +#include "util.h" + +#include + +#define XML_CHAR(s) (const xmlChar *)(s) +#define STD_CHAR(s) (const char *)(s) + +static EZCONFIG ezConfig; +static const char *blankString = ""; + +unsigned int checkDecoderLine(const char *, const char *, long); +unsigned int checkEncoderLine(const char *, const char *, long); +unsigned int checkFormatLine(const char *, const char *, long); + +EZCONFIG * +getEZConfig(void) +{ + return (&ezConfig); +} + +const char * +getFormatEncoder(const char *format) +{ + int i; + + for (i = 0; i < ezConfig.numEncoderDecoders; i++) { + if (ezConfig.encoderDecoders[i] != NULL && + ezConfig.encoderDecoders[i]->format != NULL && + strcmp(ezConfig.encoderDecoders[i]->format, format) == 0) { + if (ezConfig.encoderDecoders[i]->encoder != NULL) + return (ezConfig.encoderDecoders[i]->encoder); + else + return (blankString); + } + } + + return (blankString); +} + +const char * +getFormatDecoder(const char *match) +{ + int i; + + for (i = 0; i < ezConfig.numEncoderDecoders; i++) { + if (ezConfig.encoderDecoders[i] != NULL && + ezConfig.encoderDecoders[i]->match != NULL && + strcmp(ezConfig.encoderDecoders[i]->match, match) == 0) { + if (ezConfig.encoderDecoders[i]->decoder != NULL) + return (ezConfig.encoderDecoders[i]->decoder); + else + return (blankString); + } + } + + return (blankString); +} + +int +parseConfig(const char *fileName) +{ + xmlDocPtr doc; + xmlNodePtr cur; + xmlChar *ls_xmlContentPtr; + int program_set, reconnect_set, shuffle_set, + streamOnce_set, svrinfopublic_set, + refresh_set; + unsigned int config_error; + + xmlLineNumbersDefault(1); + if ((doc = xmlParseFile(fileName)) == NULL) { + log_error("%s: Parse error (not well-formed XML.)\n", fileName); + return (0); + } + + cur = xmlDocGetRootElement(doc); + + if (cur == NULL) { + log_error("%s: Parse error (empty XML document.)\n", fileName); + xmlFreeDoc(doc); + return (0); + } + + memset(&ezConfig, 0, sizeof(ezConfig)); + ezConfig.metadataRefreshInterval = -1; + + config_error = 0; + program_set = 0; + reconnect_set = 0; + refresh_set = 0; + shuffle_set = 0; + streamOnce_set = 0; + svrinfopublic_set = 0; + + for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { + if (!xmlStrcmp(cur->name, (const xmlChar *)"url")) { + if (ezConfig.URL != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.URL = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"sourceuser")) { + if (ezConfig.username != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.username = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"sourcepassword")) { + if (ezConfig.password != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.password = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"format")) { + if (ezConfig.format != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + char *p; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.format = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + for (p = ezConfig.format; *p != '\0'; p++) + *p = (char)toupper((int)*p); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"filename")) { + if (ezConfig.fileName != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (strlen(STD_CHAR(ls_xmlContentPtr)) > PATH_MAX - 1) { + log_error("%s[%ld]: Error: Path or filename in is too long\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + ezConfig.fileName = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"metadata_progname")) { + if (ezConfig.metadataProgram != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (strlen(STD_CHAR(ls_xmlContentPtr)) > PATH_MAX - 1) { + log_error("%s[%ld]: Error: Path or filename in is too long\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + ezConfig.metadataProgram = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"metadata_format")) { + if (ezConfig.metadataFormat != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + unsigned int ret; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.metadataFormat = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + if ((ret = checkFormatLine(ezConfig.metadataFormat, + fileName, xmlGetLineNo(cur))) + > 0) { + config_error += ret; + continue; + } + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"metadata_refreshinterval")) { + if (refresh_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + const char *errstr; + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.metadataRefreshInterval = (int)strtonum(STD_CHAR(ls_xmlContentPtr), -1LL, (long long)INT_MAX, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: In : '%s' is %s\n", + fileName, xmlGetLineNo(cur), STD_CHAR(ls_xmlContentPtr), errstr); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + refresh_set = 1; + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"playlist_program")) { + if (program_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + const char *errstr; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.fileNameIsProgram = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: may only contain 1 or 0\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + program_set = 1; + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"shuffle")) { + if (shuffle_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + const char *errstr; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.shuffle = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: may only contain 1 or 0\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + shuffle_set = 1; + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"stream_once")) { + if (streamOnce_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + const char *errstr; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.streamOnce = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: may only contain 1 or 0\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + streamOnce_set = 1; + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"reconnect_tries")) { + if (reconnect_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + const char *errstr; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.reconnectAttempts = (unsigned int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, (long long)UINT_MAX, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: In : '%s' is %s\n", + fileName, xmlGetLineNo(cur), STD_CHAR(ls_xmlContentPtr), errstr); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + reconnect_set = 1; + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfoname")) { + if (ezConfig.serverName != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverName = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfourl")) { + if (ezConfig.serverURL != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverURL = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfogenre")) { + if (ezConfig.serverGenre != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverGenre = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfodescription")) { + if (ezConfig.serverDescription != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverDescription = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfobitrate")) { + if (ezConfig.serverBitrate != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverBitrate = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfochannels")) { + if (ezConfig.serverChannels != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverChannels = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfosamplerate")) { + if (ezConfig.serverSamplerate != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverSamplerate = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfoquality")) { + if (ezConfig.serverQuality != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverQuality = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfopublic")) { + if (svrinfopublic_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur->xmlChildrenNode != NULL) { + const char *errstr; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + ezConfig.serverPublic = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: may only contain 1 or 0\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + svrinfopublic_set = 1; + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)"reencode")) { + xmlNodePtr cur2; + int enable_set; + + enable_set = 0; + for (cur2 = cur->xmlChildrenNode; cur2 != NULL; + cur2 = cur2->next) { + if (!xmlStrcmp(cur2->name, (const xmlChar *)"enable")) { + if (enable_set) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + if (cur2->xmlChildrenNode != NULL) { + const char *errstr; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur2->xmlChildrenNode, 1); + ezConfig.reencode = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); + if (errstr) { + log_error("%s[%ld]: Error: may only contain 1 or 0\n", + fileName, xmlGetLineNo(cur)); + config_error++; + continue; + } + xmlFree(ls_xmlContentPtr); + enable_set = 1; + } + } + if (!xmlStrcmp(cur2->name, (const xmlChar *)"encdec")) { + xmlNodePtr cur3; + FORMAT_ENCDEC *pformatEncDec; + + pformatEncDec = calloc(1UL, sizeof(FORMAT_ENCDEC)); + + for (cur3 = cur2->xmlChildrenNode; + cur3 != NULL; cur3 = cur3->next) { + if (!xmlStrcmp(cur3->name, (const xmlChar *)"format")) { + if (pformatEncDec->format != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur3)); + config_error++; + continue; + } + if (cur3->xmlChildrenNode != NULL) { + char *p; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); + pformatEncDec->format = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + for (p = pformatEncDec->format; *p != '\0'; p++) + *p = (char)toupper((int)*p); + } + } + if (!xmlStrcmp(cur3->name, (const xmlChar *)"match")) { + if (pformatEncDec->match != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur3)); + config_error++; + continue; + } + if (cur3->xmlChildrenNode != NULL) { + char *p; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); + pformatEncDec->match = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + for (p = pformatEncDec->match; *p != '\0'; p++) + *p = (char)tolower((int)*p); + } + } + if (!xmlStrcmp(cur3->name, (const xmlChar *)"decode")) { + if (pformatEncDec->decoder != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur3)); + config_error++; + continue; + } + if (cur3->xmlChildrenNode != NULL) { + unsigned int ret; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); + pformatEncDec->decoder = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + if ((ret = checkDecoderLine(pformatEncDec->decoder, + fileName, xmlGetLineNo(cur3))) + > 0) { + config_error += ret; + continue; + } + } + } + if (!xmlStrcmp(cur3->name, (const xmlChar *)"encode")) { + if (pformatEncDec->encoder != NULL) { + log_error("%s[%ld]: Error: Cannot have multiple elements\n", + fileName, xmlGetLineNo(cur3)); + config_error++; + continue; + } + if (cur3->xmlChildrenNode != NULL) { + unsigned int ret; + + ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); + pformatEncDec->encoder = strdup(STD_CHAR(ls_xmlContentPtr)); + xmlFree(ls_xmlContentPtr); + if ((ret = checkEncoderLine(pformatEncDec->encoder, + fileName, xmlGetLineNo(cur3))) + > 0) { + config_error += ret; + continue; + } + } + } + } + ezConfig.encoderDecoders[ezConfig.numEncoderDecoders] = pformatEncDec; + ezConfig.numEncoderDecoders++; + } + } + } + } + + xmlFreeDoc(doc); + + if (config_error == 0) + return (1); + + freeConfig(&ezConfig); + log_error("%u configuration error(s) in %s\n", + config_error, fileName); + + return (0); +} + +void +freeConfig(EZCONFIG *cfg) +{ + unsigned int i; + + if (cfg == NULL) + return; + + if (cfg->URL != NULL) + free(cfg->URL); + if (cfg->password != NULL) + free(cfg->password); + if (cfg->format != NULL) + free(cfg->format); + if (cfg->fileName != NULL) + free(cfg->fileName); + if (cfg->metadataProgram != NULL) + free(cfg->metadataProgram); + if (cfg->metadataFormat != NULL) + free(cfg->metadataFormat); + if (cfg->serverName != NULL) + free(cfg->serverName); + if (cfg->serverURL != NULL) + free(cfg->serverURL); + if (cfg->serverGenre != NULL) + free(cfg->serverGenre); + if (cfg->serverDescription != NULL) + free(cfg->serverDescription); + if (cfg->serverBitrate != NULL) + free(cfg->serverBitrate); + if (cfg->serverChannels != NULL) + free(cfg->serverChannels); + if (cfg->serverSamplerate != NULL) + free(cfg->serverSamplerate); + if (cfg->serverQuality != NULL) + free(cfg->serverQuality); + if (cfg->encoderDecoders != NULL) { + for (i = 0; i < MAX_FORMAT_ENCDEC; i++) { + if (cfg->encoderDecoders[i] != NULL) { + if (cfg->encoderDecoders[i]->format != NULL) + free(cfg->encoderDecoders[i]->format); + if (cfg->encoderDecoders[i]->match != NULL) + free(cfg->encoderDecoders[i]->match); + if (cfg->encoderDecoders[i]->encoder != NULL) + free(cfg->encoderDecoders[i]->encoder); + if (cfg->encoderDecoders[i]->decoder != NULL) + free(cfg->encoderDecoders[i]->decoder); + free(cfg->encoderDecoders[i]); + } + } + } + + memset(cfg, 0, sizeof(EZCONFIG)); +} + +unsigned int +checkDecoderLine(const char *str, const char *file, long line) +{ + unsigned int errors; + char *p; + int have_track = 0; + + errors = 0; + if ((p = strstr(str, STRING_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: `%s' placeholder not allowed in decoder command\n", + file, line, STRING_PLACEHOLDER); + errors++; + } + if ((p = strstr(str, TRACK_PLACEHOLDER)) != NULL) { + p += strlen(TRACK_PLACEHOLDER); + if ((p = strstr(p, TRACK_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", + file, line, TRACK_PLACEHOLDER); + errors++; + } else + have_track = 1; + } + if ((p = strstr(str, METADATA_PLACEHOLDER)) != NULL) { + p += strlen(METADATA_PLACEHOLDER); + if ((p = strstr(p, METADATA_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", + file, line, METADATA_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, ARTIST_PLACEHOLDER)) != NULL) { + p += strlen(ARTIST_PLACEHOLDER); + if ((p = strstr(p, ARTIST_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", + file, line, ARTIST_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, TITLE_PLACEHOLDER)) != NULL) { + p += strlen(TITLE_PLACEHOLDER); + if ((p = strstr(p, TITLE_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", + file, line, TITLE_PLACEHOLDER); + errors++; + } + } + + if (!have_track) { + log_error("%s[%ld]: Error: The decoder command requires the '%s' track placeholder\n", + file, line, TRACK_PLACEHOLDER); + errors++; + } + + return (errors); +} + +unsigned int +checkEncoderLine(const char *str, const char *file, long line) +{ + unsigned int errors; + char *p; + + errors = 0; + if ((p = strstr(str, TRACK_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: `%s' placeholder not allowed in encoder command\n", + file, line, TRACK_PLACEHOLDER); + errors++; + } + if ((p = strstr(str, STRING_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: `%s' placeholder not allowed in encoder command\n", + file, line, STRING_PLACEHOLDER); + errors++; + } + if ((p = strstr(str, METADATA_PLACEHOLDER)) != NULL) { + p += strlen(METADATA_PLACEHOLDER); + if ((p = strstr(p, METADATA_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in encoder command\n", + file, line, METADATA_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, ARTIST_PLACEHOLDER)) != NULL) { + p += strlen(ARTIST_PLACEHOLDER); + if ((p = strstr(p, ARTIST_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in encoder command\n", + file, line, ARTIST_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, TITLE_PLACEHOLDER)) != NULL) { + p += strlen(TITLE_PLACEHOLDER); + if ((p = strstr(p, TITLE_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in encoder command\n", + file, line, TITLE_PLACEHOLDER); + errors++; + } + } + + return (errors); +} + +unsigned int +checkFormatLine(const char *str, const char *file, long line) +{ + unsigned int errors; + char *p; + + errors = 0; + if ((p = strstr(str, METADATA_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: `%s' placeholder not allowed in \n", + file, line, METADATA_PLACEHOLDER); + errors++; + } + if ((p = strstr(str, TRACK_PLACEHOLDER)) != NULL) { + p += strlen(TRACK_PLACEHOLDER); + if ((p = strstr(p, TRACK_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", + file, line, TRACK_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, STRING_PLACEHOLDER)) != NULL) { + p += strlen(STRING_PLACEHOLDER); + if ((p = strstr(p, STRING_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", + file, line, STRING_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, ARTIST_PLACEHOLDER)) != NULL) { + p += strlen(ARTIST_PLACEHOLDER); + if ((p = strstr(p, ARTIST_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", + file, line, ARTIST_PLACEHOLDER); + errors++; + } + } + if ((p = strstr(str, TITLE_PLACEHOLDER)) != NULL) { + p += strlen(TITLE_PLACEHOLDER); + if ((p = strstr(p, TITLE_PLACEHOLDER)) != NULL) { + log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", + file, line, TITLE_PLACEHOLDER); + errors++; + } + } + + return (errors); +} diff --git a/src/ezconfig0.h b/src/ezconfig0.h new file mode 100644 index 0000000..a026d88 --- /dev/null +++ b/src/ezconfig0.h @@ -0,0 +1,75 @@ +/* + * ezstream - source client for Icecast with external en-/decoder support + * Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski + * Copyright (C) 2007, 2015 Moritz Grimm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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-1307 USA + */ + +#ifndef __EZCONFIG0_H__ +#define __EZCONFIG0_H__ + +#define MP3_FORMAT "MP3" +#define VORBIS_FORMAT "VORBIS" +#define THEORA_FORMAT "THEORA" + +#define MAX_FORMAT_ENCDEC 15 + +#define TRACK_PLACEHOLDER "@T@" +#define METADATA_PLACEHOLDER "@M@" +#define ARTIST_PLACEHOLDER "@a@" +#define TITLE_PLACEHOLDER "@t@" +#define STRING_PLACEHOLDER "@s@" + +typedef struct tag_FORMAT_ENCDEC { + char *format; + char *match; + char *encoder; + char *decoder; +} FORMAT_ENCDEC; + +typedef struct tag_EZCONFIG { + char *URL; + char *username; + char *password; + char *format; + char *fileName; + char *metadataProgram; + char *metadataFormat; + char *serverName; + char *serverURL; + char *serverGenre; + char *serverDescription; + char *serverBitrate; + char *serverChannels; + char *serverSamplerate; + char *serverQuality; + int serverPublic; + int reencode; + FORMAT_ENCDEC *encoderDecoders[MAX_FORMAT_ENCDEC]; + int numEncoderDecoders; + int shuffle; + int fileNameIsProgram; + int streamOnce; + unsigned int reconnectAttempts; + int metadataRefreshInterval; +} EZCONFIG; + +EZCONFIG * getEZConfig(void); +const char * getFormatEncoder(const char *format); +const char * getFormatDecoder(const char *match); +int parseConfig(const char *fileName); +void freeConfig(EZCONFIG *); + +#endif /* __EZCONFIG0_H__ */ diff --git a/src/ezstream-cfgmigrate.c b/src/ezstream-cfgmigrate.c new file mode 100644 index 0000000..e50eaaf --- /dev/null +++ b/src/ezstream-cfgmigrate.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2018 Moritz Grimm + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "compat.h" + +#include +#include +#include +#include + +#include "cfg.h" +#include "cfgfile_xml.h" +#include "ezconfig0.h" +#include "log.h" +#include "util.h" +#include "xalloc.h" + +static char *v0_cfgfile; +static unsigned int cfg_verbosity; + +#define OPTSTRING "0:hv" +enum opt_vals { + OPT_V0CONFIGFILE = '0', + OPT_HELP = 'h', + OPT_VERBOSE = 'v', + OPT_INVALID = '?' +}; + +static void _usage(void); +static void _usage_help(void); +static int _cmdline_parse(int, char *[], int *); +static int _url_parse(const char *, char **, char **, char **); +static int _parse_ezconfig0(EZCONFIG *); + +static void +_usage(void) +{ + fprintf(stderr, "usage: %s [-hv] -0 v0-cfgfile\n", + util_get_progname(NULL)); +} + +static void +_usage_help(void) +{ + fprintf(stderr, "\n"); + fprintf(stderr, " -0 v0-cfgfile migrate from v0-cfgfile (ezstream version 0.x)\n"); + fprintf(stderr, " -h print this help and exit\n"); + fprintf(stderr, " -v increase logging verbosity\n"); +} + +static int +_cmdline_parse(int argc, char *argv[], int *ret_p) +{ + v0_cfgfile = NULL; + cfg_verbosity = 0; + optind = 1; + for (;;) { + int ch; + + ch = getopt(argc, argv, OPTSTRING); + if (0 > ch) + break; + + switch (ch) { + case OPT_V0CONFIGFILE: + v0_cfgfile = optarg; + break; + case OPT_HELP: + _usage(); + _usage_help(); + *ret_p = 0; + return (-1); + case OPT_VERBOSE: + cfg_verbosity++; + break; + case OPT_INVALID: + default: + _usage(); + *ret_p = 2; + return (-1); + } + } + argc -= optind; + argv += optind; + + if (!v0_cfgfile) { + fprintf(stderr, "-%c must be provided\n", + OPT_V0CONFIGFILE); + _usage(); + *ret_p = 2; + return (-1); + } + + return (0); +} + +static int +_url_parse(const char *url, char **hostname, char **port, + char **mountname) +{ + const char *p1, *p2, *p3; + size_t hostsiz, portsiz, mountsiz; + + if (strncasecmp(url, "http://", strlen("http://")) != 0) { + log_error("%s: Invalid : Not an HTTP address", + v0_cfgfile); + return (-1); + } + + p1 = url + strlen("http://"); + p2 = strchr(p1, ':'); + if (p2 == NULL) { + log_error("%s: Invalid : Missing port", v0_cfgfile); + return (-1); + } + hostsiz = (size_t)(p2 - p1) + 1; + *hostname = xmalloc(hostsiz); + strlcpy(*hostname, p1, hostsiz); + + p2++; + p3 = strchr(p2, '/'); + if (p3 == NULL) { + log_error("%s: Invalid : Missing mountpoint or too long port number", + v0_cfgfile); + xfree(*hostname); + return (-1); + } + + portsiz = (size_t)(p3 - p2) + 1; + *port = xmalloc(portsiz); + strlcpy(*port, p2, portsiz); + + mountsiz = strlen(p3) + 1; + *mountname = xmalloc(mountsiz); + strlcpy(*mountname, p3, mountsiz); + + return (0); +} + +#define ENTITY_SET(e, el, func, ctx, val) do { \ + const char *err_str2; \ + \ + if (0 > (func)((e), (el), (val), &err_str2)) { \ + log_warning("%s: %s: %s: %s", v0_cfgfile, (ctx), \ + err_str2, (val)); \ + warnings++; \ + } \ +} while (0) + +static int +_parse_ezconfig0(EZCONFIG *ez) +{ + char *hostname, *port, *mountname; + const char *err_str; + int warnings = 0; + cfg_server_list_t srv_list = cfg_get_servers(); + cfg_server_t srv = cfg_server_list_get(srv_list, CFG_DEFAULT); + cfg_stream_list_t str_list = cfg_get_streams(); + cfg_stream_t str = cfg_stream_list_get(str_list, CFG_DEFAULT); + cfg_intake_list_t in_list = cfg_get_intakes(); + cfg_intake_t in = cfg_intake_list_get(in_list, CFG_DEFAULT); + cfg_encoder_list_t enc_list = cfg_get_encoders(); + cfg_decoder_list_t dec_list = cfg_get_decoders();; + unsigned int i; + char strbuf[BUFSIZ]; + + if (NULL == ez->URL || 0 == strlen(ez->URL)) { + log_error("%s: Missing -- not an ezstream version 0.x configuration?", + v0_cfgfile); + return (-1); + } + if (0 > _url_parse(ez->URL, &hostname, &port, &mountname)) + return (-1); + ENTITY_SET(srv, srv_list, cfg_server_set_protocol, "", "HTTP"); + ENTITY_SET(srv, srv_list, cfg_server_set_hostname, "", hostname); + xfree(hostname); + ENTITY_SET(srv, srv_list, cfg_server_set_port, "", port); + xfree(port); + ENTITY_SET(str, str_list, cfg_stream_set_mountpoint, "", + mountname); + xfree(mountname); + + if (ez->username) + ENTITY_SET(srv, srv_list, cfg_server_set_user, + "", ez->username); + if (ez->password) + ENTITY_SET(srv, srv_list, cfg_server_set_password, + "", ez->password); + if (ez->format) + ENTITY_SET(str, str_list, cfg_stream_set_format, + "", ez->format); + if (ez->fileName) { + if (0 == strcasecmp(ez->fileName, "stdin")) + ENTITY_SET(in, in_list, cfg_intake_set_type, + "", "stdin"); + else + ENTITY_SET(in, in_list, cfg_intake_set_filename, + "", ez->fileName); + } + if (ez->metadataProgram) { + if (0 > cfg_set_metadata_program(ez->metadataProgram, + &err_str)) { + log_warning("%s: %s: %s: %s", v0_cfgfile, + "", err_str, + ez->metadataProgram); + warnings++; + } + } + if (ez->metadataFormat) { + if (0 > cfg_set_metadata_format_str(ez->metadataFormat, + &err_str)) { + log_warning("%s: %s: %s: %s", v0_cfgfile, + "", err_str, ez->metadataFormat); + warnings++; + } + } + if (ez->metadataRefreshInterval) { + snprintf(strbuf, sizeof(strbuf), "%d", + ez->metadataRefreshInterval); + if (0 > cfg_set_metadata_refresh_interval(strbuf, &err_str)) { + log_warning("%s: %s: %s: %s", v0_cfgfile, + "", err_str, strbuf); + warnings++; + } + } + if (ez->fileNameIsProgram) + ENTITY_SET(in, in_list, cfg_intake_set_type, + "playlist_program", "program"); + snprintf(strbuf, sizeof(strbuf), "%d", ez->shuffle); + ENTITY_SET(in, in_list, cfg_intake_set_shuffle, + "", strbuf); + snprintf(strbuf, sizeof(strbuf), "%d", ez->streamOnce); + ENTITY_SET(in, in_list, cfg_intake_set_stream_once, + "", strbuf); + snprintf(strbuf, sizeof(strbuf), "%u", ez->reconnectAttempts); + ENTITY_SET(srv, srv_list, cfg_server_set_reconnect_attempts, + "", strbuf); + if (ez->serverName) + ENTITY_SET(str, str_list, cfg_stream_set_stream_name, + "", ez->serverName); + if (ez->serverURL) + ENTITY_SET(str, str_list, cfg_stream_set_stream_url, + "", ez->serverURL); + if (ez->serverGenre) + ENTITY_SET(str, str_list, cfg_stream_set_stream_genre, + "", ez->serverGenre); + if (ez->serverDescription) + ENTITY_SET(str, str_list, cfg_stream_set_stream_description, + "", ez->serverDescription); + if (ez->serverBitrate) + ENTITY_SET(str, str_list, cfg_stream_set_stream_bitrate, + "", ez->serverBitrate); + if (ez->serverChannels) + ENTITY_SET(str, str_list, cfg_stream_set_stream_channels, + "", ez->serverChannels); + if (ez->serverSamplerate) + ENTITY_SET(str, str_list, cfg_stream_set_stream_samplerate, + "", ez->serverSamplerate); + if (ez->serverQuality) + ENTITY_SET(str, str_list, cfg_stream_set_stream_quality, + "", ez->serverQuality); + ENTITY_SET(str, str_list, cfg_stream_set_public, + "", ez->serverPublic ? "yes" : "no"); + if (ez->reencode) + ENTITY_SET(str, str_list, cfg_stream_set_encoder, "", + ez->format); + + for (i = 0; i < MAX_FORMAT_ENCDEC; i++) { + FORMAT_ENCDEC *ed = ez->encoderDecoders[i]; + + if (!ed) + continue; + + if (ed->encoder) { + cfg_encoder_t enc = cfg_encoder_list_get(enc_list, + CFG_DEFAULT); + + ENTITY_SET(enc, enc_list, cfg_encoder_set_program, + "", ed->encoder); + if (ed->format) { + ENTITY_SET(enc, enc_list, cfg_encoder_set_name, + " (encoder)", ed->format); + ENTITY_SET(enc, enc_list, cfg_encoder_set_format_str, + " (encoder)", ed->format); + } + if (0 > cfg_encoder_validate(enc, &err_str)) { + log_warning("%s: %s: %s", v0_cfgfile, + " (encoder)", err_str); + cfg_encoder_list_remove(enc_list, &enc); + warnings++; + } + } else { + if (ed->format && + 0 == strcasecmp(ed->format, "THEORA")) { + cfg_encoder_t enc = NULL; + + enc = cfg_encoder_list_find(enc_list, + ed->format); + if (NULL == enc) + enc = cfg_encoder_list_get(enc_list, + CFG_DEFAULT); + ENTITY_SET(enc, enc_list, cfg_encoder_set_name, + " (encoder)", ed->format); + ENTITY_SET(enc, enc_list, cfg_encoder_set_format_str, + " (encoder)", ed->format); + if (0 > cfg_encoder_validate(enc, &err_str)) { + log_warning("%s: %s: %s", v0_cfgfile, + " (encoder)", err_str); + cfg_encoder_list_remove(enc_list, &enc); + warnings++; + } + } + } + if (ed->decoder) { + cfg_decoder_t dec = NULL; + + if (ed->format) + dec = cfg_decoder_list_find(dec_list, ed->format); + if (NULL == dec) + dec = cfg_decoder_list_get(dec_list, CFG_DEFAULT); + + ENTITY_SET(dec, dec_list, cfg_decoder_set_program, + "", ed->decoder); + if (ed->format) + ENTITY_SET(dec, dec_list, cfg_decoder_set_name, + " (decoder)", ed->format); + if (ed->match) + ENTITY_SET(dec, dec_list, cfg_decoder_add_match, + "", ed->match); + + if (0 > cfg_decoder_validate(dec, &err_str)) { + log_warning("%s: %s: %s", v0_cfgfile, + " (decoder)", err_str); + cfg_decoder_list_remove(dec_list, &dec); + warnings++; + } + } + } + + if (warnings) + log_warning("%s: %u warnings", v0_cfgfile, warnings); + + if (cfg_stream_get_encoder(str) && + NULL == cfg_encoder_list_find(enc_list, + cfg_stream_get_encoder(str))) { + log_error("%s: %s encoder not found due to errors", + v0_cfgfile, cfg_stream_get_encoder(str)); + return (-1); + } + + if (0 > cfg_server_validate(srv, &err_str) || + 0 > cfg_stream_validate(str, &err_str) || + 0 > cfg_intake_validate(in, &err_str)) { + log_error("%s: configuration invalid: %s", v0_cfgfile, + err_str); + return (-1); + } + + return (0); +} + +int +main(int argc, char *argv[]) +{ + int ret; + EZCONFIG *ez; + + ret = 1; + if (0 > cfg_init() || + 0 > log_init(util_get_progname(argv[0])) || + 0 > _cmdline_parse(argc, argv, &ret)) + return (ret); + (void)cfg_set_program_name(util_get_progname(argv[0]), NULL); + + if (0 > parseConfig(v0_cfgfile)) + goto error; + ez = getEZConfig(); + + if (0 > _parse_ezconfig0(ez)) + goto error; + + printf("\n\n"); + printf("\n\n"); + cfgfile_xml_print(stdout); + + freeConfig(ez); + + log_exit(); + cfg_exit(); + + return (0); + +error: + if (ez) + freeConfig(ez); + + log_exit(); + cfg_exit(); + + return (1); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index ce25a1b..932b0b5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -7,7 +7,7 @@ TESTS = \ check_cfg_intake \ check_cfg_server \ check_cfg_stream \ - check_cfg_xmlfile \ + check_cfgfile_xml \ check_cmdline \ check_log \ check_mdata \ @@ -43,9 +43,9 @@ check_cfg_stream_SOURCES = check_cfg_stream.c check_cfg_stream_DEPENDENCIES = $(top_builddir)/src/libezstream.la check_cfg_stream_LDADD = $(check_cfg_stream_DEPENDENCIES) @CHECK_LIBS@ -check_cfg_xmlfile_SOURCES = check_cfg_xmlfile.c -check_cfg_xmlfile_DEPENDENCIES = $(top_builddir)/src/libezstream.la -check_cfg_xmlfile_LDADD = $(check_cfg_xmlfile_DEPENDENCIES) @CHECK_LIBS@ +check_cfgfile_xml_SOURCES = check_cfgfile_xml.c +check_cfgfile_xml_DEPENDENCIES = $(top_builddir)/src/libezstream.la +check_cfgfile_xml_LDADD = $(check_cfgfile_xml_DEPENDENCIES) @CHECK_LIBS@ check_cmdline_SOURCES = check_cmdline.c check_cmdline_DEPENDENCIES = $(top_builddir)/src/libezstream.la diff --git a/tests/check_cfg_decoder.c b/tests/check_cfg_decoder.c index ef0fc9b..8fe0c30 100644 --- a/tests/check_cfg_decoder.c +++ b/tests/check_cfg_decoder.c @@ -7,15 +7,36 @@ #include "check_cfg.h" +static void _d_cb(const char *, void *); +static void _dl_cb(cfg_decoder_t, void *); + Suite * cfg_suite(void); void setup_checked(void); void teardown_checked(void); cfg_decoder_list_t decoders; +static void +_d_cb(const char *ext, void *unused) +{ + (void)unused; + ck_assert_str_ne(ext, ""); +} + +static void +_dl_cb(cfg_decoder_t dec, void *arg) +{ + int *count = (int *)arg; + + ck_assert_ptr_ne(cfg_decoder_get_name(dec), NULL); + cfg_decoder_ext_foreach(dec, _d_cb, NULL); + (*count)++; +} + START_TEST(test_decoder_list_get) { cfg_decoder_t dec, dec2; + int count = 0; ck_assert_ptr_eq(cfg_decoder_list_get(decoders, NULL), NULL); ck_assert_ptr_eq(cfg_decoder_list_get(decoders, ""), NULL); @@ -23,6 +44,12 @@ START_TEST(test_decoder_list_get) dec = cfg_decoder_list_get(decoders, "TeSt"); dec2 = cfg_decoder_list_get(decoders, "test"); ck_assert_ptr_eq(dec, dec2); + + (void)cfg_decoder_list_get(decoders, "test2"); + ck_assert_int_eq(cfg_decoder_add_match(dec, decoders, ".test", NULL), 0); + ck_assert_uint_eq(cfg_decoder_list_nentries(decoders), 2); + cfg_decoder_list_foreach(decoders, _dl_cb, &count); + ck_assert_int_eq(count, 2); } END_TEST diff --git a/tests/check_cfg_encoder.c b/tests/check_cfg_encoder.c index ee1c4d0..5ea1abe 100644 --- a/tests/check_cfg_encoder.c +++ b/tests/check_cfg_encoder.c @@ -7,15 +7,27 @@ #include "check_cfg.h" +static void _el_cb(cfg_encoder_t, void *); + Suite * cfg_suite(void); void setup_checked(void); void teardown_checked(void); cfg_encoder_list_t encoders; +static void +_el_cb(cfg_encoder_t enc, void *arg) +{ + int *count = (int *)arg; + + ck_assert_ptr_ne(cfg_encoder_get_name(enc), NULL); + (*count)++; +} + START_TEST(test_encoder_list_get) { cfg_encoder_t enc, enc2; + int count = 0; ck_assert_ptr_eq(cfg_encoder_list_get(encoders, NULL), NULL); ck_assert_ptr_eq(cfg_encoder_list_get(encoders, ""), NULL); @@ -23,6 +35,11 @@ START_TEST(test_encoder_list_get) enc = cfg_encoder_list_get(encoders, "TeSt"); enc2 = cfg_encoder_list_get(encoders, "test"); ck_assert_ptr_eq(enc, enc2); + + (void)cfg_encoder_list_get(encoders, "test2"); + ck_assert_uint_eq(cfg_encoder_list_nentries(encoders), 2); + cfg_encoder_list_foreach(encoders, _el_cb, &count); + ck_assert_int_eq(count, 2); } END_TEST diff --git a/tests/check_cfg_intake.c b/tests/check_cfg_intake.c index 6dc44c9..afc8f57 100644 --- a/tests/check_cfg_intake.c +++ b/tests/check_cfg_intake.c @@ -7,15 +7,27 @@ #include "check_cfg.h" +static void _il_cb(cfg_intake_t, void *); + Suite * cfg_suite(void); void setup_checked(void); void teardown_checked(void); cfg_intake_list_t intakes; +static void +_il_cb(cfg_intake_t in, void *arg) +{ + int *count = (int *)arg; + + ck_assert_ptr_ne(cfg_intake_get_name(in), NULL); + (*count)++; +} + START_TEST(test_intake_list_get) { cfg_intake_t in, in2; + int count = 0; ck_assert_ptr_eq(cfg_intake_list_get(intakes, NULL), NULL); ck_assert_ptr_eq(cfg_intake_list_get(intakes, ""), NULL); @@ -23,6 +35,11 @@ START_TEST(test_intake_list_get) in = cfg_intake_list_get(intakes, "TeSt"); in2 = cfg_intake_list_get(intakes, "test"); ck_assert_ptr_eq(in, in2); + + (void)cfg_intake_list_get(intakes, "test2"); + ck_assert_uint_eq(cfg_intake_list_nentries(intakes), 2); + cfg_intake_list_foreach(intakes, _il_cb, &count); + ck_assert_int_eq(count, 2); } END_TEST @@ -50,15 +67,20 @@ START_TEST(test_intake_set_type) ck_assert_int_eq(cfg_intake_set_type(in, intakes, "aUtOdEtEcT", NULL), 0); ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_AUTODETECT); + ck_assert_str_eq(cfg_intake_get_type_str(in), "autodetect"); ck_assert_int_eq(cfg_intake_set_type(in, intakes, "FiLe", NULL), 0); ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_FILE); + ck_assert_str_eq(cfg_intake_get_type_str(in), "file"); ck_assert_int_eq(cfg_intake_set_type(in, intakes, "pLaYlIsT", NULL), 0); ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_PLAYLIST); + ck_assert_str_eq(cfg_intake_get_type_str(in), "playlist"); ck_assert_int_eq(cfg_intake_set_type(in, intakes, "PrOgRaM", NULL), 0); ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_PROGRAM); + ck_assert_str_eq(cfg_intake_get_type_str(in), "program"); ck_assert_int_eq(cfg_intake_set_type(in, intakes, "sTdIn", NULL), 0); ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_STDIN); + ck_assert_str_eq(cfg_intake_get_type_str(in), "stdin"); } END_TEST diff --git a/tests/check_cfg_server.c b/tests/check_cfg_server.c index 517c943..d7f399d 100644 --- a/tests/check_cfg_server.c +++ b/tests/check_cfg_server.c @@ -7,15 +7,27 @@ #include "check_cfg.h" +static void _sl_cb(cfg_server_t, void *); + Suite * cfg_suite(void); void setup_checked(void); void teardown_checked(void); cfg_server_list_t servers; +static void +_sl_cb(cfg_server_t srv, void *arg) +{ + int *count = (int *)arg; + + ck_assert_ptr_ne(cfg_server_get_name(srv), NULL); + (*count)++; +} + START_TEST(test_server_list_get) { cfg_server_t srv, srv2; + int count = 0; ck_assert_ptr_eq(cfg_server_list_get(servers, NULL), NULL); ck_assert_ptr_eq(cfg_server_list_get(servers, ""), NULL); @@ -23,6 +35,11 @@ START_TEST(test_server_list_get) srv = cfg_server_list_get(servers, "TeSt"); srv2 = cfg_server_list_get(servers, "test"); ck_assert_ptr_eq(srv, srv2); + + (void)cfg_server_list_get(servers, "test2"); + ck_assert_uint_eq(cfg_server_list_nentries(servers), 2); + cfg_server_list_foreach(servers, _sl_cb, &count); + ck_assert_int_eq(count, 2); } END_TEST @@ -65,7 +82,7 @@ START_TEST(test_server_port) cfg_server_t srv = cfg_server_list_get(servers, "test_server_port"); const char *errstr2; - ck_assert_uint_eq(cfg_server_get_port(srv), DEFAULT_PORT); + ck_assert_uint_eq(cfg_server_get_port(srv), CFG_SERVER_DEFAULT_PORT); TEST_EMPTYSTR_T(cfg_server_t, cfg_server_list_get, servers, cfg_server_set_port); @@ -89,7 +106,7 @@ START_TEST(test_server_user) { cfg_server_t srv = cfg_server_list_get(servers, "test_server_user"); - ck_assert_str_eq(cfg_server_get_user(srv), DEFAULT_USER); + ck_assert_str_eq(cfg_server_get_user(srv), CFG_SERVER_DEFAULT_USER); TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers, cfg_server_set_user, cfg_server_get_user, UCREDS_SIZE); } @@ -123,11 +140,14 @@ START_TEST(test_server_tls) ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_MAY); ck_assert_int_eq(cfg_server_set_tls(srv, servers, "None", NULL), 0); ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_NONE); + ck_assert_str_eq(cfg_server_get_tls_str(srv), "none"); ck_assert_int_eq(cfg_server_set_tls(srv, servers, "Required", NULL), 0); ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_REQUIRED); + ck_assert_str_eq(cfg_server_get_tls_str(srv), "required"); ck_assert_int_eq(cfg_server_set_tls(srv, servers, "May", NULL), 0); ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_MAY); + ck_assert_str_eq(cfg_server_get_tls_str(srv), "may"); } END_TEST diff --git a/tests/check_cfg_stream.c b/tests/check_cfg_stream.c index d3104cc..b3e5aef 100644 --- a/tests/check_cfg_stream.c +++ b/tests/check_cfg_stream.c @@ -7,15 +7,27 @@ #include "check_cfg.h" +static void _sl_cb(cfg_stream_t, void *); + Suite * cfg_suite(void); void setup_checked(void); void teardown_checked(void); cfg_stream_list_t streams; +static void +_sl_cb(cfg_stream_t str, void *arg) +{ + int *count = (int *)arg; + + ck_assert_ptr_ne(cfg_stream_get_name(str), NULL); + (*count)++; +} + START_TEST(test_stream_list_get) { cfg_stream_t str, str2; + int count = 0; ck_assert_ptr_eq(cfg_stream_list_get(streams, NULL), NULL); ck_assert_ptr_eq(cfg_stream_list_get(streams, ""), NULL); @@ -23,6 +35,11 @@ START_TEST(test_stream_list_get) str = cfg_stream_list_get(streams, "TeSt"); str2 = cfg_stream_list_get(streams, "test"); ck_assert_ptr_eq(str, str2); + + (void)cfg_stream_list_get(streams, "test2"); + ck_assert_uint_eq(cfg_stream_list_nentries(streams), 2); + cfg_stream_list_foreach(streams, _sl_cb, &count); + ck_assert_int_eq(count, 2); } END_TEST diff --git a/tests/check_cfg_xmlfile.c b/tests/check_cfgfile_xml.c similarity index 63% rename from tests/check_cfg_xmlfile.c rename to tests/check_cfgfile_xml.c index c7336c3..2482244 100644 --- a/tests/check_cfg_xmlfile.c +++ b/tests/check_cfgfile_xml.c @@ -1,29 +1,61 @@ +#include +#include + +#include +#include +#include + #include #include "cfg_private.h" +#include "cfgfile_xml.h" #include "log.h" +static void _test_cfgfile_rw(const char *cfgfile); + Suite * cfg_xmlfile_suite(void); void setup_checked(void); void teardown_checked(void); +static void +_test_cfgfile_rw(const char *cfgfile) +{ + const char *testfile = BUILDDIR "/check_cfgfile_xml.xml"; + FILE *fp; + int fd; + int ret; + + ck_assert_int_eq(cfg_set_program_config_file(cfgfile, NULL), 0); + ck_assert_int_eq(cfg_file_reload(), 0); + + fd = open(testfile, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); + ck_assert_int_ge(fd, 0); + fp = fdopen(fd, "w"); + if (NULL == fp) { + unlink(testfile); + close(fd); + } + ck_assert_ptr_ne(fp, NULL); + cfgfile_xml_print(fp); + ck_assert_int_eq(fclose(fp), 0); + + ck_assert_int_eq(cfg_set_program_config_file(testfile, NULL), 0); + ret = cfg_file_reload(); + ck_assert_int_eq(unlink(testfile), 0); + ck_assert_int_eq(ret, 0); +} + START_TEST(test_reload) { - ck_assert_int_eq(cfg_set_program_config_file(EXAMPLESDIR "/ezstream-file_template.xml", - NULL), 0); - ck_assert_int_eq(cfg_file_reload(), 0); + _test_cfgfile_rw(EXAMPLESDIR "/ezstream-file_template.xml"); + _test_cfgfile_rw(EXAMPLESDIR "/ezstream-full.xml"); + _test_cfgfile_rw(EXAMPLESDIR "/ezstream-metadata.xml"); + _test_cfgfile_rw(EXAMPLESDIR "/ezstream-minimal.xml"); + _test_cfgfile_rw(EXAMPLESDIR "/ezstream-stdin.xml"); + _test_cfgfile_rw(EXAMPLESDIR "/ezstream-video.xml"); ck_assert_int_eq(cfg_set_program_config_file(EXAMPLESDIR "/ezstream-full.xml", NULL), 0); ck_assert_int_eq(cfg_file_reload(), 0); - ck_assert_int_eq(cfg_set_program_config_file(EXAMPLESDIR "/ezstream-minimal.xml", - NULL), 0); - ck_assert_int_eq(cfg_file_reload(), 0); - ck_assert_int_eq(cfg_set_program_config_file(EXAMPLESDIR "/ezstream-stdin.xml", - NULL), 0); - ck_assert_int_eq(cfg_file_reload(), 0); - ck_assert_int_eq(cfg_set_program_config_file(EXAMPLESDIR "/ezstream-video.xml", - NULL), 0); - ck_assert_int_eq(cfg_file_reload(), 0); ck_assert_int_eq(cfg_file_reload(), 0); ck_assert_int_eq(cfg_set_program_config_file(SRCDIR "/config-bad.xml", NULL), 0);