2006-01-24 23:41:05 +00:00
|
|
|
/*
|
2006-03-25 23:21:16 +00:00
|
|
|
* $Id$
|
2006-01-24 23:41:05 +00:00
|
|
|
* Functions for reading and writing the config file
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Ron Pedde (ron@pedde.com)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file config.c
|
|
|
|
*
|
|
|
|
* Config file reading and writing
|
|
|
|
*/
|
|
|
|
|
2006-03-06 06:55:58 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2006-01-24 23:41:05 +00:00
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2006-05-24 03:56:06 +00:00
|
|
|
#include <ctype.h>
|
2006-03-25 23:27:55 +00:00
|
|
|
#include <errno.h>
|
2006-03-07 07:55:36 +00:00
|
|
|
#include <limits.h>
|
2006-02-21 03:08:14 +00:00
|
|
|
#include <pthread.h>
|
2006-01-24 23:41:05 +00:00
|
|
|
#include <stdio.h>
|
2006-03-03 06:05:34 +00:00
|
|
|
#include <stdlib.h>
|
2006-02-14 06:39:06 +00:00
|
|
|
#include <string.h>
|
2006-01-24 23:41:05 +00:00
|
|
|
|
2006-05-24 03:53:22 +00:00
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2006-03-06 06:29:03 +00:00
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
|
|
# include <sys/param.h>
|
|
|
|
#endif
|
2006-03-25 23:27:55 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2006-03-03 06:05:34 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
#include "conf.h"
|
2006-02-05 00:22:46 +00:00
|
|
|
#include "err.h"
|
|
|
|
#include "ll.h"
|
|
|
|
#include "daapd.h"
|
2006-03-26 04:55:59 +00:00
|
|
|
#include "os.h"
|
2006-04-04 06:31:19 +00:00
|
|
|
#include "webserver.h"
|
|
|
|
#include "xml-rpc.h"
|
2006-01-24 23:41:05 +00:00
|
|
|
|
2006-05-24 03:53:22 +00:00
|
|
|
|
|
|
|
#ifndef HOST_NAME_MAX
|
|
|
|
# define HOST_NAME_MAX 255
|
|
|
|
#endif
|
|
|
|
|
2006-01-24 23:41:05 +00:00
|
|
|
/** Globals */
|
2006-03-03 06:05:34 +00:00
|
|
|
//static int ecode;
|
2006-02-21 03:08:14 +00:00
|
|
|
static LL_HANDLE conf_main=NULL;
|
2006-03-13 06:33:58 +00:00
|
|
|
static LL_HANDLE conf_comments=NULL;
|
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
static char *conf_main_file = NULL;
|
2006-02-21 03:08:14 +00:00
|
|
|
static pthread_mutex_t conf_mutex = PTHREAD_MUTEX_INITIALIZER;
|
2006-02-05 00:22:46 +00:00
|
|
|
|
2006-03-12 21:25:29 +00:00
|
|
|
#define CONF_LINEBUFFER 128
|
2006-01-24 23:41:05 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
#define CONF_T_INT 0
|
|
|
|
#define CONF_T_STRING 1
|
2006-03-19 22:03:26 +00:00
|
|
|
#define CONF_T_EXISTPATH 2 /** a path that must exist */
|
|
|
|
#define CONF_T_MULTICOMMA 3 /** multiple entries separated by commas */
|
2006-02-08 23:02:43 +00:00
|
|
|
|
|
|
|
typedef struct _CONF_ELEMENTS {
|
|
|
|
int required;
|
|
|
|
int deprecated;
|
|
|
|
int type;
|
|
|
|
char *section;
|
|
|
|
char *term;
|
|
|
|
} CONF_ELEMENTS;
|
|
|
|
|
2006-03-19 22:03:26 +00:00
|
|
|
/** Forwards */
|
|
|
|
static int _conf_verify(LL_HANDLE pll);
|
2006-03-26 22:07:33 +00:00
|
|
|
static LL_ITEM *_conf_fetch_item(LL_HANDLE pll, char *section, char *key);
|
|
|
|
static int _conf_exists(LL_HANDLE pll, char *section, char *key);
|
2006-03-19 22:03:26 +00:00
|
|
|
static void _conf_lock(void);
|
|
|
|
static void _conf_unlock(void);
|
|
|
|
static int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent);
|
|
|
|
static CONF_ELEMENTS *_conf_get_keyinfo(char *section, char *key);
|
2006-03-26 04:55:59 +00:00
|
|
|
static int _conf_makedir(char *path, char *user);
|
2006-03-25 23:21:16 +00:00
|
|
|
static int _conf_existdir(char *path);
|
2006-03-26 22:07:33 +00:00
|
|
|
static int _conf_split(char *s, char *delimiters, char ***argvp);
|
|
|
|
static void _conf_dispose_split(char **argv);
|
2006-04-04 06:31:19 +00:00
|
|
|
static int _conf_xml_dump(XMLSTRUCT *pxml,LL *pll,int sublevel,char *parent);
|
2006-05-16 06:39:09 +00:00
|
|
|
static int _conf_verify_element(char *section, char *key, char *value);
|
2006-03-19 22:03:26 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
static CONF_ELEMENTS conf_elements[] = {
|
|
|
|
{ 1, 0, CONF_T_STRING,"general","runas" },
|
2006-03-03 06:05:34 +00:00
|
|
|
{ 1, 0, CONF_T_EXISTPATH,"general","web_root" },
|
2006-05-24 04:19:44 +00:00
|
|
|
{ 0, 0, CONF_T_INT,"general","port" },
|
2006-02-08 23:02:43 +00:00
|
|
|
{ 1, 0, CONF_T_STRING,"general","admin_pw" },
|
2006-04-12 05:18:55 +00:00
|
|
|
{ 1, 0, CONF_T_MULTICOMMA,"general","mp3_dir" },
|
2006-03-03 06:05:34 +00:00
|
|
|
{ 0, 1, CONF_T_EXISTPATH,"general","db_dir" },
|
2006-02-08 23:02:43 +00:00
|
|
|
{ 0, 0, CONF_T_STRING,"general","db_type" },
|
2006-03-26 04:55:59 +00:00
|
|
|
{ 0, 0, CONF_T_EXISTPATH,"general","db_parms" }, /* this isn't right */
|
2006-02-08 23:02:43 +00:00
|
|
|
{ 0, 0, CONF_T_INT,"general","debuglevel" },
|
2006-05-24 03:53:22 +00:00
|
|
|
{ 0, 0, CONF_T_STRING,"general","servername" },
|
2006-02-08 23:02:43 +00:00
|
|
|
{ 0, 0, CONF_T_INT,"general","rescan_interval" },
|
|
|
|
{ 0, 0, CONF_T_INT,"general","always_scan" },
|
|
|
|
{ 0, 1, CONF_T_INT,"general","latin1_tags" },
|
|
|
|
{ 0, 0, CONF_T_INT,"general","process_m3u" },
|
|
|
|
{ 0, 0, CONF_T_INT,"general","scan_type" },
|
|
|
|
{ 0, 1, CONF_T_INT,"general","compress" },
|
|
|
|
{ 0, 0, CONF_T_STRING,"general","playlist" },
|
|
|
|
{ 0, 0, CONF_T_STRING,"general","extensions" },
|
|
|
|
{ 0, 0, CONF_T_STRING,"general","interface" },
|
|
|
|
{ 0, 0, CONF_T_STRING,"general","ssc_codectypes" },
|
|
|
|
{ 0, 0, CONF_T_STRING,"general","ssc_prog" },
|
|
|
|
{ 0, 0, CONF_T_STRING,"general","password" },
|
2006-03-19 22:03:26 +00:00
|
|
|
{ 0, 0, CONF_T_MULTICOMMA,"general","compdirs" },
|
2006-02-14 06:39:06 +00:00
|
|
|
{ 0, 0, CONF_T_STRING,"general","logfile" },
|
2006-04-19 08:32:18 +00:00
|
|
|
{ 0, 0, CONF_T_EXISTPATH,"plugins","plugin_dir" },
|
|
|
|
{ 0, 0, CONF_T_MULTICOMMA,"plugins","plugins" },
|
2006-05-17 06:44:56 +00:00
|
|
|
{ 0, 0, CONF_T_INT,"daap","empty_strings" },
|
|
|
|
{ 0, 0, CONF_T_INT,"daap","supports_browse" },
|
2006-02-14 06:39:06 +00:00
|
|
|
{ 0, 0, CONF_T_INT, NULL, NULL }
|
2006-02-08 23:02:43 +00:00
|
|
|
};
|
|
|
|
|
2006-03-19 22:03:26 +00:00
|
|
|
|
2006-03-25 23:21:16 +00:00
|
|
|
/**
|
|
|
|
* Try and create a directory, including parents (probably
|
|
|
|
* in response to a config file entry that does not exist).
|
|
|
|
*
|
|
|
|
* @param path path to make
|
|
|
|
* @returns TRUE on success, FALSE otherwise
|
|
|
|
*/
|
|
|
|
|
2006-03-26 04:55:59 +00:00
|
|
|
int _conf_makedir(char *path,char *user) {
|
2006-03-25 23:21:16 +00:00
|
|
|
char *token, *next_token;
|
|
|
|
char *pathdup;
|
|
|
|
char path_buffer[PATH_MAX];
|
|
|
|
int retval = FALSE;
|
|
|
|
|
|
|
|
DPRINTF(E_DBG,L_CONF,"Creating %s\n",path);
|
|
|
|
|
|
|
|
pathdup=strdup(path);
|
|
|
|
if(!pathdup) {
|
2006-03-26 22:07:33 +00:00
|
|
|
DPRINTF(E_FATAL,L_CONF,"Malloc error\n");
|
2006-03-25 23:21:16 +00:00
|
|
|
}
|
|
|
|
|
2006-04-10 19:02:39 +00:00
|
|
|
next_token=pathdup;
|
|
|
|
while(*next_token && (*next_token != PATHSEP))
|
|
|
|
next_token++;
|
|
|
|
if(*next_token)
|
|
|
|
next_token++;
|
|
|
|
|
2006-03-25 23:21:16 +00:00
|
|
|
memset(path_buffer,0,sizeof(path_buffer));
|
|
|
|
|
2006-04-10 19:02:39 +00:00
|
|
|
while((token=strsep(&next_token,PATHSEP_STR))) {
|
2006-03-25 23:21:16 +00:00
|
|
|
if((strlen(path_buffer) + strlen(token)) < PATH_MAX) {
|
2006-04-10 19:02:39 +00:00
|
|
|
strcat(path_buffer,PATHSEP_STR);
|
2006-03-25 23:21:16 +00:00
|
|
|
strcat(path_buffer,token);
|
2006-03-26 04:55:59 +00:00
|
|
|
|
2006-03-26 22:07:33 +00:00
|
|
|
if(!_conf_existdir(path_buffer)) {
|
|
|
|
DPRINTF(E_DBG,L_CONF,"Making %s\n",path_buffer);
|
|
|
|
if((mkdir(path_buffer,0700)) && (errno != EEXIST)) {
|
|
|
|
free(pathdup);
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Could not make dirctory %s: %s\n",
|
|
|
|
path_buffer,strerror(errno));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
os_chown(path_buffer,user);
|
|
|
|
}
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
2006-03-25 23:21:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(pathdup);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a particular directory exists or not
|
|
|
|
*
|
|
|
|
* @param path directory to test for existence
|
|
|
|
* @returns true if path exists, false otherwise
|
|
|
|
*/
|
|
|
|
int _conf_existdir(char *path) {
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
DPRINTF(E_DBG,L_CONF,"Checking existence of %s\n",path);
|
|
|
|
|
|
|
|
if(stat(path,&sb)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sb.st_mode & S_IFDIR)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2006-03-19 22:03:26 +00:00
|
|
|
/**
|
|
|
|
* given a section and key, get the conf_element for it.
|
|
|
|
* right now this is simple, but eventually there might
|
|
|
|
* be more difficult mateches to be made
|
|
|
|
*
|
|
|
|
* @param section section the key was found ind
|
|
|
|
* @param key key we are searching for info on
|
|
|
|
* @returns the CONF_ELEMENT that is the closest match, or
|
|
|
|
* NULL if no match was found.
|
|
|
|
*/
|
|
|
|
CONF_ELEMENTS *_conf_get_keyinfo(char *section, char *key) {
|
|
|
|
CONF_ELEMENTS *pcurrent;
|
|
|
|
int found=0;
|
|
|
|
|
|
|
|
pcurrent = &conf_elements[0];
|
|
|
|
while(pcurrent->section && pcurrent->term) {
|
2006-03-20 22:27:56 +00:00
|
|
|
if((strcasecmp(section,pcurrent->section) != 0) ||
|
|
|
|
(strcasecmp(key,pcurrent->term) != 0)) {
|
|
|
|
pcurrent++;
|
|
|
|
} else {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
2006-03-19 22:03:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return found ? pcurrent : NULL;
|
|
|
|
}
|
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
/**
|
|
|
|
* lock the conf mutex
|
|
|
|
*/
|
|
|
|
void _conf_lock() {
|
|
|
|
int err;
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
if((err=pthread_mutex_lock(&conf_mutex))) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"Cannot lock configuration mutex: %s\n",
|
|
|
|
strerror(err));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* unlock the conf mutex
|
|
|
|
*/
|
|
|
|
void _conf_unlock() {
|
|
|
|
int err;
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
if((err = pthread_mutex_unlock(&conf_mutex))) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"Cannot unlock configuration mutex %s\n",
|
|
|
|
strerror(err));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-20 03:56:47 +00:00
|
|
|
/**
|
|
|
|
* fetch item based on section/term basis, rather than just a single
|
|
|
|
* level deep, like ll_fetch_item does
|
2006-02-21 23:19:53 +00:00
|
|
|
*
|
2006-02-20 03:56:47 +00:00
|
|
|
* @param pll top level linked list to test (config tree)
|
|
|
|
* @param section section to term (key) is in
|
2006-03-26 22:07:33 +00:00
|
|
|
* @param key key to look for
|
2006-02-20 03:56:47 +00:00
|
|
|
* @returns LL_ITEM of the key, or NULL
|
|
|
|
*/
|
2006-03-26 22:07:33 +00:00
|
|
|
LL_ITEM *_conf_fetch_item(LL_HANDLE pll, char *section, char *key) {
|
2006-02-20 03:56:47 +00:00
|
|
|
LL_ITEM *psection;
|
2006-02-14 06:39:06 +00:00
|
|
|
LL_ITEM *pitem;
|
2006-02-20 03:56:47 +00:00
|
|
|
|
|
|
|
if(!(psection = ll_fetch_item(pll,section)))
|
2006-02-21 03:08:14 +00:00
|
|
|
return NULL;
|
2006-02-20 03:56:47 +00:00
|
|
|
|
|
|
|
if(psection->type != LL_TYPE_LL)
|
2006-02-21 03:08:14 +00:00
|
|
|
return NULL;
|
2006-02-20 03:56:47 +00:00
|
|
|
|
2006-03-26 22:07:33 +00:00
|
|
|
if(!(pitem = ll_fetch_item(psection->value.as_ll,key)))
|
2006-02-21 03:08:14 +00:00
|
|
|
return NULL;
|
2006-02-20 03:56:47 +00:00
|
|
|
|
|
|
|
return pitem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* simple test to see if a particular section/key value exists
|
|
|
|
*
|
|
|
|
* @param pll config tree to test
|
|
|
|
* @param section section to find the term under
|
2006-03-26 22:07:33 +00:00
|
|
|
* @param key key to search for under the specified section
|
2006-02-20 03:56:47 +00:00
|
|
|
* @returns TRUE if key exists, FALSE otherwise
|
|
|
|
*/
|
2006-03-26 22:07:33 +00:00
|
|
|
int _conf_exists(LL_HANDLE pll, char *section, char *key) {
|
|
|
|
if(!_conf_fetch_item(pll,section,key))
|
2006-02-21 03:08:14 +00:00
|
|
|
return FALSE;
|
2006-02-20 03:56:47 +00:00
|
|
|
|
2006-02-14 06:39:06 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-16 06:22:32 +00:00
|
|
|
/**
|
|
|
|
* verify a specific element, that is, see if changing a specific
|
|
|
|
* element, in a vacuum, would be problematic
|
|
|
|
*
|
|
|
|
* @param section section to test
|
|
|
|
* @param key key to test
|
|
|
|
* @param value value to test
|
|
|
|
* @returns CONF_E_SUCCESS on success
|
|
|
|
*/
|
|
|
|
|
|
|
|
int _conf_verify_element(char *section, char *key, char *value) {
|
|
|
|
CONF_ELEMENTS *pce;
|
|
|
|
|
|
|
|
pce = _conf_get_keyinfo(section, key);
|
|
|
|
if(!pce) {
|
|
|
|
return CONF_E_BADELEMENT;
|
|
|
|
}
|
|
|
|
|
2006-05-18 05:11:07 +00:00
|
|
|
if(strcmp(value,"") == 0) {
|
|
|
|
if(!pce->required) {
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-16 06:39:09 +00:00
|
|
|
switch(pce->type) {
|
2006-05-16 06:22:32 +00:00
|
|
|
case CONF_T_MULTICOMMA: /* can't really check these */
|
|
|
|
case CONF_T_STRING:
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONF_T_INT:
|
|
|
|
if((atoi(value) || (strcmp(value,"0")==0)))
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
return CONF_E_INTEXPECTED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONF_T_EXISTPATH:
|
|
|
|
if(!_conf_existdir(value))
|
|
|
|
return CONF_E_PATHEXPECTED;
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Bad config type: %d\n",pce->type);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-01-24 23:41:05 +00:00
|
|
|
/**
|
2006-02-14 06:39:06 +00:00
|
|
|
* Verify that the configuration isn't obviously wrong.
|
|
|
|
* Type checking has already been done, this just checks
|
|
|
|
* required stuff isn't missing.
|
2006-02-08 23:02:43 +00:00
|
|
|
*
|
|
|
|
* @param pll tree to check
|
2006-02-14 06:39:06 +00:00
|
|
|
* @returns TRUE if configuration appears valid, FALSE otherwise
|
2006-02-08 23:02:43 +00:00
|
|
|
*/
|
2006-02-14 06:39:06 +00:00
|
|
|
int _conf_verify(LL_HANDLE pll) {
|
|
|
|
LL_ITEM *pi = NULL;
|
2006-03-26 04:55:59 +00:00
|
|
|
LL_ITEM *ptemp = NULL;
|
2006-02-14 06:39:06 +00:00
|
|
|
CONF_ELEMENTS *pce;
|
|
|
|
int is_valid=TRUE;
|
2006-03-03 06:05:34 +00:00
|
|
|
char resolved_path[PATH_MAX];
|
2006-03-26 04:55:59 +00:00
|
|
|
char *user;
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-14 06:39:06 +00:00
|
|
|
/* first, walk through the elements and make sure
|
|
|
|
* all required elements are there */
|
|
|
|
pce = &conf_elements[0];
|
|
|
|
while(pce->section) {
|
|
|
|
if(pce->required) {
|
2006-02-21 03:08:14 +00:00
|
|
|
if(!_conf_exists(pll,pce->section, pce->term)) {
|
2006-02-14 06:39:06 +00:00
|
|
|
DPRINTF(E_LOG,L_CONF,"Missing configuration entry "
|
|
|
|
" %s/%s. Please review the sample config\n",
|
|
|
|
pce->section, pce->term);
|
2006-02-21 03:08:14 +00:00
|
|
|
is_valid=FALSE;
|
|
|
|
}
|
2006-02-14 06:39:06 +00:00
|
|
|
}
|
|
|
|
if(pce->deprecated) {
|
2006-03-13 06:33:58 +00:00
|
|
|
if(_conf_exists(pll,pce->section,pce->term)) {
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Config entry %s/%s is deprecated. Please "
|
|
|
|
"review the sample config\n",
|
|
|
|
pce->section, pce->term);
|
|
|
|
}
|
2006-02-14 06:39:06 +00:00
|
|
|
}
|
2006-03-03 06:05:34 +00:00
|
|
|
if(pce->type == CONF_T_EXISTPATH) {
|
2006-03-26 22:07:33 +00:00
|
|
|
/* first, need to resolve */
|
2006-03-03 06:05:34 +00:00
|
|
|
pi = _conf_fetch_item(pll,pce->section, pce->term);
|
|
|
|
if(pi) {
|
|
|
|
memset(resolved_path,0,sizeof(resolved_path));
|
|
|
|
if(pi->value.as_string) {
|
2006-03-26 22:07:33 +00:00
|
|
|
DPRINTF(E_SPAM,L_CONF,"Found %s/%s as %s... checking\n",
|
|
|
|
pce->section, pce->term, pi->value.as_string);
|
|
|
|
|
|
|
|
/* verify it exists, creating it if necessary */
|
|
|
|
if(!_conf_existdir(pi->value.as_string)) {
|
|
|
|
user = "nobody";
|
|
|
|
ptemp = _conf_fetch_item(pll, "general", "runas");
|
|
|
|
if(ptemp) {
|
|
|
|
user = ptemp->value.as_string;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!_conf_makedir(pi->value.as_string,user)) {
|
|
|
|
is_valid=0;
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Can't make path %s, invalid config.\n",
|
|
|
|
resolved_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_conf_existdir(pi->value.as_string)) {
|
|
|
|
realpath(pi->value.as_string,resolved_path);
|
|
|
|
free(pi->value.as_string);
|
|
|
|
pi->value.as_string = strdup(resolved_path);
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_CONF,"Resolved to %s\n",resolved_path);
|
|
|
|
}
|
|
|
|
}
|
2006-03-03 06:05:34 +00:00
|
|
|
}
|
|
|
|
}
|
2006-02-14 06:39:06 +00:00
|
|
|
pce++;
|
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-14 06:39:06 +00:00
|
|
|
/* here we would walk through derived sections, if there
|
|
|
|
* were any */
|
2006-02-08 23:02:43 +00:00
|
|
|
|
2006-02-14 06:39:06 +00:00
|
|
|
return is_valid;
|
2006-02-08 23:02:43 +00:00
|
|
|
}
|
|
|
|
|
2006-04-15 23:03:31 +00:00
|
|
|
/**
|
|
|
|
* reload the existing config file.
|
|
|
|
*
|
|
|
|
* @returns CONF_E_SUCCESS on success
|
|
|
|
*/
|
|
|
|
int conf_reload(void) {
|
|
|
|
return conf_read(conf_main_file);
|
|
|
|
}
|
2006-02-08 23:02:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* read a configfile into a tree
|
2006-01-24 23:41:05 +00:00
|
|
|
*
|
|
|
|
* @param file file to read
|
|
|
|
* @returns TRUE if successful, FALSE otherwise
|
|
|
|
*/
|
2006-02-21 03:08:14 +00:00
|
|
|
int conf_read(char *file) {
|
2006-01-25 22:31:53 +00:00
|
|
|
FILE *fin;
|
|
|
|
int err;
|
2006-03-12 21:25:29 +00:00
|
|
|
LL_HANDLE pllnew, plltemp, pllcurrent, pllcomment;
|
2006-03-26 22:07:33 +00:00
|
|
|
LL_ITEM *pli;
|
2006-03-12 21:25:29 +00:00
|
|
|
char linebuffer[CONF_LINEBUFFER+1];
|
2006-03-13 06:33:58 +00:00
|
|
|
char keybuffer[256];
|
2006-02-05 00:22:46 +00:00
|
|
|
char *comment, *term, *value, *delim;
|
2006-03-13 06:33:58 +00:00
|
|
|
char *section_name=NULL;
|
|
|
|
char *prev_comments=NULL;
|
2006-03-16 23:56:49 +00:00
|
|
|
int total_comment_length=CONF_LINEBUFFER;
|
|
|
|
int current_comment_length=0;
|
2006-02-05 00:22:46 +00:00
|
|
|
int compat_mode=1;
|
2006-03-16 23:56:49 +00:00
|
|
|
int warned_truncate=0;
|
2006-02-08 23:02:43 +00:00
|
|
|
int line=0;
|
2006-03-13 06:33:58 +00:00
|
|
|
int ws=0;
|
2006-03-19 22:03:26 +00:00
|
|
|
CONF_ELEMENTS *pce;
|
|
|
|
int key_type;
|
2006-03-26 22:07:33 +00:00
|
|
|
char **valuearray;
|
|
|
|
int index;
|
2006-03-12 21:25:29 +00:00
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
if(conf_main_file) {
|
|
|
|
conf_close();
|
|
|
|
}
|
|
|
|
|
|
|
|
conf_main_file = strdup(file);
|
|
|
|
|
2006-01-25 22:31:53 +00:00
|
|
|
fin=fopen(file,"r");
|
|
|
|
if(!fin) {
|
2006-02-08 23:02:43 +00:00
|
|
|
return CONF_E_FOPEN;
|
2006-01-25 22:31:53 +00:00
|
|
|
}
|
|
|
|
|
2006-03-16 23:56:49 +00:00
|
|
|
prev_comments = (char*)malloc(total_comment_length);
|
2006-03-20 22:27:56 +00:00
|
|
|
if(!prev_comments)
|
2006-03-16 23:56:49 +00:00
|
|
|
DPRINTF(E_FATAL,L_CONF,"Malloc error\n");
|
2006-03-13 06:33:58 +00:00
|
|
|
prev_comments[0] = '\0';
|
|
|
|
|
2006-02-05 00:22:46 +00:00
|
|
|
if((err=ll_create(&pllnew)) != LL_E_SUCCESS) {
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Error creating linked list: %d\n",err);
|
|
|
|
fclose(fin);
|
2006-02-08 23:02:43 +00:00
|
|
|
return CONF_E_UNKNOWN;
|
2006-01-25 22:31:53 +00:00
|
|
|
}
|
2006-01-24 23:41:05 +00:00
|
|
|
|
2006-03-12 21:25:29 +00:00
|
|
|
ll_create(&pllcomment); /* don't care if we lose comments */
|
2006-03-13 06:33:58 +00:00
|
|
|
|
2006-03-12 21:25:29 +00:00
|
|
|
comment = NULL;
|
2006-02-08 23:02:43 +00:00
|
|
|
pllcurrent=NULL;
|
|
|
|
|
2006-02-05 00:22:46 +00:00
|
|
|
/* got what will be the root of the config tree, now start walking through
|
|
|
|
* the input file, populating the tree
|
|
|
|
*/
|
2006-03-12 21:25:29 +00:00
|
|
|
while(fgets(linebuffer,CONF_LINEBUFFER,fin)) {
|
2006-02-08 23:02:43 +00:00
|
|
|
line++;
|
2006-03-12 21:25:29 +00:00
|
|
|
linebuffer[CONF_LINEBUFFER] = '\0';
|
2006-03-13 06:33:58 +00:00
|
|
|
ws=0;
|
2006-02-05 00:22:46 +00:00
|
|
|
|
|
|
|
comment=strchr(linebuffer,'#');
|
|
|
|
if(comment) {
|
|
|
|
*comment = '\0';
|
2006-03-12 21:25:29 +00:00
|
|
|
comment++;
|
2006-02-05 00:22:46 +00:00
|
|
|
}
|
|
|
|
|
2006-03-20 22:27:56 +00:00
|
|
|
while(strlen(linebuffer) &&
|
2006-03-13 06:33:58 +00:00
|
|
|
(strchr("\n\r ",linebuffer[strlen(linebuffer)-1])))
|
2006-02-05 00:22:46 +00:00
|
|
|
linebuffer[strlen(linebuffer)-1] = '\0';
|
|
|
|
|
|
|
|
if(linebuffer[0] == '[') {
|
|
|
|
/* section header */
|
|
|
|
compat_mode=0;
|
|
|
|
term=&linebuffer[1];
|
|
|
|
value = strchr(term,']');
|
|
|
|
if(!value) {
|
|
|
|
ll_destroy(pllnew);
|
|
|
|
fclose(fin);
|
2006-02-08 23:02:43 +00:00
|
|
|
return CONF_E_BADHEADER;
|
2006-02-05 00:22:46 +00:00
|
|
|
}
|
|
|
|
*value = '\0';
|
|
|
|
|
|
|
|
if((err = ll_create(&plltemp)) != LL_E_SUCCESS) {
|
|
|
|
ll_destroy(pllnew);
|
|
|
|
fclose(fin);
|
2006-02-08 23:02:43 +00:00
|
|
|
return CONF_E_UNKNOWN;
|
2006-02-05 00:22:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ll_add_ll(pllnew,term,plltemp);
|
2006-03-13 06:33:58 +00:00
|
|
|
|
|
|
|
/* set current section and name */
|
2006-02-08 23:02:43 +00:00
|
|
|
pllcurrent = plltemp;
|
2006-03-13 06:33:58 +00:00
|
|
|
if(section_name)
|
|
|
|
free(section_name);
|
|
|
|
section_name = strdup(term);
|
|
|
|
|
|
|
|
/* set precomments */
|
|
|
|
if(prev_comments[0] != '\0') {
|
|
|
|
/* we had some preceding comments */
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"pre_%s",section_name);
|
|
|
|
ll_add_string(pllcomment,keybuffer,prev_comments);
|
|
|
|
prev_comments[0] = '\0';
|
2006-03-16 23:56:49 +00:00
|
|
|
current_comment_length=0;
|
2006-03-13 06:33:58 +00:00
|
|
|
}
|
|
|
|
if(comment) {
|
|
|
|
/* we had some preceding comments */
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"in_%s",section_name);
|
|
|
|
ll_add_string(pllcomment,keybuffer,comment);
|
|
|
|
prev_comments[0] = '\0';
|
2006-03-16 23:56:49 +00:00
|
|
|
current_comment_length=0;
|
2006-03-13 06:33:58 +00:00
|
|
|
comment = NULL;
|
|
|
|
}
|
2006-02-05 00:22:46 +00:00
|
|
|
} else {
|
|
|
|
/* k/v pair */
|
|
|
|
term=&linebuffer[0];
|
|
|
|
|
|
|
|
while((*term=='\t') || (*term==' '))
|
|
|
|
term++;
|
|
|
|
|
|
|
|
value=term;
|
|
|
|
|
|
|
|
if(compat_mode) {
|
|
|
|
delim="\t ";
|
|
|
|
} else {
|
|
|
|
delim="=";
|
|
|
|
}
|
|
|
|
|
|
|
|
strsep(&value,delim);
|
|
|
|
if((value) && (term) && (strlen(term))) {
|
2006-03-13 06:33:58 +00:00
|
|
|
while((strlen(term) && (strchr("\t ",term[strlen(term)-1]))))
|
|
|
|
term[strlen(term)-1] = '\0';
|
2006-02-05 00:22:46 +00:00
|
|
|
while(strlen(value) && (strchr("\t ",*value)))
|
|
|
|
value++;
|
2006-03-13 06:33:58 +00:00
|
|
|
while((strlen(value) && (strchr("\t ",value[strlen(value)-1]))))
|
|
|
|
value[strlen(value)-1] = '\0';
|
2006-02-05 00:22:46 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
if(!pllcurrent) {
|
2006-03-13 06:33:58 +00:00
|
|
|
/* in compat mode -- add a general section */
|
2006-02-08 23:02:43 +00:00
|
|
|
if((err=ll_create(&plltemp)) != LL_E_SUCCESS) {
|
2006-03-13 06:33:58 +00:00
|
|
|
DPRINTF(E_LOG,L_CONF,"Error creating list: %d\n",err);
|
2006-02-08 23:02:43 +00:00
|
|
|
ll_destroy(pllnew);
|
|
|
|
fclose(fin);
|
|
|
|
return CONF_E_UNKNOWN;
|
|
|
|
}
|
|
|
|
ll_add_ll(pllnew,"general",plltemp);
|
|
|
|
pllcurrent = plltemp;
|
2006-03-20 22:27:56 +00:00
|
|
|
|
|
|
|
if(section_name)
|
2006-03-13 06:33:58 +00:00
|
|
|
free(section_name); /* shouldn't ahppen */
|
|
|
|
section_name = strdup("general");
|
|
|
|
|
|
|
|
/* no inline comments, just precomments */
|
|
|
|
if(prev_comments[0] != '\0') {
|
|
|
|
/* we had some preceding comments */
|
|
|
|
ll_add_string(pllcomment,"pre_general",prev_comments);
|
|
|
|
prev_comments[0] = '\0';
|
2006-03-16 23:56:49 +00:00
|
|
|
current_comment_length=0;
|
2006-03-13 06:33:58 +00:00
|
|
|
}
|
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
}
|
2006-03-19 22:03:26 +00:00
|
|
|
|
2006-03-20 22:27:56 +00:00
|
|
|
/* see what kind this is, and act accordingly */
|
|
|
|
pce = _conf_get_keyinfo(section_name,term);
|
|
|
|
key_type = CONF_T_STRING;
|
|
|
|
if(pce)
|
|
|
|
key_type = pce->type;
|
|
|
|
|
|
|
|
switch(key_type) {
|
|
|
|
case CONF_T_MULTICOMMA:
|
2006-03-26 22:07:33 +00:00
|
|
|
/* first, see if we already have a tree... */
|
|
|
|
pli = ll_fetch_item(pllcurrent,term);
|
|
|
|
if(!pli) {
|
|
|
|
if((ll_create(&plltemp) != LL_E_SUCCESS)) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"Could not create "
|
|
|
|
"linked list.\n");
|
|
|
|
}
|
|
|
|
ll_add_ll(pllcurrent,term,plltemp);
|
|
|
|
ll_set_flags(plltemp,0); /* allow dups */
|
|
|
|
} else {
|
|
|
|
plltemp = pli->value.as_ll;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* got list, break comma sep and add */
|
|
|
|
if(_conf_split(value,",",&valuearray) >= 0) {
|
|
|
|
index = 0;
|
|
|
|
while(valuearray[index]) {
|
|
|
|
ll_add_string(plltemp,term,valuearray[index]);
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
_conf_dispose_split(valuearray);
|
|
|
|
} else {
|
|
|
|
ll_add_string(plltemp,term,value);
|
|
|
|
}
|
2006-03-20 22:27:56 +00:00
|
|
|
break;
|
|
|
|
case CONF_T_INT:
|
|
|
|
case CONF_T_STRING:
|
|
|
|
case CONF_T_EXISTPATH:
|
|
|
|
default:
|
|
|
|
ll_add_string(pllcurrent,term,value);
|
|
|
|
break;
|
|
|
|
}
|
2006-03-19 22:03:26 +00:00
|
|
|
|
2006-03-13 06:33:58 +00:00
|
|
|
|
|
|
|
if(comment) {
|
|
|
|
/* this is an inline comment */
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"in_%s_%s",
|
|
|
|
section_name,term);
|
|
|
|
ll_add_string(pllcomment,keybuffer,comment);
|
|
|
|
comment = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(prev_comments[0] != '\0') {
|
|
|
|
/* we had some preceding comments */
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"pre_%s_%s",
|
|
|
|
section_name, term);
|
|
|
|
ll_add_string(pllcomment,keybuffer,prev_comments);
|
|
|
|
prev_comments[0] = '\0';
|
2006-03-16 23:56:49 +00:00
|
|
|
current_comment_length=0;
|
2006-03-13 06:33:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ws=1;
|
2006-02-08 23:02:43 +00:00
|
|
|
}
|
2006-03-13 06:33:58 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
if(((term) && (strlen(term))) && (!value)) {
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Error in config file on line %d\n",line);
|
|
|
|
ll_destroy(pllnew);
|
|
|
|
return CONF_E_PARSE;
|
2006-02-05 00:22:46 +00:00
|
|
|
}
|
|
|
|
}
|
2006-03-13 06:33:58 +00:00
|
|
|
|
|
|
|
if((comment)||(ws)) {
|
|
|
|
if(!comment)
|
|
|
|
comment = "";
|
|
|
|
|
|
|
|
|
|
|
|
/* add to prev comments */
|
2006-03-16 23:56:49 +00:00
|
|
|
while((current_comment_length + (int)strlen(comment) + 2 >=
|
|
|
|
total_comment_length) && (total_comment_length < 32768)) {
|
|
|
|
total_comment_length *= 2;
|
|
|
|
DPRINTF(E_DBG,L_CONF,"Expanding precomments to %d\n",
|
|
|
|
total_comment_length);
|
|
|
|
prev_comments=realloc(prev_comments,total_comment_length);
|
|
|
|
if(!prev_comments)
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"Malloc error\n");
|
2006-03-13 06:33:58 +00:00
|
|
|
}
|
|
|
|
|
2006-03-16 23:56:49 +00:00
|
|
|
if(current_comment_length + (int)strlen(comment)+2 >=
|
|
|
|
total_comment_length) {
|
|
|
|
if(!warned_truncate)
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Truncating comments in config\n");
|
|
|
|
warned_truncate=1;
|
2006-03-13 06:33:58 +00:00
|
|
|
} else {
|
2006-03-16 23:56:49 +00:00
|
|
|
if(strlen(comment)) {
|
|
|
|
strcat(prev_comments,"#");
|
|
|
|
strcat(prev_comments,comment);
|
|
|
|
current_comment_length += ((int) strlen(comment) + 1);
|
|
|
|
} else {
|
|
|
|
strcat(prev_comments,"\n");
|
|
|
|
current_comment_length += 2; /* windows, worst case */
|
|
|
|
}
|
2006-03-13 06:33:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-03-20 22:27:56 +00:00
|
|
|
|
2006-03-13 06:33:58 +00:00
|
|
|
if(section_name)
|
|
|
|
free(section_name);
|
2006-03-20 22:27:56 +00:00
|
|
|
|
2006-03-13 06:33:58 +00:00
|
|
|
if(prev_comments) {
|
|
|
|
if(prev_comments[0] != '\0') {
|
|
|
|
ll_add_string(pllcomment,"end",prev_comments);
|
|
|
|
}
|
|
|
|
free(prev_comments);
|
2006-02-05 00:22:46 +00:00
|
|
|
}
|
2006-01-24 23:41:05 +00:00
|
|
|
|
2006-01-25 22:31:53 +00:00
|
|
|
fclose(fin);
|
2006-02-05 00:22:46 +00:00
|
|
|
|
|
|
|
/* Sanity check */
|
2006-02-21 03:08:14 +00:00
|
|
|
if(_conf_verify(pllnew)) {
|
|
|
|
DPRINTF(E_INF,L_CONF,"Loading new config file.\n");
|
|
|
|
_conf_lock();
|
|
|
|
if(conf_main) {
|
|
|
|
ll_destroy(conf_main);
|
|
|
|
}
|
2006-03-13 06:33:58 +00:00
|
|
|
|
|
|
|
if(conf_comments) {
|
|
|
|
ll_destroy(conf_comments);
|
|
|
|
}
|
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
conf_main = pllnew;
|
2006-03-13 06:33:58 +00:00
|
|
|
conf_comments = pllcomment;
|
2006-02-21 03:08:14 +00:00
|
|
|
_conf_unlock();
|
|
|
|
} else {
|
|
|
|
ll_destroy(pllnew);
|
2006-03-13 06:33:58 +00:00
|
|
|
ll_destroy(pllcomment);
|
2006-02-21 03:08:14 +00:00
|
|
|
DPRINTF(E_LOG,L_CONF,"Could not validate config file. Ignoring\n");
|
2006-05-24 04:19:44 +00:00
|
|
|
return CONF_E_BADCONFIG;
|
2006-02-21 03:08:14 +00:00
|
|
|
}
|
2006-02-05 00:22:46 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
return CONF_E_SUCCESS;
|
2006-01-24 23:41:05 +00:00
|
|
|
}
|
|
|
|
|
2006-02-20 03:56:47 +00:00
|
|
|
/**
|
|
|
|
* do final config file shutdown
|
|
|
|
*/
|
2006-02-21 03:08:14 +00:00
|
|
|
int conf_close(void) {
|
2006-02-21 23:19:53 +00:00
|
|
|
if(conf_main) {
|
2006-02-21 03:08:14 +00:00
|
|
|
ll_destroy(conf_main);
|
2006-02-21 23:19:53 +00:00
|
|
|
conf_main = NULL;
|
|
|
|
}
|
|
|
|
|
2006-03-13 06:33:58 +00:00
|
|
|
if(conf_comments) {
|
|
|
|
ll_destroy(conf_comments);
|
|
|
|
conf_comments = NULL;
|
|
|
|
}
|
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
if(conf_main_file) {
|
|
|
|
free(conf_main_file);
|
|
|
|
conf_main_file = NULL;
|
|
|
|
}
|
2006-01-24 23:41:05 +00:00
|
|
|
|
2006-02-08 23:02:43 +00:00
|
|
|
return CONF_E_SUCCESS;
|
2006-01-24 23:41:05 +00:00
|
|
|
}
|
|
|
|
|
2006-02-20 03:56:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* read a value from the CURRENT config tree as an integer
|
|
|
|
*
|
|
|
|
* @param section section name to search in
|
|
|
|
* @param key key to search for
|
2006-02-21 03:08:14 +00:00
|
|
|
* @param dflt default value to return if key not found
|
|
|
|
* @returns value as integer if found, dflt value otherwise
|
2006-02-20 03:56:47 +00:00
|
|
|
*/
|
2006-02-21 03:08:14 +00:00
|
|
|
int conf_get_int(char *section, char *key, int dflt) {
|
2006-02-20 03:56:47 +00:00
|
|
|
LL_ITEM *pitem;
|
2006-02-21 03:08:14 +00:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
_conf_lock();
|
|
|
|
pitem = _conf_fetch_item(conf_main,section,key);
|
|
|
|
if((!pitem) || (pitem->type != LL_TYPE_STRING)) {
|
|
|
|
retval = dflt;
|
|
|
|
} else {
|
|
|
|
retval = atoi(pitem->value.as_string);
|
|
|
|
}
|
|
|
|
_conf_unlock();
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
return retval;
|
|
|
|
}
|
2006-02-20 03:56:47 +00:00
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
/**
|
|
|
|
* read a value from the CURRENT config tree as a string
|
|
|
|
*
|
|
|
|
* @param section section name to search in
|
|
|
|
* @param key key to search for
|
|
|
|
* @param dflt default value to return if key not found
|
|
|
|
* @param out buffer to put resulting string in
|
|
|
|
* @param size pointer to size of buffer
|
|
|
|
* @returns CONF_E_SUCCESS with out filled on success,
|
|
|
|
* or CONF_E_OVERFLOW, with size set to required buffer size
|
|
|
|
*/
|
|
|
|
int conf_get_string(char *section, char *key, char *dflt, char *out, int *size) {
|
|
|
|
LL_ITEM *pitem;
|
|
|
|
char *result;
|
|
|
|
int len;
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
_conf_lock();
|
|
|
|
pitem = _conf_fetch_item(conf_main,section,key);
|
|
|
|
if((!pitem) || (pitem->type != LL_TYPE_STRING)) {
|
|
|
|
result = dflt;
|
|
|
|
} else {
|
|
|
|
result = pitem->value.as_string;
|
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-27 23:39:11 +00:00
|
|
|
if(!result) {
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_NOTFOUND;
|
|
|
|
}
|
|
|
|
|
2006-02-27 22:55:05 +00:00
|
|
|
len = (int) strlen(result) + 1;
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
if(len <= *size) {
|
|
|
|
*size = len;
|
|
|
|
strcpy(out,result);
|
|
|
|
} else {
|
|
|
|
_conf_unlock();
|
|
|
|
*size = len;
|
|
|
|
return CONF_E_OVERFLOW;
|
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-03-25 10:52:10 +00:00
|
|
|
/**
|
|
|
|
* return the value from the CURRENT config tree as an allocated string
|
|
|
|
*
|
|
|
|
* @param section section name to search in
|
|
|
|
* @param key key to search for
|
|
|
|
* @param dflt default value to return if key not found
|
|
|
|
* @returns a pointer to an allocated string containing the required
|
|
|
|
* configuration key
|
|
|
|
*/
|
2006-03-26 22:07:33 +00:00
|
|
|
char *conf_alloc_string(char *section, char *key, char *dflt) {
|
|
|
|
LL_ITEM *pitem;
|
|
|
|
char *result;
|
|
|
|
char *retval;
|
2006-03-25 10:52:10 +00:00
|
|
|
|
2006-03-26 22:07:33 +00:00
|
|
|
_conf_lock();
|
|
|
|
pitem = _conf_fetch_item(conf_main,section,key);
|
|
|
|
if((!pitem) || (pitem->type != LL_TYPE_STRING)) {
|
|
|
|
result = dflt;
|
|
|
|
} else {
|
|
|
|
result = pitem->value.as_string;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(result == NULL) {
|
|
|
|
_conf_unlock();
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-03-25 10:52:10 +00:00
|
|
|
|
2006-03-26 22:07:33 +00:00
|
|
|
retval = strdup(result);
|
2006-03-25 10:52:10 +00:00
|
|
|
|
2006-03-26 22:07:33 +00:00
|
|
|
if(!retval) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"Malloc error in conf_alloc_string\n");
|
|
|
|
}
|
|
|
|
_conf_unlock();
|
|
|
|
return retval;
|
2006-03-25 10:52:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
/**
|
|
|
|
* set (update) the config tree with a particular value.
|
|
|
|
* this accepts an int, but it actually adds it as a string.
|
|
|
|
* in that sense, it's really just a wrapper for conf_set_string
|
|
|
|
*
|
|
|
|
* @param section section that the key is in
|
|
|
|
* @param key key to update
|
|
|
|
* @param value value to set it to
|
|
|
|
* @returns E_CONF_SUCCESS on success, error code otherwise
|
|
|
|
*/
|
2006-05-16 06:39:09 +00:00
|
|
|
int conf_set_int(char *section, char *key, int value, int verify) {
|
2006-02-21 23:19:53 +00:00
|
|
|
char buffer[40]; /* ?? */
|
|
|
|
snprintf(buffer,sizeof(buffer),"%d",value);
|
|
|
|
|
2006-05-16 06:39:09 +00:00
|
|
|
return conf_set_string(section, key, buffer, verify);
|
2006-02-21 23:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set (update) the config tree with a particular string value
|
|
|
|
*
|
|
|
|
* @param section section that the key is in
|
|
|
|
* @param key key to update
|
|
|
|
* @param value value to set it to
|
|
|
|
* @returns E_CONF_SUCCESS on success, error code otherwise
|
|
|
|
*/
|
2006-05-16 06:39:09 +00:00
|
|
|
int conf_set_string(char *section, char *key, char *value, int verify) {
|
2006-02-21 23:19:53 +00:00
|
|
|
LL_ITEM *pitem;
|
|
|
|
LL_ITEM *psection;
|
|
|
|
LL *section_ll;
|
2006-04-10 06:33:57 +00:00
|
|
|
LL *temp_ll;
|
|
|
|
CONF_ELEMENTS *pce;
|
|
|
|
int key_type = CONF_T_STRING;
|
|
|
|
char **valuearray;
|
|
|
|
int index;
|
2006-02-21 23:19:53 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
_conf_lock();
|
2006-05-16 06:22:32 +00:00
|
|
|
|
|
|
|
/* verify the item */
|
2006-05-16 06:39:09 +00:00
|
|
|
err=_conf_verify_element(section,key,value);
|
|
|
|
if(err != CONF_E_SUCCESS) {
|
|
|
|
_conf_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(verify) {
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
}
|
2006-05-16 06:22:32 +00:00
|
|
|
|
2006-04-10 06:33:57 +00:00
|
|
|
pce = _conf_get_keyinfo(section,key);
|
|
|
|
if(pce)
|
|
|
|
key_type = pce->type;
|
|
|
|
|
2006-05-18 05:11:07 +00:00
|
|
|
if(strcmp(value,"") == 0) {
|
|
|
|
/* deleting the item */
|
|
|
|
pitem = ll_fetch_item(conf_main,section);
|
|
|
|
if(!pitem) {
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
section_ll = pitem->value.as_ll;
|
|
|
|
if(!section_ll) {
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_SUCCESS; /* ?? deleting an already deleted item */
|
|
|
|
}
|
|
|
|
|
2006-05-19 05:10:08 +00:00
|
|
|
/* don't care about item... might already be gone! */
|
|
|
|
ll_del_item(section_ll,key);
|
2006-05-18 05:11:07 +00:00
|
|
|
_conf_unlock();
|
2006-05-19 05:10:08 +00:00
|
|
|
return CONF_E_SUCCESS;
|
2006-05-18 05:11:07 +00:00
|
|
|
}
|
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
pitem = _conf_fetch_item(conf_main,section,key);
|
|
|
|
if(!pitem) {
|
|
|
|
/* fetch the section and add it to that list */
|
|
|
|
if(!(psection = ll_fetch_item(conf_main,section))) {
|
|
|
|
/* that subkey doesn't exist yet... */
|
|
|
|
if((err = ll_create(§ion_ll)) != LL_E_SUCCESS) {
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Could not create linked list: %d\n",err);
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_UNKNOWN;
|
|
|
|
}
|
|
|
|
if((err=ll_add_ll(conf_main,section,section_ll)) != LL_E_SUCCESS) {
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Error inserting new subkey: %d\n",err);
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_UNKNOWN;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
section_ll = psection->value.as_ll;
|
|
|
|
}
|
|
|
|
/* have the section, now add it */
|
2006-04-10 06:33:57 +00:00
|
|
|
if(key_type == CONF_T_MULTICOMMA) {
|
|
|
|
if((err = ll_create(&temp_ll)) != LL_E_SUCCESS) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"conf_set_string: could not create ll\n");
|
|
|
|
}
|
|
|
|
ll_add_ll(section_ll,key,temp_ll);
|
|
|
|
ll_set_flags(temp_ll,0); /* allow dups */
|
|
|
|
if(_conf_split(value,",",&valuearray) >= 0) {
|
|
|
|
index = 0;
|
|
|
|
while(valuearray[index]) {
|
|
|
|
ll_add_string(temp_ll,key,valuearray[index]);
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
_conf_dispose_split(valuearray);
|
|
|
|
}
|
2006-04-10 17:59:10 +00:00
|
|
|
} else {
|
2006-04-10 06:33:57 +00:00
|
|
|
if((err = ll_add_string(section_ll,key,value)) != LL_E_SUCCESS) {
|
|
|
|
DPRINTF(E_LOG,L_CONF,"Error in conf_set_string: "
|
|
|
|
"(%s/%s)\n",section,key);
|
|
|
|
_conf_unlock();
|
|
|
|
return CONF_E_UNKNOWN;
|
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* we have the item, let's update it */
|
2006-04-10 06:33:57 +00:00
|
|
|
if(key_type == CONF_T_MULTICOMMA) {
|
|
|
|
/* delete whatever is there, then add from commas */
|
|
|
|
ll_destroy(pitem->value.as_ll);
|
|
|
|
if(ll_create(&pitem->value.as_ll) != LL_E_SUCCESS) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,
|
|
|
|
"conf_set_string: could not create ll\n");
|
|
|
|
}
|
|
|
|
ll_set_flags(pitem->value.as_ll,0); /* allow dups */
|
|
|
|
if(_conf_split(value,",",&valuearray) >= 0) {
|
|
|
|
index = 0;
|
|
|
|
while(valuearray[index]) {
|
|
|
|
ll_add_string(pitem->value.as_ll,key,valuearray[index]);
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
_conf_dispose_split(valuearray);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ll_update_string(pitem,value);
|
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
}
|
|
|
|
|
2006-02-21 03:08:14 +00:00
|
|
|
_conf_unlock();
|
2006-04-10 06:33:57 +00:00
|
|
|
return conf_write();
|
2006-02-20 03:56:47 +00:00
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* determine if the configuration file is writable
|
|
|
|
*
|
|
|
|
* @returns TRUE if writable, FALSE otherwise
|
|
|
|
*/
|
|
|
|
int conf_iswritable(void) {
|
|
|
|
FILE *fp;
|
|
|
|
int retval = FALSE;
|
|
|
|
|
|
|
|
/* don't want configfile reopened under us */
|
|
|
|
_conf_lock();
|
|
|
|
|
|
|
|
if(!conf_main_file)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if((fp = fopen(conf_main_file,"r+")) != NULL) {
|
|
|
|
fclose(fp);
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
_conf_unlock();
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* write the current config tree back to the config file
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int conf_write(void) {
|
2006-04-10 06:33:57 +00:00
|
|
|
int retval = CONF_E_NOTWRITABLE;
|
2006-02-21 23:19:53 +00:00
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
if(!conf_main_file) {
|
|
|
|
return CONF_E_NOCONF;
|
|
|
|
}
|
|
|
|
|
|
|
|
_conf_lock();
|
|
|
|
if((fp = fopen(conf_main_file,"w+")) != NULL) {
|
2006-03-13 06:33:58 +00:00
|
|
|
retval = _conf_write(fp,conf_main,0,NULL);
|
2006-02-21 23:19:53 +00:00
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
_conf_unlock();
|
|
|
|
|
2006-04-10 06:33:57 +00:00
|
|
|
return retval ? CONF_E_SUCCESS : CONF_E_NOTWRITABLE;
|
2006-02-21 23:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do the actual work of writing the config file
|
|
|
|
*
|
|
|
|
* @param fp file we are writing the config file to
|
|
|
|
* @param pll list we are dumping k/v pairs for
|
|
|
|
* @param sublevel whether this is the root, or a subkey
|
|
|
|
* @returns TRUE on success, FALSE otherwise
|
|
|
|
*/
|
2006-03-13 06:33:58 +00:00
|
|
|
int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
|
2006-02-21 23:19:53 +00:00
|
|
|
LL_ITEM *pli;
|
2006-03-13 06:33:58 +00:00
|
|
|
LL_ITEM *ppre, *pin;
|
2006-03-26 22:07:33 +00:00
|
|
|
LL_ITEM *plitemp;
|
|
|
|
|
2006-03-13 06:33:58 +00:00
|
|
|
char keybuffer[256];
|
2006-02-21 23:19:53 +00:00
|
|
|
|
|
|
|
if(!pll)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/* write all the solo keys, first! */
|
|
|
|
pli = pll->itemlist.next;
|
|
|
|
while(pli) {
|
2006-03-13 06:33:58 +00:00
|
|
|
/* if there is a PRE there, then let's emit that*/
|
|
|
|
if(sublevel) {
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"pre_%s_%s",parent,pli->key);
|
|
|
|
ppre=ll_fetch_item(conf_comments,keybuffer);
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"in_%s_%s",parent,pli->key);
|
|
|
|
pin = ll_fetch_item(conf_comments,keybuffer);
|
|
|
|
} else {
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"pre_%s",pli->key);
|
|
|
|
ppre=ll_fetch_item(conf_comments,keybuffer);
|
|
|
|
snprintf(keybuffer,sizeof(keybuffer),"in_%s",pli->key);
|
|
|
|
pin = ll_fetch_item(conf_comments,keybuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(ppre) {
|
|
|
|
fprintf(fp,"%s",ppre->value.as_string);
|
|
|
|
}
|
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
switch(pli->type) {
|
|
|
|
case LL_TYPE_LL:
|
|
|
|
if(sublevel) {
|
2006-03-26 22:07:33 +00:00
|
|
|
/* must be multivalued */
|
|
|
|
plitemp = NULL;
|
|
|
|
while((plitemp = ll_get_next(pli->value.as_ll,plitemp))) {
|
|
|
|
fprintf(fp,"%s = %s\n",pli->key,plitemp->value.as_string);
|
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
} else {
|
2006-03-13 06:33:58 +00:00
|
|
|
fprintf(fp,"[%s]",pli->key);
|
|
|
|
if(pin) {
|
|
|
|
fprintf(fp," #%s",pin->value.as_string);
|
|
|
|
}
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
|
|
|
|
if(!_conf_write(fp, pli->value.as_ll, 1, pli->key))
|
2006-02-21 23:19:53 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
break;
|
2006-03-13 06:33:58 +00:00
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
case LL_TYPE_INT:
|
2006-03-13 06:33:58 +00:00
|
|
|
fprintf(fp,"%s = %d",pli->key,pli->value.as_int);
|
|
|
|
if(pin) {
|
|
|
|
fprintf(fp," #%s",pin->value.as_string);
|
|
|
|
}
|
|
|
|
fprintf(fp,"\n");
|
2006-02-21 23:19:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LL_TYPE_STRING:
|
2006-03-13 06:33:58 +00:00
|
|
|
fprintf(fp,"%s = %s",pli->key,pli->value.as_string);
|
|
|
|
if(pin) {
|
|
|
|
fprintf(fp," #%s",pin->value.as_string);
|
|
|
|
}
|
|
|
|
fprintf(fp,"\n");
|
2006-02-21 23:19:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pli = pli->next;
|
|
|
|
}
|
|
|
|
|
2006-03-13 06:33:58 +00:00
|
|
|
if(!sublevel) {
|
|
|
|
pin = ll_fetch_item(conf_comments,"end");
|
|
|
|
if(pin) {
|
|
|
|
fprintf(fp,"%s",pin->value.as_string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-21 23:19:53 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-27 22:48:42 +00:00
|
|
|
/**
|
|
|
|
* determine if a configuration entry is actually set
|
|
|
|
*
|
|
|
|
* @param section section to test
|
|
|
|
* @key key to check
|
|
|
|
* @return TRUE if set, FALSE otherwise
|
|
|
|
*/
|
|
|
|
int conf_isset(char *section, char *key) {
|
|
|
|
int retval = FALSE;
|
2006-03-20 22:27:56 +00:00
|
|
|
|
2006-02-27 22:48:42 +00:00
|
|
|
_conf_lock();
|
|
|
|
if(_conf_fetch_item(conf_main,section,key)) {
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
_conf_unlock();
|
2006-03-20 22:27:56 +00:00
|
|
|
|
2006-02-27 22:48:42 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2006-03-20 22:27:56 +00:00
|
|
|
/**
|
|
|
|
* split a string on delimiter boundaries, filling
|
|
|
|
* a string-pointer array.
|
|
|
|
*
|
|
|
|
* The user must free both the first element in the array,
|
|
|
|
* and the array itself.
|
|
|
|
*
|
|
|
|
* @param s string to split
|
|
|
|
* @param delimiters boundaries to split on
|
|
|
|
* @param argvp an argv array to be filled
|
|
|
|
* @returns number of tokens
|
|
|
|
*/
|
|
|
|
int _conf_split(char *s, char *delimiters, char ***argvp) {
|
|
|
|
int i;
|
|
|
|
int numtokens;
|
|
|
|
const char *snew;
|
|
|
|
char *t;
|
|
|
|
char *tokptr;
|
2006-03-26 22:07:33 +00:00
|
|
|
char *tmp;
|
2006-03-20 22:27:56 +00:00
|
|
|
|
|
|
|
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL))
|
|
|
|
return -1;
|
|
|
|
*argvp = NULL;
|
|
|
|
snew = s + strspn(s, delimiters);
|
|
|
|
if ((t = malloc(strlen(snew) + 1)) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
strcpy(t, snew);
|
|
|
|
numtokens = 0;
|
|
|
|
tokptr = NULL;
|
2006-03-26 22:07:33 +00:00
|
|
|
tmp = t;
|
|
|
|
while(strtok_r(tmp,delimiters,&tokptr) != NULL) {
|
|
|
|
tmp=NULL;
|
2006-03-20 22:27:56 +00:00
|
|
|
numtokens++;
|
2006-03-26 22:07:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_DBG,L_CONF,"Found %d tokens in %s\n",numtokens,s);
|
2006-03-20 22:27:56 +00:00
|
|
|
|
|
|
|
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
|
|
|
|
free(t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numtokens == 0)
|
|
|
|
free(t);
|
|
|
|
else {
|
|
|
|
strcpy(t, snew);
|
|
|
|
tokptr = NULL;
|
2006-03-26 22:07:33 +00:00
|
|
|
tmp = t;
|
|
|
|
for (i = 0; i < numtokens; i++) {
|
|
|
|
*((*argvp) + i) = strtok_r(tmp, delimiters, &tokptr);
|
|
|
|
tmp=NULL;
|
|
|
|
DPRINTF(E_DBG,L_CONF,"Token %d: %s\n",i+1,(*argvp)[i]);
|
|
|
|
}
|
2006-03-20 22:27:56 +00:00
|
|
|
}
|
2006-02-21 23:19:53 +00:00
|
|
|
|
2006-03-20 22:27:56 +00:00
|
|
|
*((*argvp) + numtokens) = NULL;
|
|
|
|
return numtokens;
|
|
|
|
}
|
2006-03-26 22:07:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* implode a multivalued term in a perl sense.
|
|
|
|
*
|
|
|
|
* @param section section of term to implode
|
|
|
|
* @param key key of term to implode
|
|
|
|
* @pararm delimiter what to "glue" them with
|
|
|
|
* @returns imploded string (preallocated), or NULL
|
|
|
|
*/
|
|
|
|
char *conf_implode(char *section, char *key, char *delimiter) {
|
|
|
|
LL_ITEM *pitem;
|
|
|
|
LL_ITEM *penum;
|
|
|
|
int count;
|
|
|
|
int len;
|
|
|
|
char *retval;
|
|
|
|
|
|
|
|
_conf_lock();
|
|
|
|
pitem = _conf_fetch_item(conf_main,section,key);
|
|
|
|
if((!pitem) || (pitem->type != LL_TYPE_LL)) {
|
|
|
|
_conf_unlock();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise, alloc a string and go */
|
|
|
|
count = len = 0;
|
|
|
|
penum = NULL;
|
|
|
|
while((penum = ll_get_next(pitem->value.as_ll,penum))) {
|
|
|
|
if(penum->type != LL_TYPE_STRING) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"multivalued property not a string?\n");
|
|
|
|
}
|
2006-03-29 22:02:42 +00:00
|
|
|
len += (int)strlen(penum->value.as_string);
|
2006-03-26 22:07:33 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!count) {
|
|
|
|
_conf_unlock();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-03-29 22:02:42 +00:00
|
|
|
len += ((int)strlen(delimiter) * (count-1));
|
2006-03-26 22:07:33 +00:00
|
|
|
retval = (char*)malloc(len + 1);
|
|
|
|
if(!retval) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"conf_implode: malloc\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(retval,0,len+1);
|
|
|
|
penum = NULL;
|
|
|
|
while((penum = ll_get_next(pitem->value.as_ll,penum))) {
|
|
|
|
strcat(retval,penum->value.as_string);
|
|
|
|
if(--count) {
|
|
|
|
strcat(retval,delimiter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_conf_unlock();
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dispose of the argv set that was created in _conf_split
|
|
|
|
*
|
|
|
|
* @param argv string array to delete
|
|
|
|
*/
|
|
|
|
void _conf_dispose_split(char **argv) {
|
|
|
|
if(!argv)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(argv[0])
|
|
|
|
free(argv[0]);
|
|
|
|
|
|
|
|
free(argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return a multi-valued item as an array (values)
|
|
|
|
*
|
|
|
|
* @param section section to fetch
|
|
|
|
* @param key multivalued key to get from array
|
|
|
|
* @returns TRUE on success, FALSE on failure
|
|
|
|
*/
|
|
|
|
int conf_get_array(char *section, char *key, char ***argvp) {
|
|
|
|
LL_ITEM *pitem, *penum;
|
|
|
|
int count;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
_conf_lock();
|
|
|
|
pitem = _conf_fetch_item(conf_main,section,key);
|
|
|
|
if((!pitem) || (pitem->type != LL_TYPE_LL)) {
|
|
|
|
_conf_unlock();
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise, alloc a string and go */
|
|
|
|
count = 0;
|
|
|
|
penum = NULL;
|
|
|
|
while((penum = ll_get_next(pitem->value.as_ll,penum))) {
|
|
|
|
if(penum->type != LL_TYPE_STRING) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"multivalued property not a string?\n");
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now we have a count, alloc an argv */
|
|
|
|
len = (count+1) * sizeof(char*);
|
|
|
|
*(argvp) = (char**)malloc(len);
|
|
|
|
if(!*(argvp)) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"conf_get_array: malloc\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(*(argvp),0,len);
|
|
|
|
|
|
|
|
count=0;
|
|
|
|
penum=NULL;
|
|
|
|
while((penum = ll_get_next(pitem->value.as_ll,penum))) {
|
|
|
|
(*argvp)[count] = strdup(penum->value.as_string);
|
|
|
|
if(!(*argvp)[count]) {
|
|
|
|
DPRINTF(E_FATAL,L_CONF,"conf_get_array: malloc\n");
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
_conf_unlock();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dispose of the array created above
|
|
|
|
*
|
|
|
|
* @param argv argv pointer created
|
|
|
|
*/
|
|
|
|
void conf_dispose_array(char **argv) {
|
|
|
|
int index=0;
|
|
|
|
|
|
|
|
if(!argv)
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
while(argv[index]) {
|
|
|
|
free(argv[index]);
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
}
|
2006-04-04 06:31:19 +00:00
|
|
|
|
|
|
|
/* FIXME: this belongs in xml-rpc, but need config enumerating fns */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dump the config to xml
|
2006-04-10 17:59:10 +00:00
|
|
|
*
|
2006-04-04 06:31:19 +00:00
|
|
|
* @param pwsc web connection to dump to
|
|
|
|
* @returns TRUE on success, FALSE otherwise
|
|
|
|
*/
|
|
|
|
int conf_xml_dump(WS_CONNINFO *pwsc) {
|
|
|
|
XMLSTRUCT *pxml;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if(!conf_main_file) {
|
|
|
|
return FALSE; /* CONF_E_NOCONF */
|
|
|
|
}
|
|
|
|
|
|
|
|
pxml = xml_init(pwsc,1);
|
|
|
|
xml_push(pxml,"config");
|
|
|
|
|
|
|
|
_conf_lock();
|
|
|
|
|
|
|
|
retval = _conf_xml_dump(pxml,conf_main,0,NULL);
|
|
|
|
|
|
|
|
_conf_unlock();
|
|
|
|
|
|
|
|
xml_pop(pxml);
|
|
|
|
xml_deinit(pxml);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* do the actual work of dumping the config file
|
|
|
|
*
|
|
|
|
* @param pwsc web connection we are writing the config file to
|
|
|
|
* @param pll list we are dumping k/v pairs for
|
|
|
|
* @param sublevel whether this is the root, or a subkey
|
|
|
|
* @returns TRUE on success, FALSE otherwise
|
|
|
|
*/
|
|
|
|
int _conf_xml_dump(XMLSTRUCT *pxml, LL *pll, int sublevel, char *parent) {
|
|
|
|
LL_ITEM *pli;
|
|
|
|
LL_ITEM *plitemp;
|
|
|
|
|
|
|
|
if(!pll)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/* write all the solo keys, first! */
|
|
|
|
pli = pll->itemlist.next;
|
|
|
|
while(pli) {
|
|
|
|
switch(pli->type) {
|
|
|
|
case LL_TYPE_LL:
|
|
|
|
if(sublevel) {
|
|
|
|
/* must be multivalued */
|
|
|
|
plitemp = NULL;
|
2006-04-10 06:33:57 +00:00
|
|
|
xml_push(pxml,pli->key);
|
2006-04-04 06:31:19 +00:00
|
|
|
while((plitemp = ll_get_next(pli->value.as_ll,plitemp))) {
|
2006-04-10 06:33:57 +00:00
|
|
|
xml_output(pxml,"item","%s",plitemp->value.as_string);
|
2006-04-04 06:31:19 +00:00
|
|
|
}
|
|
|
|
xml_pop(pxml);
|
|
|
|
} else {
|
|
|
|
xml_push(pxml,pli->key);
|
|
|
|
if(!_conf_xml_dump(pxml, pli->value.as_ll, 1, pli->key))
|
|
|
|
return FALSE;
|
|
|
|
xml_pop(pxml);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LL_TYPE_INT:
|
|
|
|
xml_output(pxml,pli->key,"%d",pli->value.as_int);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LL_TYPE_STRING:
|
|
|
|
xml_output(pxml,pli->key,"%s",pli->value.as_string);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pli = pli->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2006-04-10 17:59:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the filename of the currently runnig config file
|
|
|
|
*
|
|
|
|
* @returns path if it exists, or NULL if no config file opened
|
|
|
|
*/
|
|
|
|
char *conf_get_filename(void) {
|
|
|
|
return conf_main_file;
|
|
|
|
}
|
2006-05-24 03:53:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* this is an ugly block of crap to carry around every
|
|
|
|
* time one wants the servername.
|
|
|
|
*/
|
|
|
|
char *conf_get_servername(void) {
|
|
|
|
char *retval;
|
|
|
|
char newname[HOST_NAME_MAX + 1 + 16]; /* " Firefly Server" */
|
|
|
|
|
|
|
|
gethostname(newname,HOST_NAME_MAX);
|
|
|
|
retval = strchr(newname,'.');
|
|
|
|
if(retval) *retval = '\0';
|
|
|
|
|
|
|
|
retval = newname;
|
|
|
|
while(*retval) {
|
|
|
|
*retval = tolower(*retval);
|
|
|
|
retval++;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcat(newname," Firefly Server");
|
|
|
|
|
|
|
|
return conf_alloc_string("general","servername",newname);
|
|
|
|
}
|