/* $Id: read_config.c,v 1.197 2011/06/28 00:13:48 sbajic Exp $ */
/*
DSPAM
COPYRIGHT (C) 2002-2012 DSPAM PROJECT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#ifdef SPLIT_CONFIG
#include
#endif
#ifdef HAVE_STRINGS_H
#include
#endif
#include
#include "config_shared.h"
#include "read_config.h"
#include "config.h"
#include "error.h"
#include "language.h"
#include "libdspam.h"
#include "pref.h"
#include "util.h"
#ifdef SPLIT_CONFIG
long dirread(const char *path, config_t *attrib, long num_root);
long fileread(const char *path, config_t *attrib, long num_root);
#endif
static char *next_normal_token(char **p)
{
char *start = *p;
while (**p) {
if (**p == ' ' || **p == '\t') {
**p = 0;
*p += 1;
break;
}
*p += 1;
}
return start;
}
static char *next_quoted_token(char **p)
{
char *start = *p;
while (**p) {
if (**p == '\"') {
**p = 0;
*p += 1;
break;
}
*p += 1;
}
return start;
}
static char *tokenize(char *text, char **next)
{
/* Initialize */
if (text)
*next = text;
while (**next) {
/* Skip leading whitespace */
if (**next == ' ' || **next == '\t') {
*next += 1;
continue;
}
/* Strip off one token */
if (**next == '\"') {
*next += 1;
return next_quoted_token(next);
} else
return next_normal_token(next);
}
return NULL;
}
#ifdef SPLIT_CONFIG
// Read the files in the directory and pass it to fileread
// or if it is a file, pass it to fileread.
long dirread(const char *path, config_t *attrib, long num_root) {
DIR *dir_p;
char *fulldir = NULL;
struct dirent *dir_entry_p;
if (path == NULL) return 0;
// Strip "\n"
char *ptr = strrchr(path, '\n');
if (ptr)
*ptr = '\0';
if ((dir_p = opendir(path)) == NULL) {
LOG(LOG_ERR, ERR_IO_FILE_OPEN, path, strerror(errno));
return 0;
}
if ((dir_p = opendir(path))) {
while((dir_entry_p = readdir(dir_p)))
{
// We don't need the . and ..
if (strcmp(dir_entry_p->d_name, ".") == 0 ||
strcmp(dir_entry_p->d_name, "..") == 0)
continue;
int n = strlen(dir_entry_p->d_name);
// only use files which end in .conf:
if (n < 5) continue;
if (strncmp(dir_entry_p->d_name + n - 5, ".conf", 5) != 0) continue;
int m = strlen(path);
fulldir = (char *)malloc(n+m+2);
if (fulldir == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return 0;
}
strcpy(fulldir, (char *)path);
strcat(fulldir, "/");
strcat(fulldir, dir_entry_p->d_name);
num_root = fileread((const char *)fulldir, attrib, num_root);
free(fulldir);
}
closedir(dir_p);
} else {
// Could be a file.
return fileread((const char *)path, attrib, num_root);
}
return num_root;
}
// Read the file and check if there is an Include directive, if so then pass
// it to dirread.
long fileread(const char *path, config_t *attrib, long num_root) {
config_t ptr;
#else
config_t read_config(const char *path) {
config_t attrib, ptr;
#endif
FILE *file;
#ifdef SPLIT_CONFIG
long attrib_size = 128;
#else
long attrib_size = 128, num_root = 0;
#endif
char buffer[1024];
char *a, *c, *v, *bufptr = buffer;
#ifndef SPLIT_CONFIG
attrib = calloc(1, attrib_size*sizeof(attribute_t));
if (attrib == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return NULL;
}
#endif
if (path == NULL)
file = fopen(CONFIG_DEFAULT, "r");
else
file = fopen(path, "r");
if (file == NULL) {
#ifdef SPLIT_CONFIG
if (path == NULL) {
LOG(LOG_ERR, ERR_IO_FILE_OPEN, CONFIG_DEFAULT, strerror(errno));
} else {
LOG(LOG_ERR, ERR_IO_FILE_OPEN, path, strerror(errno));
}
return 0;
#else
LOG(LOG_ERR, ERR_IO_FILE_OPEN, CONFIG_DEFAULT, strerror(errno));
free(attrib);
return NULL;
#endif
}
while(fgets(buffer, sizeof(buffer), file)!=NULL) {
chomp(buffer);
/* Remove comments */
if ((c = strchr(buffer, '#')) || (c = strchr(buffer, ';')))
*c = 0;
/* Parse attribute name */
if (!(a = tokenize(buffer, &bufptr)))
continue; /* Ignore whitespace-only lines */
while ((v = tokenize(NULL, &bufptr)) != NULL) {
#ifdef SPLIT_CONFIG
// Check for include directive
if (strcmp(a, "Include") == 0) {
// Give v (value) to dirraed
num_root = dirread(v, attrib, num_root);
} else {
if (_ds_find_attribute((*attrib), a)!=NULL) {
_ds_add_attribute((*attrib), a, v);
}
else {
num_root++;
if (num_root >= attrib_size) {
attrib_size *=2;
ptr = realloc((*attrib), attrib_size*sizeof(attribute_t));
if (ptr)
*attrib = ptr;
else {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
fclose(file);
return 0;
}
}
_ds_add_attribute((*attrib), a, v);
}
#else
if (_ds_find_attribute(attrib, a)!=NULL) {
_ds_add_attribute(attrib, a, v);
}
else {
num_root++;
if (num_root >= attrib_size) {
attrib_size *=2;
ptr = realloc(attrib, attrib_size*sizeof(attribute_t));
if (ptr)
attrib = ptr;
else {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
fclose(file);
return NULL;
}
}
_ds_add_attribute(attrib, a, v);
#endif
}
}
}
fclose(file);
#ifdef SPLIT_CONFIG
return num_root;
}
config_t read_config(const char *path) {
config_t attrib;
long attrib_size = 128, num_root = 0;
attrib = calloc(1, attrib_size*sizeof(attribute_t));
if (attrib == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return NULL;
}
if (fileread(path, &attrib, num_root) == 0)
return NULL;
#else
ptr = realloc(attrib, ((num_root+1)*sizeof(attribute_t))+1);
if (ptr)
return ptr;
LOG(LOG_CRIT, ERR_MEM_ALLOC);
#endif
return attrib;
}
int configure_algorithms(DSPAM_CTX *CTX) {
if (_ds_read_attribute(agent_config, "Algorithm"))
CTX->algorithms = 0;
if (_ds_match_attribute(agent_config, "Algorithm", "graham"))
CTX->algorithms |= DSA_GRAHAM;
if (_ds_match_attribute(agent_config, "Algorithm", "burton"))
CTX->algorithms |= DSA_BURTON;
if (_ds_match_attribute(agent_config, "Algorithm", "robinson"))
CTX->algorithms |= DSA_ROBINSON;
if (_ds_match_attribute(agent_config, "Algorithm", "naive"))
CTX->algorithms |= DSA_NAIVE;
if (_ds_match_attribute(agent_config, "PValue", "robinson"))
CTX->algorithms |= DSP_ROBINSON;
else if (_ds_match_attribute(agent_config, "PValue", "markov"))
CTX->algorithms |= DSP_MARKOV;
else
CTX->algorithms |= DSP_GRAHAM;
if (_ds_match_attribute(agent_config, "Tokenizer", "word"))
CTX->tokenizer = DSZ_WORD;
else if (_ds_match_attribute(agent_config, "Tokenizer", "chain") ||
_ds_match_attribute(agent_config, "Tokenizer", "chained"))
CTX->tokenizer = DSZ_CHAIN;
else if (_ds_match_attribute(agent_config, "Tokenizer", "sbph"))
CTX->tokenizer = DSZ_SBPH;
else if (_ds_match_attribute(agent_config, "Tokenizer", "osb"))
CTX->tokenizer = DSZ_OSB;
if (_ds_match_attribute(agent_config, "Algorithm", "chi-square"))
{
if (CTX->algorithms != 0 && CTX->algorithms != DSP_ROBINSON) {
LOG(LOG_WARNING, "Warning: Chi-Square algorithm enabled with other algorithms. False positives may ensue.");
}
CTX->algorithms |= DSA_CHI_SQUARE;
}
return 0;
}
agent_pref_t pref_config(void)
{
agent_pref_t PTX = malloc(sizeof(agent_attrib_t)*PREF_MAX);
agent_pref_t ptr;
attribute_t attrib;
char *p, *q;
char *ptrptr = NULL;
int i = 0;
if (PTX == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return NULL;
}
PTX[0] = NULL;
/* Apply default preferences from dspam.conf */
attrib = _ds_find_attribute(agent_config, "Preference");
LOGDEBUG("Loading preferences from dspam.conf");
while(attrib != NULL) {
char *pcopy = strdup(attrib->value);
p = strtok_r(pcopy, "=", &ptrptr);
if (p == NULL) {
free(pcopy);
continue;
}
q = p + strlen(p)+1;
PTX[i] = _ds_pref_new(p, q);
PTX[i+1] = NULL;
i++;
attrib = attrib->next;
free(pcopy);
}
ptr = realloc(PTX, sizeof(agent_attrib_t)*(i+1));
if (ptr)
return ptr;
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return PTX;
}