/*
* Program: Synonym
* File: config.c
* Author: Ionut Nistor
* Date: 20 Feb 2003
*
* $Id: config.c,v 1.6.2.1 2004/10/20 12:30:11 ionut Exp $
*
* Licensed under the Modulo Consulting Software License
* (see file license.txt)
*
*/
const char config_c_objid[]="$Id: config.c,v 1.6.2.1 2004/10/20 12:30:11 ionut Exp $";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <regex.h>
#include <libxml/tree.h>
#include "synonym.h"
#include "config.h"
char * Synonym_Trim_Whitespaces(char *target)
{
char buf[512];
char *tok;
tok=target;
while((*tok==' ')||(*tok=='\t'))
tok++;
strcpy(buf, tok);
if(strlen(buf))
{
tok=buf+strlen(buf)-1;
while((*tok==' ')||(*tok=='\t')||(*tok=='\r')||(*tok=='\n'))
tok--;
*(tok+1)='\0';
}
strcpy(target, buf);
return target;
}
sresult Synonym_Parse_Condition(xmlNodePtr condition_node, synonym_condition *condition)
{
xmlNodePtr current_node;
for(current_node=condition_node->next; current_node != NULL; current_node=current_node->next)
{
if(!strcasecmp(current_node->name, TAG_HEADER) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Header condition details - look for the node content */
{
if(condition->header_field[0] != 0) /* Did we find a Header element before? */
{
syslog(LOG_ERR, "More then one Header defined in a condition");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(condition->header_field, current_node->children->content, MAX_FIELD_SIZE);
condition->header_field[MAX_FIELD_SIZE] = '\0';
if(condition->header_field[0] == '\0')
{
syslog(LOG_ERR, "Void Header defined");
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The header tag must contain a value");
return SYNONYM_CONFIG_ERROR;
}
}
if(!strcasecmp(current_node->name, TAG_MATCH) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Match condition details - look for the node content */
{
if(condition->header_regexp[0] != 0) /* Did we find a Match element before? */
{
syslog(LOG_ERR, "More then one Match defined in a condition");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(condition->header_regexp, current_node->children->content, MAX_REGEXP_SIZE);
condition->header_regexp[MAX_REGEXP_SIZE] = '\0';
if(condition->header_regexp[0] == '\0')
{
syslog(LOG_ERR, "Void Match defined");
return SYNONYM_CONFIG_ERROR;
}
if(regcomp(&condition->header_compiled_regexp, condition->header_regexp, REG_NOSUB|REG_ICASE))
{
syslog(LOG_ERR, "Invalid regular expression defined - \"%s\"", condition->header_regexp);
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The match field must contain a value");
return SYNONYM_CONFIG_ERROR;
}
}
if(!strcasecmp(current_node->name, TAG_FROM) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the 'From' condition details - look for the node content */
{
if(condition->env_from_condition[0] != '\0') /* Did we find a 'From' element before? */
{
syslog(LOG_ERR, "More then one 'From' defined in a condition");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(condition->env_from_condition, current_node->children->content, MAX_REGEXP_SIZE);
condition->env_from_condition[MAX_REGEXP_SIZE] = '\0';
if(condition->env_from_condition[0] == '\0')
{
syslog(LOG_ERR, "Void 'From' defined");
return SYNONYM_CONFIG_ERROR;
}
if(regcomp(&condition->env_from_compiled_regexp, condition->env_from_condition, REG_NOSUB|REG_ICASE))
{
syslog(LOG_ERR, "Invalid regular expression defined for 'From' - \"%s\"", condition->env_from_condition);
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The 'From' must contain a value");
return SYNONYM_CONFIG_ERROR;
}
}
if(!strcasecmp(current_node->name, TAG_TO) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the 'To' condition details - look for the node content */
{
if(condition->env_to_condition[0] != '\0') /* Did we find a 'To' element before? */
{
syslog(LOG_ERR, "More then one 'To' defined in a condition");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(condition->env_to_condition, current_node->children->content, MAX_REGEXP_SIZE);
condition->env_to_condition[MAX_REGEXP_SIZE] = '\0';
if(condition->env_to_condition[0] == '\0')
{
syslog(LOG_ERR, "Void 'To' defined");
return SYNONYM_CONFIG_ERROR;
}
if(regcomp(&condition->env_to_compiled_regexp, condition->env_to_condition, REG_NOSUB|REG_ICASE))
{
syslog(LOG_ERR, "Invalid regular expression defined for 'To' - \"%s\"", condition->env_from_condition);
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The 'To' must contain a value");
return SYNONYM_CONFIG_ERROR;
}
}
}
if((condition->env_from_condition[0] != '\0' && condition->env_to_condition[0] != '\0') ||
(condition->env_from_condition[0] != '\0' && condition->header_field[0] != '\0') ||
(condition->header_field[0] != '\0' && condition->env_to_condition[0] != '\0'))
{ /* Only one of 'Header', 'From', 'To' may be defined in a single condition */
syslog(LOG_ERR, "Only one of 'Header', 'From', 'To' may be defined in one condition");
return SYNONYM_CONFIG_ERROR;
}
if(condition->header_field[0] == '\0' && condition->env_from_condition[0] == '\0' && condition->env_to_condition[0] == '\0')
{
syslog(LOG_ERR, "No 'Header', 'To' or 'From' defined in the condition");
return SYNONYM_CONFIG_ERROR;
}
if(condition->header_field[0] != '\0' && condition->header_regexp[0] == '\0')
{
syslog(LOG_ERR, "No Match directive for the header condition");
return SYNONYM_CONFIG_ERROR;
}
return SYNONYM_OK;
}
sresult Synonym_Parse_Action(xmlNodePtr action_node, synonym_action *action)
{
xmlNodePtr current_node;
for(current_node=action_node->next; current_node != NULL; current_node=current_node->next)
{
if(!strcasecmp(current_node->name, TAG_ACTIONTYPE) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the ActionType action details - look for the node content */
{
if(action->action_type != ACTION_NONE) /* Did we find an ActionType element before? */
{
syslog(LOG_ERR, "More then one action type defined in an action");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
if(!strcasecmp(current_node->children->content, ACTIONNAME_COPY))
action->action_type = ACTION_COPY;
if(!strcasecmp(current_node->children->content, ACTIONNAME_DELETE))
action->action_type = ACTION_DELETE;
if(!strcasecmp(current_node->children->content, ACTIONNAME_REJECT))
action->action_type = ACTION_REJECT;
if(!strcasecmp(current_node->children->content, ACTIONNAME_DISCLAIMER))
action->action_type = ACTION_DISCLAIMER;
if(action->action_type == ACTION_NONE)
{
syslog(LOG_ERR, "Invalid action type defined");
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "Action must contain a value");
return SYNONYM_CONFIG_ERROR;
}
}
if(!strcasecmp(current_node->name, TAG_ACTIONADDRESS) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Match condition details - look for the node content */
{
if(action->action_custom_field[0] != '\0') /* Did we find a address element before? */
{
syslog(LOG_ERR, "More then one address defined in the action");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(action->action_custom_field, current_node->children->content, MAX_ACTION_CUSTOMFIELD_SIZE);
action->action_custom_field[MAX_ACTION_CUSTOMFIELD_SIZE] = '\0';
if(action->action_custom_field[0] == '\0')
{
syslog(LOG_ERR, "Void action address defined");
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The action address must contain a value");
return SYNONYM_CONFIG_ERROR;
}
}
if(!strcasecmp(current_node->name, TAG_TEXT_DISCLAIMER) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Match condition details - look for the node content */
{
#ifdef DISCLAIMER_SUPPORT
if(action->action_text_disclaimer[0] != '\0') /* Did we find a address element before? */
{
syslog(LOG_ERR, "More then one text disclaimer defined in the action");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(action->action_text_disclaimer, current_node->children->content, MAX_DISCLAIMER_SIZE);
action->action_text_disclaimer[MAX_DISCLAIMER_SIZE] = '\0';
if(action->action_text_disclaimer[0] == '\0')
{
syslog(LOG_ERR, "Void text disclaimer defined");
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The text disclaimer must contain a value");
return SYNONYM_CONFIG_ERROR;
}
#else
syslog(LOG_ERR, "Disclaimer support not available. Please recompile.");
return SYNONYM_CONFIG_ERROR;
#endif
}
if(!strcasecmp(current_node->name, TAG_HTML_DISCLAIMER) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Match condition details - look for the node content */
{
#ifdef DISCLAIMER_SUPPORT
if(action->action_html_disclaimer[0] != '\0') /* Did we find a address element before? */
{
syslog(LOG_ERR, "More then one html disclaimer defined in the action");
return SYNONYM_CONFIG_ERROR;
}
else
if(current_node->children != NULL)
if(current_node->children->type==XML_TEXT_NODE) /* Has to be - otherwise we got a problem */
{
strncpy(action->action_html_disclaimer, current_node->children->content, MAX_DISCLAIMER_SIZE);
action->action_html_disclaimer[MAX_DISCLAIMER_SIZE] = '\0';
if(action->action_html_disclaimer[0] == '\0')
{
syslog(LOG_ERR, "Void html disclaimer defined");
return SYNONYM_CONFIG_ERROR;
}
}
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "The html disclaimer must contain a value");
return SYNONYM_CONFIG_ERROR;
}
#else
syslog(LOG_ERR, "Disclaimer support not available. Please recompile.");
return SYNONYM_CONFIG_ERROR;
#endif
}
}
if(action->action_type == ACTION_NONE)
{
syslog(LOG_ERR, "No action defined for the rule");
return SYNONYM_CONFIG_ERROR;
}
if(action->action_custom_field[0] == '\0' && (action->action_type != ACTION_DELETE && action->action_type != ACTION_DISCLAIMER)) /* We enforce this rule only for actions other then delete; delete does not need the custom field */
{
syslog(LOG_ERR, "No address defined for the action of the rule");
return SYNONYM_CONFIG_ERROR;
}
#ifdef DISCLAIMER_SUPPORT
if(action->action_text_disclaimer[0] == '\0' && action->action_html_disclaimer[0] == '\0' && action->action_type == ACTION_DISCLAIMER)
{
syslog(LOG_ERR, "Either text or html disclaimer body must be defined");
return SYNONYM_CONFIG_ERROR;
}
#endif
return SYNONYM_OK;
}
sresult Synonym_Parse_Config_Rule(xmlNodePtr rule_node, synonym_rule *rule)
{
xmlNodePtr current_node, text_node;
synonym_condition *new_condition, *temp_condition;
for(current_node=rule_node->next; current_node != NULL; current_node=current_node->next)
{
if(!strcasecmp(current_node->name, TAG_CONDITION) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Condition details - look for the node content */
{
for(text_node=current_node->children; text_node!=NULL; text_node = text_node->children)
if(text_node->type == XML_TEXT_NODE) /* Got the content */
{
new_condition = (synonym_condition*)malloc(sizeof(synonym_condition));
if(new_condition == NULL)
{
syslog(LOG_CRIT, "CRITICAL: Memory allocation error - cannont continue");
return SYNONYM_ALLOC_FAILED;
}
new_condition->next=NULL;
new_condition->header_field[0]='\0';
new_condition->header_regexp[0]='\0';
new_condition->env_from_condition[0]='\0';
new_condition->env_to_condition[0]='\0';
if(Synonym_Parse_Condition(text_node, new_condition) != SYNONYM_OK)
{
free(new_condition);
syslog(LOG_ERR, "Problem located in the Condition section of the config file");
return SYNONYM_CONFIG_ERROR;
}
else /* add the rule to the end of the rule chain */
{
if(rule->conditions != NULL)
{
for(temp_condition=rule->conditions; temp_condition->next!=NULL; temp_condition=temp_condition->next)
;
temp_condition->next=new_condition;
}
else
rule->conditions=new_condition;
}
}
}
if(!strcasecmp(current_node->name, TAG_ACTION) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Rule details - look for the node content */
{
for(text_node=current_node->children; text_node!=NULL; text_node = text_node->children)
if(text_node->type == XML_TEXT_NODE) /* Got the content */
if(Synonym_Parse_Action(text_node, &(rule->action))!=SYNONYM_OK)
{
syslog(LOG_ERR, "Failed to parse an action section");
return SYNONYM_CONFIG_ERROR;
}
}
}
if(rule->conditions == NULL)
{
syslog(LOG_ERR, "No conditions found in the rule");
return SYNONYM_CONFIG_ERROR;
}
if(rule->action.action_type == ACTION_NONE)
{
syslog(LOG_ERR, "Action was not defined in the rule");
return SYNONYM_CONFIG_ERROR;
}
return SYNONYM_OK;
}
sresult Synonym_Parse_Rules_Config_Section(xmlNodePtr rules_node, _type_synonym_config *config)
{
xmlNodePtr current_node, text_node;
synonym_rule *new_rule, *temp_rule;
for(current_node=rules_node->next; current_node != NULL; current_node=current_node->next)
{
if(!strcasecmp(current_node->name, TAG_RULE) && current_node->type==XML_ELEMENT_NODE) /* Ok, go to the Rule details - look for the node content */
for(text_node=current_node->children; text_node!=NULL; text_node = text_node->children)
if(text_node->type == XML_TEXT_NODE) /* Got the content */
{
new_rule = (synonym_rule*)malloc(sizeof(synonym_rule));
if(new_rule == NULL)
{
syslog(LOG_CRIT, "CRITICAL: Memory allocation error - cannont continue");
return SYNONYM_ALLOC_FAILED;
}
new_rule->next=NULL;
new_rule->conditions=NULL;
new_rule->action.action_type=ACTION_NONE;
new_rule->action.action_custom_field[0]='\0';
#ifdef DISCLAIMER_SUPPORT
new_rule->action.action_text_disclaimer[0] = '\0';
new_rule->action.action_html_disclaimer[0] = '\0';
#endif
if(Synonym_Parse_Config_Rule(text_node, new_rule) != SYNONYM_OK)
{
free(new_rule);
return SYNONYM_CONFIG_ERROR;
}
else /* add the rule to the end of the rule chain */
{
if(config->rules != NULL)
{
for(temp_rule=config->rules; temp_rule->next!=NULL; temp_rule=temp_rule->next)
;
temp_rule->next=new_rule;
}
else
config->rules=new_rule;
}
}
}
if(config->rules == NULL) /* No rules were found in the config file */
{
syslog(LOG_ERR, "No rules defined in the configuration file");
return SYNONYM_CONFIG_ERROR;
}
else
return SYNONYM_OK;
}
sresult Synonym_Get_Config(char *configfile, _type_synonym_config *config)
{
xmlDocPtr config_tree;
xmlNodePtr current_node, text_node;
config->rules=NULL; /* In order to be able to check wether at least one rule was defined */
config_tree = xmlParseFile(configfile);
if(configfile == NULL)
{
syslog(LOG_ERR, "Unable to load the configuration file %s (file does not exist or parse error)", configfile);
return SYNONYM_FAILED_LOAD_CONFIG;
}
for(current_node=xmlDocGetRootElement(config_tree);current_node!=NULL;current_node=current_node->children)
{
if(!strcasecmp(current_node->name, TAG_RULES)) /* We are in the global section - look for the node content */
for(text_node=current_node->children; text_node!=NULL; text_node = text_node->children)
if(text_node->type == XML_TEXT_NODE)
return Synonym_Parse_Rules_Config_Section(text_node, config);
else
{
syslog(LOG_ERR, "Internal error in %s, line number %d", __FILE__, __LINE__);
return SYNONYM_CONFIG_INTERNAL_ERROR;
}
else
{
syslog(LOG_ERR, "Unknown section (%s) encountered", current_node->name);
return SYNONYM_CONFIG_ERROR;
}
}
/* If we got here, no 'Rules' section was found so we got a problem */
syslog(LOG_ERR, "Not Rules section found");
return SYNONYM_CONFIG_ERROR;
}
void Synonym_Free_Config(_type_synonym_config *config)
{
synonym_rule *current_rule, *previous_rule;
synonym_condition *current_condition, *previous_condition;
current_rule = config->rules;
previous_rule = current_rule;
while(current_rule!=NULL)
{
/* Go to the conditions and free them */
current_condition = current_rule->conditions;
previous_condition = current_condition;
while(current_condition != NULL)
{
/* Fix leak in config.c*/
regfree(¤t_condition->header_compiled_regexp);
previous_condition = current_condition;
current_condition = current_condition->next;
free(previous_condition);
}
previous_rule=current_rule;
current_rule = current_rule->next;
free(previous_rule);
}
config->rules = NULL;
}
sresult Synonym_Reload_Config(char *configfile, _type_synonym_config *config)
{
_type_synonym_config temp_config;
/* We'll load the configuration in a temporary structure to check if it (the config) is consistent, then replace the 'live' config
We need to have some mutexes enabled for the config */
if(Synonym_Get_Config(configfile, &temp_config) != SYNONYM_OK)
{
syslog(LOG_ERR, "Failed to reload the configuration");
return SYNONYM_CONFIG_ERROR;
}
else
{ /* The configuration was ok, we should replace the old one */
/* Lock the config */
Synonym_Free_Config(config);
/* Copy the configuration structure */
config->rules = temp_config.rules;
/* Release the lock*/
}
return SYNONYM_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1