/* dircproxy
* Copyright (C) 2002 Scott James Remnant <scott@netsplit.com>.
* All Rights Reserved.
*
* cfgfile.c
* - reading of configuration file
* --
* @(#) $Id: cfgfile.c,v 1.41 2002/02/06 10:07:42 scott Exp $
*
* This file is distributed according to the GNU General Public
* License. For full details, read the top of 'main.c' or the
* file called COPYING that was distributed with this code.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dircproxy.h>
#include "sprintf.h"
#include "irc_net.h"
#include "cfgfile.h"
/* forward declaration */
static int _cfg_read_bool(char **, int *);
static int _cfg_read_numeric(char **, long *);
static int _cfg_read_string(char **, char **);
static int _cfg_read_pair(char **, long **);
/* Whitespace */
#define WS " \t\r\n"
/* Quick and easy "Unmatched Quote" define */
#define UNMATCHED_QUOTE { error("Unmatched quote for key '%s' " \
"at line %ld of %s", key, line, \
filename); valid = 0; break; }
/* Read a config file */
int cfg_read(const char *filename, char **listen_port, char **pid_file,
struct globalvars *globals) {
struct ircconnclass defaults, *def, *class;
int valid;
long line;
FILE *fd;
def = &defaults;
memset(globals, 0, sizeof(struct globalvars));
memset(def, 0, sizeof(struct ircconnclass));
class = 0;
line = 0;
valid = 1;
fd = fopen(filename, "r");
if (!fd)
return -1;
/* Initialise globals */
globals->client_timeout = DEFAULT_CLIENT_TIMEOUT;
globals->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
globals->dns_timeout = DEFAULT_DNS_TIMEOUT;
/* Initialise using defaults */
def->server_port = x_strdup(DEFAULT_SERVER_PORT ? DEFAULT_SERVER_PORT : "0");
def->server_retry = DEFAULT_SERVER_RETRY;
def->server_maxattempts = DEFAULT_SERVER_MAXATTEMPTS;
def->server_maxinitattempts = DEFAULT_SERVER_MAXINITATTEMPTS;
def->server_keepalive = DEFAULT_SERVER_KEEPALIVE;
def->server_pingtimeout = DEFAULT_SERVER_PINGTIMEOUT;
if (DEFAULT_SERVER_THROTTLE_BYTES || DEFAULT_SERVER_THROTTLE_PERIOD) {
def->server_throttle = (long *)malloc(sizeof(long) * 2);
def->server_throttle[0] = DEFAULT_SERVER_THROTTLE_BYTES;
def->server_throttle[1] = DEFAULT_SERVER_THROTTLE_PERIOD;
}
def->server_autoconnect = DEFAULT_SERVER_AUTOCONNECT;
def->channel_rejoin = DEFAULT_CHANNEL_REJOIN;
def->channel_leave_on_detach = DEFAULT_CHANNEL_LEAVE_ON_DETACH;
def->channel_rejoin_on_attach = DEFAULT_CHANNEL_REJOIN_ON_ATTACH;
def->idle_maxtime = DEFAULT_IDLE_MAXTIME;
def->disconnect_existing = DEFAULT_DISCONNECT_EXISTING;
def->disconnect_on_detach = DEFAULT_DISCONNECT_ON_DETACH;
def->initial_modes = (DEFAULT_INITIAL_MODES
? x_strdup(DEFAULT_INITIAL_MODES) : 0);
def->drop_modes = (DEFAULT_DROP_MODES ? x_strdup(DEFAULT_DROP_MODES) : 0);
def->refuse_modes = (DEFAULT_REFUSE_MODES
? x_strdup(DEFAULT_REFUSE_MODES) : 0);
def->local_address = (DEFAULT_LOCAL_ADDRESS
? x_strdup(DEFAULT_LOCAL_ADDRESS) : 0);
def->away_message = (DEFAULT_AWAY_MESSAGE
? x_strdup(DEFAULT_AWAY_MESSAGE) : 0);
def->quit_message = (DEFAULT_QUIT_MESSAGE
? x_strdup(DEFAULT_QUIT_MESSAGE) : 0);
def->attach_message = (DEFAULT_ATTACH_MESSAGE
? x_strdup(DEFAULT_ATTACH_MESSAGE) : 0);
def->detach_message = (DEFAULT_DETACH_MESSAGE
? x_strdup(DEFAULT_DETACH_MESSAGE) : 0);
def->detach_nickname = (DEFAULT_DETACH_NICKNAME
? x_strdup(DEFAULT_DETACH_NICKNAME) : 0);
def->nick_keep = DEFAULT_NICK_KEEP;
def->ctcp_replies = DEFAULT_CTCP_REPLIES;
def->chan_log_enabled = DEFAULT_CHAN_LOG_ENABLED;
def->chan_log_always = DEFAULT_CHAN_LOG_ALWAYS;
def->chan_log_maxsize = DEFAULT_CHAN_LOG_MAXSIZE;
def->chan_log_recall = DEFAULT_CHAN_LOG_RECALL;
def->chan_log_timestamp = DEFAULT_CHAN_LOG_TIMESTAMP;
def->chan_log_relativetime = DEFAULT_CHAN_LOG_RELATIVETIME;
def->chan_log_copydir = (DEFAULT_CHAN_LOG_COPYDIR
? x_strdup(DEFAULT_CHAN_LOG_COPYDIR) : 0);
def->chan_log_program = (DEFAULT_CHAN_LOG_PROGRAM
? x_strdup(DEFAULT_CHAN_LOG_PROGRAM) : 0);
def->other_log_enabled = DEFAULT_OTHER_LOG_ENABLED;
def->other_log_always = DEFAULT_OTHER_LOG_ALWAYS;
def->other_log_maxsize = DEFAULT_OTHER_LOG_MAXSIZE;
def->other_log_recall = DEFAULT_OTHER_LOG_RECALL;
def->other_log_timestamp = DEFAULT_OTHER_LOG_TIMESTAMP;
def->other_log_relativetime = DEFAULT_OTHER_LOG_RELATIVETIME;
def->other_log_copydir = (DEFAULT_OTHER_LOG_COPYDIR
? x_strdup(DEFAULT_OTHER_LOG_COPYDIR) : 0);
def->other_log_program = (DEFAULT_OTHER_LOG_PROGRAM
? x_strdup(DEFAULT_OTHER_LOG_PROGRAM) : 0);
def->log_timeoffset = DEFAULT_LOG_TIMEOFFSET;
def->log_events = DEFAULT_LOG_EVENTS;
def->dcc_proxy_incoming = DEFAULT_DCC_PROXY_INCOMING;
def->dcc_proxy_outgoing = DEFAULT_DCC_PROXY_OUTGOING;
def->dcc_proxy_ports = 0;
def->dcc_proxy_ports_sz = 0;
def->dcc_proxy_timeout = DEFAULT_DCC_PROXY_TIMEOUT;
def->dcc_proxy_sendreject = DEFAULT_DCC_PROXY_SENDREJECT;
def->dcc_send_fast = DEFAULT_DCC_SEND_FAST;
def->dcc_capture_directory = (DEFAULT_DCC_CAPTURE_DIRECTORY
? x_strdup(DEFAULT_DCC_CAPTURE_DIRECTORY) : 0);
def->dcc_capture_always = DEFAULT_DCC_CAPTURE_ALWAYS;
def->dcc_capture_withnick = DEFAULT_DCC_CAPTURE_WITHNICK;
def->dcc_capture_maxsize = DEFAULT_DCC_CAPTURE_MAXSIZE;
def->dcc_tunnel_incoming = (DEFAULT_DCC_TUNNEL_INCOMING
? x_strdup(DEFAULT_DCC_TUNNEL_INCOMING) : 0);
def->dcc_tunnel_outgoing = (DEFAULT_DCC_TUNNEL_OUTGOING
? x_strdup(DEFAULT_DCC_TUNNEL_OUTGOING) : 0);
def->switch_user = (DEFAULT_SWITCH_USER ? x_strdup(DEFAULT_SWITCH_USER) : 0);
def->motd_logo = DEFAULT_MOTD_LOGO;
def->motd_file = (DEFAULT_MOTD_FILE ? x_strdup(DEFAULT_MOTD_FILE) : 0);
def->motd_stats = DEFAULT_MOTD_STATS;
def->allow_persist = DEFAULT_ALLOW_PERSIST;
def->allow_jump = DEFAULT_ALLOW_JUMP;
def->allow_jump_new = DEFAULT_ALLOW_JUMP_NEW;
def->allow_host = DEFAULT_ALLOW_HOST;
def->allow_die = DEFAULT_ALLOW_DIE;
def->allow_users = DEFAULT_ALLOW_USERS;
def->allow_kill = DEFAULT_ALLOW_KILL;
while (valid) {
char buff[512], *buf;
if (!fgets(buff, 512, fd))
break;
line++;
buf = buff;
while ((buf < (buff + 512)) && strlen(buf)) {
char *key;
/* Skip whitespace, and ignore lines that are comments */
buf += strspn(buf, WS);
if (*buf == '#')
break;
/* Find the end of the key, and if there isn't one, exit */
key = buf;
buf += strcspn(buf, WS);
if (!strlen(key))
break;
/* If there isn't a newline, then we could reach the end of the
buffer - so be a bit careful and ensure we don't skip past it */
if (*buf) {
*(buf++) = 0;
/* Close brace is only allowed when class is defined
(we check here, because its a special case and has no value) */
if (!strcmp(key, "}") && !class) {
error("Close brace without open at line %ld of %s", line, filename);
valid = 0;
break;
}
buf += strspn(buf, WS);
}
/* If we reached the end of the buffer, or a comment, that means
this key has no value. Unless the key is '}' then thats bad */
if ((!*buf || (*buf == '#')) && strcmp(key, "}")) {
error("Missing value for key '%s' at line %ld of %s",
key, line, filename);
valid = 0;
break;
}
/* Handle the keys */
if (!class && !strcasecmp(key, "listen_port")) {
/* listen_port 57000
listen_port "dircproxy" # From /etc/services
( cannot go in a connection {} ) */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
/* Make sure the silly programmer supplied the pointer! */
if (listen_port) {
free(*listen_port);
*listen_port = str;
}
} else if (!class && !strcasecmp(key, "pid_file")) {
/* pid_file none
pid_file "" # same as none
pid_file "/file"
pid_file "~/file"
( cannot go in a connection {} ) */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
/* Make sure the silly programmer supplied the pointer! */
if (pid_file) {
free(*pid_file);
*pid_file = str;
}
} else if (!class && !strcasecmp(key, "client_timeout")) {
/* client_timeout 60 */
_cfg_read_numeric(&buf, &globals->client_timeout);
} else if (!class && !strcasecmp(key, "connect_timeout")) {
/* connect_timeout 60 */
_cfg_read_numeric(&buf, &globals->connect_timeout);
} else if (!class && !strcasecmp(key, "dns_timeout")) {
/* dns_timeout 60 */
_cfg_read_numeric(&buf, &globals->dns_timeout);
} else if (!strcasecmp(key, "server_port")) {
/* server_port 6667
server_port "irc" # From /etc/services */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
free((class ? class : def)->server_port);
(class ? class : def)->server_port = str;
} else if (!strcasecmp(key, "server_retry")) {
/* server_retry 15 */
_cfg_read_numeric(&buf, &(class ? class : def)->server_retry);
} else if (!strcasecmp(key, "server_maxattempts")) {
/* server_maxattempts 0 */
_cfg_read_numeric(&buf, &(class ? class : def)->server_maxattempts);
} else if (!strcasecmp(key, "server_maxinitattempts")) {
/* server_maxinitattempts 5 */
_cfg_read_numeric(&buf, &(class ? class : def)->server_maxinitattempts);
} else if (!strcasecmp(key, "server_keepalive")) {
/* server_keepalive yes
server_keepalive no */
_cfg_read_bool(&buf, &(class ? class : def)->server_keepalive);
} else if (!strcasecmp(key, "server_pingtimeout")) {
/* server_pingtimeout 600 */
_cfg_read_numeric(&buf, &(class ? class : def)->server_pingtimeout);
} else if (!strcasecmp(key, "server_throttle")) {
/* server_throttle 0
server_throttle 512
server_throttle 1024:10 */
long *pair;
_cfg_read_pair(&buf, &pair);
free((class ? class : def)->server_throttle);
(class ? class : def)->server_throttle = pair;
} else if (!strcasecmp(key, "server_autoconnect")) {
/* server_autoconnect yes
server_autoconnect no */
_cfg_read_bool(&buf, &(class ? class : def)->server_autoconnect);
} else if (!strcasecmp(key, "channel_rejoin")) {
/* channel_rejoin 5 */
_cfg_read_numeric(&buf, &(class ? class : def)->channel_rejoin);
} else if (!strcasecmp(key, "channel_leave_on_detach")) {
/* channel_leave_on_detach yes
channel_leave_on_detach no */
_cfg_read_bool(&buf, &(class ? class : def)->channel_leave_on_detach);
} else if (!strcasecmp(key, "channel_rejoin_on_attach")) {
/* channel_rejoin_on_attach yes
channel_rejoin_on_attach no */
_cfg_read_bool(&buf, &(class ? class : def)->channel_rejoin_on_attach);
} else if (!strcasecmp(key, "idle_maxtime")) {
/* idle_maxtime 120 */
_cfg_read_numeric(&buf, &(class ? class : def)->idle_maxtime);
} else if (!strcasecmp(key, "disconnect_existing_user")) {
/* disconnect_existing_user yes
disconnect_existing_user no */
_cfg_read_bool(&buf, &(class ? class : def)->disconnect_existing);
} else if (!strcasecmp(key, "disconnect_on_detach")) {
/* disconnect_on_detach yes
disconnect_on_detach no */
_cfg_read_bool(&buf, &(class ? class : def)->disconnect_on_detach);
} else if (!strcasecmp(key, "initial_modes")) {
/* initial_modes "ow"
initial_modes "" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
while ((*str == '+') || (*str == '-')) {
char *tmp;
tmp = str;
str = x_strdup(tmp + 1);
free(tmp);
}
if (!strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->initial_modes);
(class ? class : def)->initial_modes = str;
} else if (!strcasecmp(key, "drop_modes")) {
/* drop_modes "ow"
drop_modes "" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
while ((*str == '+') || (*str == '-')) {
char *tmp;
tmp = str;
str = x_strdup(tmp + 1);
free(tmp);
}
if (!strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->drop_modes);
(class ? class : def)->drop_modes = str;
} else if (!strcasecmp(key, "refuse_modes")) {
/* refuse_modes "r"
refuse_modes "" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
while ((*str == '+') || (*str == '-')) {
char *tmp;
tmp = str;
str = x_strdup(tmp + 1);
free(tmp);
}
if (!strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->refuse_modes);
(class ? class : def)->refuse_modes = str;
} else if (!strcasecmp(key, "local_address")) {
/* local_address none
local_address "" # same as none
local_address "i.am.a.virtual.host.com" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->local_address);
(class ? class : def)->local_address = str;
} else if (!strcasecmp(key, "away_message")) {
/* away_message none
away_message "" # same as none
away_message "Not available, messages are logged" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->away_message);
(class ? class : def)->away_message = str;
} else if (!strcasecmp(key, "quit_message")) {
/* quit_message none
quit_message "" # same as none
quit_message "Gotta restart this thing" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->quit_message);
(class ? class : def)->quit_message = str;
} else if (!strcasecmp(key, "attach_message")) {
/* attach_message none
attach_message "" # same as none
attach_message "I'm back!"
attach_message "/me returns" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->attach_message);
(class ? class : def)->attach_message = str;
} else if (!strcasecmp(key, "detach_message")) {
/* detach_message none
detach_message "" # same as none
detach_message "I'm gone!"
detach_message "/me vanishes" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->detach_message);
(class ? class : def)->detach_message = str;
} else if (!strcasecmp(key, "detach_nickname")) {
/* detach_nickname none
detach_nickname "" # same as none
detach_nickname "FooAWAY"
detach_nickname "*AWAY" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->detach_nickname);
(class ? class : def)->detach_nickname = str;
} else if (!strcasecmp(key, "nick_keep")) {
/* nick_keep yes
nick_keep no */
_cfg_read_bool(&buf, &(class ? class : def)->nick_keep);
} else if (!strcasecmp(key, "ctcp_replies")) {
/* ctcp_replies yes
ctcp_replies no */
_cfg_read_bool(&buf, &(class ? class : def)->ctcp_replies);
} else if (!strcasecmp(key, "chan_log_enabled")) {
/* chan_log_enabled yes
chan_log_disabled no */
_cfg_read_bool(&buf, &(class ? class : def)->chan_log_enabled);
} else if (!strcasecmp(key, "chan_log_always")) {
/* chan_log_always yes
chan_log_always no */
_cfg_read_bool(&buf, &(class ? class : def)->chan_log_always);
} else if (!strcasecmp(key, "chan_log_maxsize")) {
/* chan_log_maxsize 128
chan_log_maxsize 0 */
_cfg_read_numeric(&buf, &(class ? class : def)->chan_log_maxsize);
} else if (!strcasecmp(key, "chan_log_recall")) {
/* chan_log_recall 128
chan_log_recall 0
chan_log_recall -1 */
_cfg_read_numeric(&buf, &(class ? class : def)->chan_log_recall);
} else if (!strcasecmp(key, "chan_log_timestamp")) {
/* chan_log_timestamp yes
chan_log_timestamp no */
_cfg_read_bool(&buf, &(class ? class : def)->chan_log_timestamp);
} else if (!strcasecmp(key, "chan_log_relativetime")) {
/* chan_log_relativetime yes
chan_log_relativetime no */
_cfg_read_bool(&buf, &(class ? class : def)->chan_log_relativetime);
} else if (!strcasecmp(key, "chan_log_copydir")) {
/* chan_log_copydir none
chan_log_copydir "" # same as none
chan_log_copydir "/log"
chan_log_copydir "~/logs" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
free((class ? class : def)->chan_log_copydir);
(class ? class : def)->chan_log_copydir = str;
} else if (!strcasecmp(key, "chan_log_program")) {
/* chan_log_program none
chan_log_program "" # same as none
chan_log_program "/logprog"
chan_log_program "~/logprog" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
free((class ? class : def)->chan_log_program);
(class ? class : def)->chan_log_program = str;
} else if (!strcasecmp(key, "other_log_enabled")) {
/* other_log_enabled yes
other_log_disabled no */
_cfg_read_bool(&buf, &(class ? class : def)->other_log_enabled);
} else if (!strcasecmp(key, "other_log_always")) {
/* other_log_always yes
other_log_always no */
_cfg_read_bool(&buf, &(class ? class : def)->other_log_always);
} else if (!strcasecmp(key, "other_log_maxsize")) {
/* other_log_maxsize 128
other_log_maxsize 0 */
_cfg_read_numeric(&buf, &(class ? class : def)->other_log_maxsize);
} else if (!strcasecmp(key, "other_log_recall")) {
/* other_log_recall 128
other_log_recall 0
other_log_recall -1 */
_cfg_read_numeric(&buf, &(class ? class : def)->other_log_recall);
} else if (!strcasecmp(key, "other_log_timestamp")) {
/* other_log_timestamp yes
other_log_timestamp no */
_cfg_read_bool(&buf, &(class ? class : def)->other_log_timestamp);
} else if (!strcasecmp(key, "other_log_relativetime")) {
/* other_log_relativetime yes
other_log_relativetime no */
_cfg_read_bool(&buf, &(class ? class : def)->other_log_relativetime);
} else if (!strcasecmp(key, "other_log_copydir")) {
/* other_log_copydir none
other_log_copydir "" # same as none
other_log_copydir "/log"
other_log_copydir "~/logs" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
free((class ? class : def)->other_log_copydir);
(class ? class : def)->other_log_copydir = str;
} else if (!strcasecmp(key, "other_log_program")) {
/* other_log_program none
other_log_program "" # same as none
other_log_program "/logprog"
other_log_program "~/logprog" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
free((class ? class : def)->other_log_program);
(class ? class : def)->other_log_program = str;
} else if (!strcasecmp(key, "log_timeoffset")) {
/* log_timeoffset 0
log_timeoffset -60
log_timeoffset +60 */
_cfg_read_numeric(&buf, &(class ? class : def)->log_timeoffset);
} else if (!strcasecmp(key, "log_events")) {
/* log_events none
log_events all
log_events none,+text
log_events all,-quit */
char *str, *orig;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
orig = str;
while (str && strlen(str)) {
char *ptr;
ptr = strchr(str, ',');
if (ptr)
*(ptr++) = 0;
str += strspn(str, WS);
if (strlen(str) && !strcasecmp(str, "all")) {
(class ? class : def)->log_events = 0xffff;
} else if (strlen(str) && !strcasecmp(str, "none")) {
(class ? class : def)->log_events = 0x0000;
} else if (strlen(str)) {
int add = 1;
if (*str == '-') {
add = 0;
str++;
} else if (*str == '+') {
add = 1;
str++;
}
if (strlen(str)) {
int flag = 0;
if (!strcasecmp(str, "text")) {
flag = IRC_LOG_TEXT;
} else if (!strcasecmp(str, "action")) {
flag = IRC_LOG_ACTION;
} else if (!strcasecmp(str, "ctcp")) {
flag = IRC_LOG_CTCP;
} else if (!strcasecmp(str, "join")) {
flag = IRC_LOG_JOIN;
} else if (!strcasecmp(str, "part")) {
flag = IRC_LOG_PART;
} else if (!strcasecmp(str, "kick")) {
flag = IRC_LOG_KICK;
} else if (!strcasecmp(str, "quit")) {
flag = IRC_LOG_QUIT;
} else if (!strcasecmp(str, "nick")) {
flag = IRC_LOG_NICK;
} else if (!strcasecmp(str, "mode")) {
flag = IRC_LOG_MODE;
} else if (!strcasecmp(str, "topic")) {
flag = IRC_LOG_TOPIC;
} else if (!strcasecmp(str, "client")) {
flag = IRC_LOG_CLIENT;
} else if (!strcasecmp(str, "server")) {
flag = IRC_LOG_SERVER;
} else if (!strcasecmp(str, "error")) {
flag = IRC_LOG_ERROR;
} else {
error("Unknown event name '%s' in 'log_events' "
"at line %ld of %s", str, line, filename);
valid = 0;
break;
}
if (add) {
(class ? class : def)->log_events |= flag;
} else {
(class ? class : def)->log_events &= ~flag;
}
} else {
error("Missing event name in 'log_events' at line %ld of %s",
line, filename);
valid = 0;
break;
}
} else {
error("Missing event name in 'log_events' at line %ld of %s",
line, filename);
valid = 0;
break;
}
str = ptr;
}
free(orig);
if (!valid)
break;
} else if (!strcasecmp(key, "dcc_proxy_incoming")) {
/* dcc_proxy_incoming yes
dcc_proxy_incoming no */
_cfg_read_bool(&buf, &(class ? class : def)->dcc_proxy_incoming);
} else if (!strcasecmp(key, "dcc_proxy_outgoing")) {
/* dcc_proxy_outgoing yes
dcc_proxy_outgoing no */
_cfg_read_bool(&buf, &(class ? class : def)->dcc_proxy_outgoing);
} else if (!strcasecmp(key, "dcc_proxy_ports")) {
/* dcc_proxy_ports any
dcc_proxy_ports 6667,1042-2048 */
char *str, *orig;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "any") || !strlen(str)) {
free(str);
str = 0;
free((class ? class : def)->dcc_proxy_ports);
(class ? class : def)->dcc_proxy_ports = 0;
(class ? class : def)->dcc_proxy_ports_sz = 0;
}
orig = str;
while (str && strlen(str)) {
char *ptr;
ptr = strchr(str, ',');
if (ptr)
*(ptr++) = 0;
str += strspn(str, WS);
if (strlen(str)) {
int lwr, upr;
char *dash;
dash = strchr(str, '-');
if (dash)
*(dash++) = 0;
lwr = atoi(str);
upr = (dash ? atoi(dash) : lwr);
if (lwr && upr) {
int *newlist;
size_t newsz;
newsz = (class ? class : def)->dcc_proxy_ports_sz + 2;
newlist = (int *)malloc(sizeof(int) * newsz);
memcpy(newlist, (class ? class : def)->dcc_proxy_ports,
sizeof(int) * (class ? class : def)->dcc_proxy_ports_sz);
newlist[(class ? class : def)->dcc_proxy_ports_sz + 0] = lwr;
newlist[(class ? class : def)->dcc_proxy_ports_sz + 1] = upr;
free((class ? class : def)->dcc_proxy_ports);
(class ? class : def)->dcc_proxy_ports = newlist;
(class ? class : def)->dcc_proxy_ports_sz = newsz;
} else {
error("Bad port in 'dcc_proxy_ports' at line %ld of %s",
line, filename);
valid = 0;
free(orig);
break;
}
} else {
error("Missing port range in 'dcc_proxy_ports' at line %ld of %s",
line, filename);
valid = 0;
free(orig);
break;
}
str = ptr;
}
free(orig);
} else if (!strcasecmp(key, "dcc_proxy_timeout")) {
/* dcc_proxy_timeout 60 */
_cfg_read_numeric(&buf, &(class ? class : def)->dcc_proxy_timeout);
} else if (!strcasecmp(key, "dcc_proxy_sendreject")) {
/* dcc_proxy_sendreject yes
dcc_proxy_sendreject no */
_cfg_read_bool(&buf, &(class ? class : def)->dcc_proxy_sendreject);
} else if (!strcasecmp(key, "dcc_send_fast")) {
/* dcc_send_fast yes
dcc_send_fast no */
_cfg_read_bool(&buf, &(class ? class : def)->dcc_send_fast);
} else if (!strcasecmp(key, "dcc_capture_directory")) {
/* dcc_capture_directory none
dcc_capture_directory "" # same as none
dcc_capture_directory "/tmp"
dcc_capture_directory "~/caught" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
free((class ? class : def)->dcc_capture_directory);
(class ? class : def)->dcc_capture_directory = str;
} else if (!strcasecmp(key, "dcc_capture_always")) {
/* dcc_capture_always yes
dcc_capture_always no */
_cfg_read_bool(&buf, &(class ? class : def)->dcc_capture_always);
} else if (!strcasecmp(key, "dcc_capture_withnick")) {
/* dcc_capture_withnick yes
dcc_capture_withnick no */
_cfg_read_bool(&buf, &(class ? class : def)->dcc_capture_withnick);
} else if (!class && !strcasecmp(key, "dcc_capture_maxsize")) {
/* dcc_capture_maxsize 0
dcc_capture_maxsize 1024 */
_cfg_read_numeric(&buf, &(class ? class : def)->dcc_capture_maxsize);
} else if (!strcasecmp(key, "dcc_tunnel_incoming")) {
/* dcc_tunnel_incoming none
dcc_tunnel_incoming "" # same as none
dcc_tunnel_incoming "6667"
dcc_tunnel_incoming "irctunnel" # from /etc/services */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->dcc_tunnel_incoming);
(class ? class : def)->dcc_tunnel_incoming = str;
} else if (!strcasecmp(key, "dcc_tunnel_outgoing")) {
/* dcc_tunnel_outgoing none
dcc_tunnel_outgoing "" # same as none
dcc_tunnel_outgoing "6667"
dcc_tunnel_outgoing "irctunnel" # from /etc/services */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->dcc_tunnel_outgoing);
(class ? class : def)->dcc_tunnel_outgoing = str;
} else if (!strcasecmp(key, "switch_user")) {
/* switch_user none
switch_user "" # same as none
switch_user 1001
switch_user "bob" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
#ifdef HAVE_SETEUID
if (getuid() == 0) {
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
}
free((class ? class : def)->switch_user);
(class ? class : def)->switch_user = str;
} else {
error("(warning) must be run as root to use 'switch_user' "
"at line %ld of %s", line, filename);
free(str);
}
#else /* HAVE_SETEUID */
error("(warning) Your system does not support 'switch_user' "
"at line %ld of %s", line, filename);
free(str);
#endif /* HAVE_SETEUID */
} else if (!strcasecmp(key, "motd_logo")) {
/* motd_logo yes
motd_logo no */
_cfg_read_bool(&buf, &(class ? class : def)->motd_logo);
} else if (!strcasecmp(key, "motd_file")) {
/* motd_file none
motd_file "" # same as none
motd_file "/file"
motd_file "~/file" */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
if (!strcasecmp(str, "none") || !strlen(str)) {
free(str);
str = 0;
} else if (!strncmp(str, "~/", 2)) {
char *home;
home = getenv("HOME");
if (home) {
char *tmp;
tmp = x_sprintf("%s%s", home, str + 1);
free(str);
str = tmp;
} else {
/* Best we can do */
*str = '.';
}
}
free((class ? class : def)->motd_file);
(class ? class : def)->motd_file = str;
} else if (!strcasecmp(key, "motd_stats")) {
/* motd_stats yes
motd_stats no */
_cfg_read_bool(&buf, &(class ? class : def)->motd_stats);
} else if (!strcasecmp(key, "allow_persist")) {
/* allow_persist yes
allow_persist no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_persist);
} else if (!strcasecmp(key, "allow_jump")) {
/* allow_jump yes
allow_jump no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_jump);
} else if (!strcasecmp(key, "allow_jump_new")) {
/* allow_jump_new yes
allow_jump_new no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_jump_new);
} else if (!strcasecmp(key, "allow_host")) {
/* allow_host yes
allow_host no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_host);
} else if (!strcasecmp(key, "allow_die")) {
/* allow_die yes
allow_die no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_die);
} else if (!strcasecmp(key, "allow_users")) {
/* allow_users yes
allow_users no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_users);
} else if (!strcasecmp(key, "allow_kill")) {
/* allow_kill yes
allow_kill no */
_cfg_read_bool(&buf, &(class ? class : def)->allow_kill);
} else if (!class && !strcasecmp(key, "connection")) {
/* connection {
:
:
} */
if (*buf != '{') {
/* Connection is a bit special, because the only valid value is '{'
the internals are handled elsewhere */
error("Expected open brace for key '%s' at line %ld of %s",
key, line, filename);
valid = 0;
break;
}
buf++;
/* Allocate memory, it'll be filled later */
class = (struct ircconnclass *)malloc(sizeof(struct ircconnclass));
memcpy(class, def, sizeof(struct ircconnclass));
class->server_port = (def->server_port
? x_strdup(def->server_port) : 0);
if (def->server_throttle) {
class->server_throttle = (long *)malloc(sizeof(long) * 2);
memcpy(class->server_throttle, def->server_throttle,
sizeof(long) * 2);
}
class->initial_modes = (def->initial_modes
? x_strdup(def->initial_modes) : 0);
class->drop_modes = (def->drop_modes
? x_strdup(def->drop_modes) : 0);
class->refuse_modes = (def->refuse_modes
? x_strdup(def->refuse_modes) : 0);
class->local_address = (def->local_address
? x_strdup(def->local_address) : 0);
class->away_message = (def->away_message
? x_strdup(def->away_message) : 0);
class->quit_message = (def->quit_message
? x_strdup(def->quit_message) : 0);
class->attach_message = (def->attach_message
? x_strdup(def->attach_message) : 0);
class->detach_message = (def->detach_message
? x_strdup(def->detach_message) : 0);
class->detach_nickname = (def->detach_nickname
? x_strdup(def->detach_nickname) : 0);
class->chan_log_copydir = (def->chan_log_copydir
? x_strdup(def->chan_log_copydir) : 0);
class->chan_log_program = (def->chan_log_program
? x_strdup(def->chan_log_program) : 0);
class->other_log_copydir = (def->other_log_copydir
? x_strdup(def->other_log_copydir) : 0);
class->other_log_program = (def->other_log_program
? x_strdup(def->other_log_program) : 0);
if (def->dcc_proxy_ports) {
class->dcc_proxy_ports = (int *)malloc(sizeof(int)
* def->dcc_proxy_ports_sz);
memcpy(class->dcc_proxy_ports, def->dcc_proxy_ports,
sizeof(int) * def->dcc_proxy_ports_sz);
}
class->dcc_capture_directory = (def->dcc_capture_directory
? x_strdup(def->dcc_capture_directory)
: 0);
class->dcc_tunnel_incoming = (def->dcc_tunnel_incoming
? x_strdup(def->dcc_tunnel_incoming) : 0);
class->dcc_tunnel_outgoing = (def->dcc_tunnel_outgoing
? x_strdup(def->dcc_tunnel_outgoing) : 0);
class->switch_user = (def->switch_user
? x_strdup(def->switch_user) : 0);
class->motd_file = (def->motd_file ? x_strdup(def->motd_file) : 0);
} else if (class && !strcasecmp(key, "password")) {
/* connection {
:
password "foo"
:
} */
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
free(class->password);
class->password = str;
} else if (class && !strcasecmp(key, "server")) {
/* connection {
:
server "irc.linux.com"
server "irc.linux.com:6670" # Port other than default
server "irc.linux.com:6670:foo" # Port and password
server "irc.linux.com::foo" # Password and default port
:
} */
struct strlist *s;
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
s = (struct strlist *)malloc(sizeof(struct strlist));
s->str = str;
s->next = 0;
if (class->servers) {
struct strlist *ss;
ss = class->servers;
while (ss->next)
ss = ss->next;
ss->next = s;
} else {
class->servers = s;
}
} else if (class && !strcasecmp(key, "from")) {
/* connection {
:
from "static-132.myisp.com" # Static hostname
from "*.myisp.com" # Masked hostname
from "192.168.1.1" # Specific IP
from "192.168.*" # IP range
:
} */
struct strlist *s;
char *str;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
s = (struct strlist *)malloc(sizeof(struct strlist));
s->str = str;
s->next = class->masklist;
class->masklist = s;
} else if (class && !strcasecmp(key, "join")) {
/* connection {
:
join "#foo"
join "#foo key,#bar"
:
} */
struct strlist *s;
char *str, *orig;
if (_cfg_read_string(&buf, &str))
UNMATCHED_QUOTE;
orig = str;
while (str && strlen(str)) {
char *ptr;
ptr = strchr(str, ',');
if (ptr)
*(ptr++) = 0;
str += strspn(str, WS);
if (strlen(str)) {
s = (struct strlist *)malloc(sizeof(struct strlist));
s->str = x_strdup(str);
s->next = 0;
if (class->channels) {
struct strlist *ss;
ss = class->channels;
while (ss->next)
ss = ss->next;
ss->next = s;
} else {
class->channels = s;
}
} else {
error("Missing channel name in 'join' at line %ld of %s",
line, filename);
valid = 0;
free(orig);
break;
}
str = ptr;
}
free(orig);
} else if (class && !strcmp(key, "}")) {
/* No auto-connect? Then we *need* jump */
if (!class->server_autoconnect)
class->allow_jump = 1;
/* Check that a password and at least one server were defined */
if (!class->password) {
error("Connection class defined without password "
"before line %ld of %s", line, filename);
valid = 0;
} else if (!class->servers
&& (class->server_autoconnect || !class->allow_jump_new)) {
error("Connection class defined without a server "
"before line %ld of %s", line, filename);
valid = 0;
}
/* Add to the list of servers if valid, otherwise free it */
if (valid) {
class->orig_local_address = (class->local_address
? x_strdup(class->local_address) : 0);
class->next_server = class->servers;
class->next = connclasses;
connclasses = class;
class = 0;
} else {
ircnet_freeconnclass(class);
class = 0;
break;
}
} else {
/* Bad key! */
error("Unknown config file key '%s' at line %ld of %s",
key, line, filename);
valid = 0;
break;
}
/* Skip whitespace. The only things that can trail are comments
and close braces, and we re-pass to do those */
buf += strspn(buf, WS);
if (*buf && (*buf != '#') && (*buf != '}')) {
error("Unexpected data at end of line %ld of %s", line, filename);
valid = 0;
break;
}
}
}
/* Argh, untidy stuff left around */
if (class) {
ircnet_freeconnclass(class);
class = 0;
if (valid) {
error("Unmatched open brace in %s", filename);
valid = 0;
}
}
fclose(fd);
free(def->server_port);
free(def->server_throttle);
free(def->initial_modes);
free(def->drop_modes);
free(def->refuse_modes);
free(def->local_address);
free(def->away_message);
free(def->quit_message);
free(def->attach_message);
free(def->detach_message);
free(def->detach_nickname);
free(def->chan_log_copydir);
free(def->chan_log_program);
free(def->other_log_copydir);
free(def->other_log_program);
free(def->dcc_proxy_ports);
free(def->dcc_capture_directory);
free(def->dcc_tunnel_incoming);
free(def->dcc_tunnel_outgoing);
free(def->switch_user);
free(def->motd_file);
return (valid ? 0 : -1);
}
/* Read a boolean value from config file */
static int _cfg_read_bool(char **buf, int *val) {
char *ptr;
ptr = *buf;
*buf += strcspn(*buf, WS);
*((*buf)++) = 0;
if (!strcasecmp(ptr, "yes")) {
*val = 1;
} else if (!strcasecmp(ptr, "true")) {
*val = 1;
} else if (!strcasecmp(ptr, "y")) {
*val = 1;
} else if (!strcasecmp(ptr, "t")) {
*val = 1;
} else if (!strcasecmp(ptr, "no")) {
*val = 0;
} else if (!strcasecmp(ptr, "false")) {
*val = 0;
} else if (!strcasecmp(ptr, "n")) {
*val = 0;
} else if (!strcasecmp(ptr, "f")) {
*val = 0;
} else {
*val = (atoi(ptr) ? 1 : 0);
}
return 0;
}
/* Read a numeric value from config file */
static int _cfg_read_numeric(char **buf, long *val) {
char *ptr;
ptr = *buf;
*buf += strcspn(*buf, WS);
*((*buf)++) = 0;
*val = atol(ptr);
return 0;
}
/* Read a string value from config file */
static int _cfg_read_string(char **buf, char **val) {
char *ptr;
if (**buf == '"') {
ptr = ++(*buf);
while (1) {
*buf += strcspn(*buf, "\"");
if (**buf != '"') {
return -1;
} else if (*(*buf - 1) == '\\') {
(*buf)++;
continue;
} else {
break;
}
}
} else {
ptr = *buf;
*buf += strcspn(*buf, WS);
}
*((*buf)++) = 0;
*val = x_strdup(ptr);
return 0;
}
/* Read a numeric pair from config file */
static int _cfg_read_pair(char **buf, long **val) {
char *ptr, *col;
long ret[2];
ptr = *buf;
*buf += strcspn(*buf, WS);
*((*buf)++) = 0;
col = strchr(ptr, ':');
if (col) {
*(col++) = 0;
ret[1] = atol(col);
} else {
ret[1] = 1;
}
ret[0] = atol(ptr);
if (ret[0] || ret[1]) {
*val = (long *)malloc(sizeof(long) * 2);
memcpy(*val, ret, sizeof(long) * 2);
} else {
*val = 0;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1