/* * 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 #include #include #include #include #include #include #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; }