chiark / gitweb /
lib/configuration.c, etc.: Replace `config_userconf()' by a variable.
[disorder] / lib / configuration.c
index c2f0791c9c9ae47c1f3615daabad70664be20b6f..469804d41826b9c47c279849e974da5a81f08fef 100644 (file)
 #if HAVE_LANGINFO_H
 # include <langinfo.h>
 #endif
-#if HAVE_PCRE_H
-# include <pcre.h>
-#endif
-#if HAVE_SHLOBJ_H
-# include <Shlobj.h>
-#endif
+
 #include <signal.h>
 
 #include "rights.h"
 #include "log.h"
 #include "split.h"
 #include "syscalls.h"
+#include "home.h"
 #include "table.h"
 #include "inputline.h"
 #include "charset.h"
 #include "defs.h"
 #include "printf.h"
-#if HAVE_PCRE_H
-# include "regsub.h"
-#endif
+#include "regexp.h"
+#include "regsub.h"
 #include "signame.h"
 #include "authhash.h"
 #include "vector.h"
  */
 char *configfile;
 
+/** @brief Path to user's config file
+ *
+ * set_configfile() sets the default if it is null.
+ */
+char *userconfigfile;
+
 /** @brief Read user configuration
  *
  * If clear, the user-specific configuration is not read.
@@ -401,15 +402,15 @@ static int set_sample_format(const struct config_state *cs,
                             nvec, vec);
 }
 
-#if HAVE_PCRE_H
 static int set_namepart(const struct config_state *cs,
                        const struct conf *whoami,
                        int nvec, char **vec) {
   struct namepartlist *npl = ADDRESS(cs->config, struct namepartlist);
   unsigned reflags;
-  const char *errstr;
-  int erroffset, n;
-  pcre *re;
+  regexp *re;
+  char errstr[RXCERR_LEN];
+  size_t erroffset;
+  int n;
 
   if(nvec < 3) {
     disorder_error(0, "%s:%d: namepart needs at least 3 arguments",
@@ -422,11 +423,10 @@ static int set_namepart(const struct config_state *cs,
     return -1;
   }
   reflags = nvec >= 5 ? regsub_flags(vec[4]) : 0;
-  if(!(re = pcre_compile(vec[1],
-                        PCRE_UTF8
-                        |regsub_compile_options(reflags),
-                        &errstr, &erroffset, 0))) {
-    disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
+  if(!(re = regexp_compile(vec[1], regsub_compile_options(reflags),
+                          errstr, sizeof(errstr), &erroffset)))
+  {
+    disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %zu)",
                   cs->path, cs->line, vec[1], errstr, erroffset);
     return -1;
   }
@@ -454,10 +454,10 @@ static int set_transform(const struct config_state *cs,
                         const struct conf *whoami,
                         int nvec, char **vec) {
   struct transformlist *tl = ADDRESS(cs->config, struct transformlist);
-  pcre *re;
+  regexp *re;
+  char errstr[RXCERR_LEN];
   unsigned reflags;
-  const char *errstr;
-  int erroffset;
+  size_t erroffset;
 
   if(nvec < 3) {
     disorder_error(0, "%s:%d: transform needs at least 3 arguments",
@@ -470,11 +470,10 @@ static int set_transform(const struct config_state *cs,
     return -1;
   }
   reflags = (nvec >= 5 ? regsub_flags(vec[4]) : 0);
-  if(!(re = pcre_compile(vec[1],
-                        PCRE_UTF8
-                        |regsub_compile_options(reflags),
-                        &errstr, &erroffset, 0))) {
-    disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
+  if(!(re = regexp_compile(vec[1], regsub_compile_options(reflags),
+                          errstr, sizeof(errstr), &erroffset)))
+  {
+    disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %zu)",
                   cs->path, cs->line, vec[1], errstr, erroffset);
     return -1;
   }
@@ -487,7 +486,6 @@ static int set_transform(const struct config_state *cs,
   ++tl->n;
   return 0;
 }
-#endif
 
 static int set_rights(const struct config_state *cs,
                      const struct conf *whoami,
@@ -569,7 +567,6 @@ static void free_collectionlist(struct config *c,
   xfree(cll->s);
 }
 
-#if HAVE_PCRE_H
 static void free_namepartlist(struct config *c,
                              const struct conf *whoami) {
   struct namepartlist *npl = ADDRESS(c, struct namepartlist);
@@ -579,7 +576,7 @@ static void free_namepartlist(struct config *c,
   for(n = 0; n < npl->n; ++n) {
     np = &npl->s[n];
     xfree(np->part);
-    pcre_free(np->re);                 /* ...whatever pcre_free is set to. */
+    regexp_free(np->re);
     xfree(np->res);
     xfree(np->replace);
     xfree(np->context);
@@ -596,13 +593,12 @@ static void free_transformlist(struct config *c,
   for(n = 0; n < tl->n; ++n) {
     t = &tl->t[n];
     xfree(t->type);
-    pcre_free(t->re);                  /* ...whatever pcre_free is set to. */
+    regexp_free(t->re);
     xfree(t->replace);
     xfree(t->context);
   }
   xfree(tl->t);
 }
-#endif
 
 static void free_netaddress(struct config *c,
                            const struct conf *whoami) {
@@ -622,10 +618,8 @@ static const struct conftype
   type_stringlist_accum = { set_stringlist_accum, free_stringlistlist },
   type_string_accum = { set_string_accum, free_stringlist },
   type_sample_format = { set_sample_format, free_none },
-#if HAVE_PCRE_H
   type_namepart = { set_namepart, free_namepartlist },
   type_transform = { set_transform, free_transformlist },
-#endif
   type_netaddress = { set_netaddress, free_netaddress },
   type_rights = { set_rights, free_string };
 
@@ -733,15 +727,14 @@ static int validate_tracklength(const struct config_state *cs,
   return 0;
 }
 
-/** @brief Validate a non-negative (@c long) integer
+/** @brief Common code for validating integer values
  * @param cs Configuration state
  * @param nvec Length of (proposed) new value
  * @param vec Elements of new value
- * @return 0 on success, non-0 on error
+ * @param n_out Where to put the value
  */
-static int validate_non_negative(const struct config_state *cs,
-                                int nvec, char **vec) {
-  long n;
+static int common_validate_integer(const struct config_state *cs,
+                                  int nvec, char **vec, long *n_out) {
   char errbuf[1024];
 
   if(nvec < 1) {
@@ -752,11 +745,24 @@ static int validate_non_negative(const struct config_state *cs,
     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
     return -1;
   }
-  if(xstrtol(&n, vec[0], 0, 0)) {
+  if(xstrtol(n_out, vec[0], 0, 0)) {
     disorder_error(0, "%s:%d: %s", cs->path, cs->line,
                    format_error(ec_errno, errno, errbuf, sizeof errbuf));
     return -1;
   }
+  return 0;
+}
+
+/** @brief Validate a non-negative (@c long) integer
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
+static int validate_non_negative(const struct config_state *cs,
+                                int nvec, char **vec) {
+  long n;
+  if(common_validate_integer(cs, nvec, vec, &n)) return -1;
   if(n < 0) {
     disorder_error(0, "%s:%d: must not be negative", cs->path, cs->line);
     return -1;
@@ -773,21 +779,7 @@ static int validate_non_negative(const struct config_state *cs,
 static int validate_positive(const struct config_state *cs,
                          int nvec, char **vec) {
   long n;
-  char errbuf[1024];
-
-  if(nvec < 1) {
-    disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
-    return -1;
-  }
-  if(nvec > 1) {
-    disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
-    return -1;
-  }
-  if(xstrtol(&n, vec[0], 0, 0)) {
-    disorder_error(0, "%s:%d: %s", cs->path, cs->line,
-                   format_error(ec_errno, errno, errbuf, sizeof errbuf));
-    return -1;
-  }
+  if(common_validate_integer(cs, nvec, vec, &n)) return -1;
   if(n <= 0) {
     disorder_error(0, "%s:%d: must be positive", cs->path, cs->line);
     return -1;
@@ -996,6 +988,24 @@ static int validate_pausemode(const struct config_state attribute((unused)) *cs,
   return -1;
 }
 
+/** @brief Validate an MTU-discovery setting
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
+static int validate_mtu_discovery(const struct config_state attribute((unused)) *cs,
+                                 int nvec,
+                                 char **vec) {
+  if (nvec == 1 &&
+      (!strcmp(vec[0], "default") ||
+       !strcmp(vec[0], "yes") ||
+       !strcmp(vec[0], "no")))
+    return 0;
+  disorder_error(0, "%s:%d: invalid MTU-discovery setting", cs->path, cs->line);
+  return -1;
+}
+
 /** @brief Validate a destination network address
  * @param cs Configuration state
  * @param nvec Length of (proposed) new value
@@ -1022,6 +1032,32 @@ static int validate_destaddr(const struct config_state attribute((unused)) *cs,
   return 0;
 }
 
+/** @brief Validate an internet address
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * By a destination address, it is meant that it must be either IPv4 or IPv6.
+ */
+static int validate_inetaddr(const struct config_state *cs,
+                            int nvec, char **vec) {
+  struct netaddress na[1];
+
+  if(netaddress_parse(na, nvec, vec)) {
+    disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
+    return -1;
+  }
+  switch(na->af) {
+    case AF_INET: case AF_INET6: case AF_UNSPEC: break;
+    default:
+      disorder_error(0, "%s:%d: must be an intenet address",
+                    cs->path, cs->line);
+      return -1;
+  }
+  return 0;
+}
+
 /** @brief Item name and and offset */
 #define C(x) #x, offsetof(struct config, x)
 /** @brief Item name and and offset */
@@ -1056,9 +1092,7 @@ static const struct conf conf[] = {
   { C(mount_rescan),     &type_boolean,          validate_any },
   { C(multicast_loop),   &type_boolean,          validate_any },
   { C(multicast_ttl),    &type_integer,          validate_non_negative },
-#if HAVE_PCRE_H
   { C(namepart),         &type_namepart,         validate_any },
-#endif
   { C(new_bias),         &type_integer,          validate_positive },
   { C(new_bias_age),     &type_integer,          validate_positive },
   { C(new_max),          &type_integer,          validate_positive },
@@ -1079,8 +1113,16 @@ static const struct conf conf[] = {
   { C(reminder_interval), &type_integer,         validate_positive },
   { C(remote_userman),   &type_boolean,          validate_any },
   { C(replay_min),       &type_integer,          validate_non_negative },
+  { C(rtp_always_request), &type_boolean,       validate_any },
   { C(rtp_delay_threshold), &type_integer,       validate_positive },
+  { C(rtp_instance_name), &type_string,                 validate_any },
+  { C(rtp_max_payload),         &type_integer,          validate_positive },
+  { C(rtp_maxbuffer),   &type_integer,          validate_non_negative },
+  { C(rtp_minbuffer),   &type_integer,          validate_non_negative },
   { C(rtp_mode),         &type_string,           validate_any },
+  { C(rtp_mtu_discovery), &type_string,                 validate_mtu_discovery },
+  { C(rtp_rcvbuf),      &type_integer,          validate_non_negative },
+  { C(rtp_request_address), &type_netaddress,   validate_inetaddr },
   { C(rtp_verbose),      &type_boolean,          validate_any },
   { C(sample_format),    &type_sample_format,    validate_sample_format },
   { C(scratch),          &type_string_accum,     validate_isreg },
@@ -1098,9 +1140,7 @@ static const struct conf conf[] = {
   { C(stopword),         &type_string_accum,     validate_any },
   { C(templates),        &type_string_accum,     validate_isdir },
   { C(tracklength),      &type_stringlist_accum, validate_tracklength },
-#if HAVE_PCRE_H
   { C(transform),        &type_transform,        validate_any },
-#endif
   { C(url),              &type_string,           validate_url },
 #if !_WIN32
   { C(user),             &type_string,           validate_isauser },
@@ -1394,6 +1434,8 @@ static struct config *config_default(void) {
   c->listen.af = -1;
   c->connect.af = -1;
   c->rtp_mode = xstrdup("auto");
+  c->rtp_max_payload = -1;
+  c->rtp_mtu_discovery = xstrdup("default");
   return c;
 }
 
@@ -1415,10 +1457,20 @@ char *config_get_file2(struct config *c, const char *name) {
 
 /** @brief Set the default configuration file */
 static void set_configfile(void) {
+  char *t;
+
 #if !_WIN32
-  if(!configfile)
-    byte_xasprintf(&configfile, "%s/config", pkgconfdir);
+  if(!configfile) {
+    configfile = getenv("DISORDER_CONFIG");
+    if(!configfile)
+      byte_xasprintf(&configfile, "%s/config", pkgconfdir);
+  }
 #endif
+  if(!userconfigfile && config_per_user) {
+    if((t = getenv("DISORDER_USERCONFIG"))) userconfigfile = xstrdup(t);
+    else if(!(userconfigfile = profile_filename("passwd")))
+      disorder_fatal(0, "failed to find user profile directory");
+  }
 }
 
 /** @brief Free a configuration object
@@ -1449,12 +1501,9 @@ void config_free(struct config *c) {
 static void config_postdefaults(struct config *c,
                                int server) {
   struct config_state cs;
-#if HAVE_PCRE_H
   const struct conf *whoami;
   int n;
-#endif
 
-#if HAVE_PCRE_H
   static const char *namepart[][4] = {
     { "title",  "/([0-9]+ *[-:]? *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
     { "title",  "/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort" },
@@ -1472,12 +1521,10 @@ static void config_postdefaults(struct config *c,
     { "dir",   "[[:punct:]]",                           "", "sort", "g", }
   };
 #define NTRANSFORM (int)(sizeof transform / sizeof *transform)
-#endif
 
   cs.path = "<internal>";
   cs.line = 0;
   cs.config = c;
-#if HAVE_PCRE_H
   if(!c->namepart.n) {
     whoami = find("namepart");
     for(n = 0; n < NNAMEPART; ++n)
@@ -1488,7 +1535,6 @@ static void config_postdefaults(struct config *c,
     for(n = 0; n < NTRANSFORM; ++n)
       set_transform(&cs, whoami, 5, (char **)transform[n]);
   }
-#endif
   if(!c->api) {
     if(c->speaker_command)
       c->api = xstrdup("command");
@@ -1507,8 +1553,10 @@ static void config_postdefaults(struct config *c,
   if(server) {
     if(!strcmp(c->api, "command") && !c->speaker_command)
       disorder_fatal(0, "'api command' but speaker_command is not set");
-    if((!strcmp(c->api, "rtp")) && c->broadcast.af == -1)
-      disorder_fatal(0, "'api rtp' but broadcast is not set");
+    if((!strcmp(c->api, "rtp")) &&
+       c->broadcast.af == -1 && strcmp(c->rtp_mode, "request"))
+      disorder_fatal(0, "'api rtp' but broadcast is not set "
+                    "and mode is not not 'request'");
   }
   /* Override sample format */
   if(!strcmp(c->api, "rtp")) {
@@ -1572,11 +1620,9 @@ int config_read(int server,
     xfree(privconf);
 #endif
     /* if we have a password file, read it */
-    if((privconf = config_userconf(0, pw))
-       && access(privconf, F_OK) == 0
-       && config_include(c, privconf))
+    if(access(userconfigfile, F_OK) == 0
+       && config_include(c, userconfigfile))
       return -1;
-    xfree(privconf);
   }
   /* install default namepart and transform settings */
   config_postdefaults(c, server);
@@ -1604,12 +1650,10 @@ int config_read(int server,
       disorder_error(0, "'nice_server' cannot be changed without a restart");
       /* ...but we accept the new config anyway */
     }
-#if HAVE_PCRE_H
     if(namepartlist_compare(&c->namepart, &oldconfig->namepart)) {
       disorder_error(0, "'namepart' settings cannot be changed without a restart");
       failed = 1;
     }
-#endif
     if(stringlist_compare(&c->stopword, &oldconfig->stopword)) {
       disorder_error(0, "'stopword' settings cannot be changed without a restart");
       failed = 1;
@@ -1633,38 +1677,22 @@ char *config_private(void) {
 #else
   char *s;
 
+  if((s = getenv("DISORDER_PRIVCONFIG"))) return xstrdup(s);
   set_configfile();
   byte_xasprintf(&s, "%s.private", configfile);
   return s;
 #endif
 }
 
-/** @brief Return the path to user's personal configuration file */
-char *config_userconf(const char *home, const struct passwd *pw) {
-  char *s;
-#if _WIN32
-  wchar_t *wpath = 0;
-  char *appdata;
-  if(SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &wpath) != S_OK)
-    disorder_fatal(0, "error calling SHGetKnownFolderPath");
-  appdata = win_wtomb(wpath);
-  CoTaskMemFree(wpath);
-  byte_xasprintf(&s, "%s\\DisOrder\\passwd", appdata);
-#else
-  if(!home && !pw && !(pw = getpwuid(getuid())))
-    disorder_fatal(0, "cannot determine our username");
-  byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir);
-#endif
-  return s;
-}
-
 #if !_WIN32
 /** @brief Return the path to user-specific system configuration */
 char *config_usersysconf(const struct passwd *pw) {
   char *s;
 
   set_configfile();
-  if(!strchr(pw->pw_name, '/')) {
+  if((s = getenv("DISORDER_USERCONFIG_SYS")))
+    return xstrdup(s);
+  else if(!strchr(pw->pw_name, '/')) {
     byte_xasprintf(&s, "%s.%s", configfile, pw->pw_name);
     return s;
   } else
@@ -1702,7 +1730,6 @@ static int stringlist_compare(const struct stringlist *a,
     return 0;
 }
 
-#if HAVE_PCRE_H
 /** @brief Order two namepart definitions
  * @param a First namepart definition
  * @param b Second namepart definition
@@ -1748,7 +1775,6 @@ static int namepartlist_compare(const struct namepartlist *a,
   else
     return 0;
 }
-#endif
 
 /** @brief Verify configuration table.
  * @return The number of problems found