/* $Id: pref.c,v 1.38 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
#include
#include
#include
#include
#ifndef _WIN32
#include
#include
#endif
#include
#include
#include
#include "pref.h"
#include "config.h"
#include "util.h"
#include "language.h"
#include "read_config.h"
/*
* _ds_pref_aggregate: aggregate system preferences and user preferences
*
* This function takes a set of system preferences and a set of user
* preferences as input and returns an aggregated set of preferences based on
* the system's override rules.
*/
agent_pref_t _ds_pref_aggregate(agent_pref_t STX, agent_pref_t UTX) {
agent_pref_t PTX = calloc(1, PREF_MAX*sizeof(agent_attrib_t ));
int i, size = 0;
if (STX) {
for(i=0;STX[i];i++) {
PTX[i] = _ds_pref_new(STX[i]->attribute, STX[i]->value);
PTX[i+1] = NULL;
size++;
}
}
if (UTX) {
for(i=0;UTX[i];i++) {
if (_ds_match_attribute(agent_config, "AllowOverride", UTX[i]->attribute))
{
int found = 0;
int j;
for(j=0;PTX[j];j++) {
if (!strcasecmp(PTX[j]->attribute, UTX[i]->attribute)) {
found = 1;
free(PTX[j]->value);
PTX[j]->value = strdup(UTX[i]->value);
break;
}
}
if (!found) {
PTX[size] = _ds_pref_new(UTX[i]->attribute, UTX[i]->value);
PTX[size+1] = NULL;
size++;
}
} else {
LOG(LOG_ERR, ERR_AGENT_IGNORE_PREF, UTX[i]->attribute);
}
}
}
return PTX;
}
int _ds_pref_free(agent_pref_t PTX) {
agent_attrib_t pref;
int i;
if (!PTX)
return 0;
for(i=0;PTX[i];i++) {
pref = PTX[i];
free(pref->attribute);
free(pref->value);
free(pref);
}
return 0;
}
/*
* _ds_pref_val: returns the value of an attribute within a preference set
*
* To allow this function to work with string operations, "" will be returned
* if the value isn't found, insttead of NULL
*/
const char *_ds_pref_val(
agent_pref_t PTX,
const char *attrib)
{
agent_attrib_t pref;
int i;
if (PTX == NULL)
return "";
for(i=0;PTX[i];i++) {
pref = PTX[i];
if (!strcasecmp(pref->attribute, attrib))
return pref->value;
}
return "";
}
agent_attrib_t _ds_pref_new(const char *attribute, const char *value) {
agent_attrib_t pref;
pref = malloc(sizeof(struct _ds_agent_attribute));
if (pref == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return NULL;
}
pref->attribute = strdup(attribute);
pref->value = strdup(value);
return pref;
}
/* flat-file preference extensions (defaulted to if storage driver extensions
are not found) */
agent_pref_t _ds_ff_pref_load(
config_t config,
const char *user,
const char *home,
void *ignore)
{
char filename[MAX_FILENAME_LENGTH];
agent_pref_t PTX = malloc(sizeof(agent_attrib_t )*PREF_MAX);
char buff[258];
FILE *file;
char *p, *q;
int i = 0;
config = config; /* Keep compiler happy */
ignore = ignore; /* Keep compiler happy */
if (PTX == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return NULL;
}
PTX[0] = NULL;
if (user == NULL) {
snprintf(filename, MAX_FILENAME_LENGTH, "%s/default.prefs", home);
} else {
_ds_userdir_path (filename, home, user, "prefs");
}
file = fopen(filename, "r");
/* Apply default preferences from dspam.conf */
if (file != NULL) {
char *ptrptr;
while(i<(PREF_MAX-1) && fgets(buff, sizeof(buff), file)!=NULL) {
if (buff[0] == '#' || buff[0] == 0)
continue;
chomp(buff);
p = strtok_r(buff, "=", &ptrptr);
if (p == NULL)
continue;
q = p + strlen(p)+1;
LOGDEBUG("Loading preference '%s' = '%s'", p, q);
PTX[i] = _ds_pref_new(p, q);
PTX[i+1] = NULL;
i++;
}
fclose(file);
} else {
free(PTX);
return NULL;
}
return PTX;
}
/*
* _ds_ff_pref_prepare_file: prepares a backup copy of a preference file
*
* This operation prepares a backup copy of a given preference file, using a
* .bak extension and returns an open filehandle to it at the end of the file.
* This function also allows for an omission to be passed in, which is the name
* of a preference that should be omitted from the file (if that preference
* is to be overwritten or deleted. If nlines is provided, it will be set to
* the number of lines in the file.
*/
FILE *_ds_ff_pref_prepare_file (
const char *filename,
const char *omission,
int *nlines)
{
char line[1024], out_filename[MAX_FILENAME_LENGTH];
int lineno = 0, omission_len;
FILE *in_file, *out_file;
char omission_pref[1024];
snprintf(omission_pref, sizeof(omission_pref), "%s=", omission);
omission_len = strlen(omission_pref);
snprintf(out_filename, MAX_FILENAME_LENGTH, "%s.bak", filename);
out_file = fopen(out_filename, "w");
if (out_file == NULL) {
LOG(LOG_ERR, ERR_IO_FILE_OPEN, out_filename, strerror(errno));
return NULL;
}
in_file = fopen(filename, "r");
if (in_file) {
while (fgets(line, sizeof(line), in_file)) {
if (!strncmp(line, omission_pref, omission_len))
continue;
lineno++;
if (fputs(line, out_file)<0) {
LOG(LOG_ERR, ERR_IO_FILE_WRITE, out_filename, strerror(errno));
fclose(in_file);
fclose(out_file);
unlink(out_filename);
return NULL;
}
}
fclose(in_file);
}
if (nlines != NULL)
*nlines = lineno;
return out_file;
}
/* _ds_pref_commit: close scratch copy and commit it as the new live copy */
int _ds_ff_pref_commit (
const char *filename,
FILE *out_file)
{
char backup[MAX_FILENAME_LENGTH];
snprintf(backup, sizeof(backup), "%s.bak", filename);
if (fclose(out_file)) {
LOG(LOG_ERR, ERR_IO_FILE_CLOSE, backup, strerror(errno));
return EFAILURE;
}
if (rename(backup, filename)) {
LOG(LOG_ERR, ERR_IO_FILE_RENAME, backup, strerror(errno));
unlink(backup);
return EFAILURE;
}
return 0;
}
int _ds_ff_pref_set (
config_t config,
const char *username,
const char *home,
const char *preference,
const char *value,
void *ignore)
{
char filename[MAX_FILENAME_LENGTH];
FILE *out_file;
config = config; /* Keep compiler happy */
ignore = ignore; /* Keep compiler happy */
if (username == NULL) {
snprintf(filename, MAX_FILENAME_LENGTH, "%s/default.prefs", home);
} else {
_ds_userdir_path (filename, home, username, "prefs");
}
out_file = _ds_ff_pref_prepare_file(filename, preference, NULL);
if (out_file == NULL)
return EFAILURE;
fprintf(out_file, "%s=%s\n", preference, value);
return _ds_ff_pref_commit(filename, out_file);
}
int _ds_ff_pref_del (
config_t config,
const char *username,
const char *home,
const char *preference,
void *ignore)
{
char filename[MAX_FILENAME_LENGTH];
FILE *out_file;
int nlines;
config = config; /* Keep compiler happy */
ignore = ignore; /* Keep compiler happy */
if (username == NULL) {
snprintf(filename, MAX_FILENAME_LENGTH, "%s/default.prefs", home);
} else {
_ds_userdir_path (filename, home, username, "prefs");
}
out_file = _ds_ff_pref_prepare_file(filename, preference, &nlines);
if (out_file == NULL)
return EFAILURE;
if (!nlines) {
char backup[MAX_FILENAME_LENGTH];
fclose(out_file);
snprintf(backup, sizeof(backup), "%s.bak", filename);
unlink(backup);
return unlink(filename);
}
return _ds_ff_pref_commit(filename, out_file);
}