/* This module was written by * * Eduardo Chappa (chappa@washington.edu) * http://patches.freeiz.com/alpine/ * * Original Version: November 1999 * Last Modified : September 14, 2013 * * Send bug reports about this module to the address above. */ #include "../pith/headers.h" #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/copyaddr.h" #include "../pith/mailindx.h" #include "../pith/rules.h" #define CSEP_C ('\001') #define CSEP_S ("\001") /* Internal Prototypes */ int test_condition (CONDITION_S *, int, ENVELOPE *); int test_in (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); int test_ni (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); int test_not_in (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); int test_not_ni (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); int test_eq (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); int test_not_eq (CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int); int isolate_condition (char *, char **, int *); int sanity_check_condition (char *); char *test_rule (RULELIST *, int, ENVELOPE *, int *); char *trim (RULEACTION_S *, int, ENVELOPE *); char *rextrim (RULEACTION_S *, int, ENVELOPE *); char *raw_value (RULEACTION_S *, int, ENVELOPE *); char *extended_value (RULEACTION_S *, int, ENVELOPE *); char *exec_fcn (RULEACTION_S *, int, ENVELOPE *); char *expand (char *, void *); char *get_name_token (char *); char *advance_to_char (char *, char, int, int *); char **functions_for_token (char *); char *canonicalize_condition (char *, int *); void free_token_value (TOKEN_VALUE **); void free_condition (CONDITION_S **); void free_condition_value (CONDVALUE_S **); void free_ruleaction (RULEACTION_S **); void free_rule (RULE_S **); void free_rule_list (RULELIST **); void free_alloc_rule (void **, int); void *alloc_mem (size_t); void add_rule (int, int); void set_rule_list (struct variable *); void parse_patterns_into_action(TOKEN_VALUE **); void free_parsed_value(TOKEN_VALUE **value); RULE_S *parse_rule (char *, int); RULELIST *get_rule_list (char **, int, int); TOKEN_VALUE *parse_group_data (char *,int *); TOKEN_VALUE *copy_parsed_value (TOKEN_VALUE *, int, ENVELOPE *); CONDVALUE_S *fill_condition_value (char *); CONDITION_S *fill_condition (char *); CONDITION_S *parse_condition (char *, int *); PRULELIST_S *add_prule (PRULELIST_S *, PRULELIST_S *); RULEACTION_S *parse_action (char *, int); REL_TOKEN rel_rules_test[] = { {EQ_REL, Equal, test_eq}, {IN_REL, Subset, test_in}, {NI_REL, Includes, test_ni}, {NOT_EQ_REL, NotEqual, test_not_eq}, {NOT_IN_REL, NotSubset, test_not_in}, {NOT_NI_REL, NotIncludes, test_not_ni}, {NULL, EndTypes, NULL} }; #define NREL (sizeof(rel_rules_test)/sizeof(rel_rules_test[0]) - 1) RULE_FCN rule_fcns[] = { {COPY_FCN, extended_value, FOR_SAVE|FOR_COMPOSE}, {SAVE_FCN, extended_value, FOR_SAVE}, {EXEC_FCN, exec_fcn, FOR_REPLACE|FOR_TRIM|FOR_RESUB|FOR_COMPOSE}, {REPLY_FCN, extended_value, FOR_REPLY_INTRO}, {TRIM_FCN, trim, FOR_TRIM|FOR_RESUB|FOR_COMPOSE}, {REPLACE_FCN, extended_value, FOR_REPLACE}, {SORT_FCN, raw_value, FOR_SORT}, {INDEX_FCN, raw_value, FOR_INDEX}, {COMMAND_FCN, raw_value, FOR_KEY}, {REPLYSTR_FCN, raw_value, FOR_COMPOSE}, {SIGNATURE_FCN, raw_value, FOR_COMPOSE}, {RESUB_FCN, extended_value, FOR_RESUB}, {STARTUP_FCN, raw_value, FOR_STARTUP}, {REXTRIM_FCN, rextrim, FOR_TRIM|FOR_RESUB|FOR_COMPOSE}, {THRDSTYLE_FCN, raw_value, FOR_THREAD}, {THRDINDEX_FCN, raw_value, FOR_THREAD}, {SMTP_FCN, raw_value, FOR_COMPOSE}, {NULL, 0, FOR_NOTHING} }; char* token_rules[] = { FROM_TOKEN, NICK_TOKEN, FCCF_TOKEN, FCCS_TOKEN, OTEXT_TOKEN, OTEXTNQ_TOKEN, ROLE_TOKEN, FOLDER_TOKEN, SUBJ_TOKEN, PROCID_TOKEN, THDDSPSTY_TOKEN, THDNDXSTY_TOKEN, FLAG_TOKEN, COLLECT_TOKEN, THDDSPSTY_TOKEN, ADDR_TOKEN, TO_TOKEN, ADDTO_TOKEN, ADDCC_TOKEN, ADDRECIP_TOKEN, SCREEN_TOKEN, KEY_TOKEN, SEND_TOKEN, CC_TOKEN, LCC_TOKEN, BCC_TOKEN, FFROM_TOKEN, FADDRESS_TOKEN, NULL }; #define NTOKENS (sizeof(token_rules)/sizeof(token_rules[0]) - 1) #define NFCN (sizeof(rule_fcns)/sizeof(rule_fcns[0]) - 1) char *subj_fcn[] = {SUBJ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; char *from_fcn[] = {FROM_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; char *otext_fcn[] = {OTEXT_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; char *otextnq_fcn[] = {OTEXTNQ_TOKEN, REPLACE_FCN, TRIM_FCN, REXTRIM_FCN, EXEC_FCN}; char *adto_fcn[] = {ADDTO_TOKEN, EXEC_FCN, NULL, NULL, NULL}; char **fcns_for_index[] = {subj_fcn, from_fcn, otext_fcn, otextnq_fcn}; #define NFCNFI (sizeof(fcns_for_index)/sizeof(fcns_for_index[0])) /*for idx*/ #define NFPT (sizeof(fcns_for_index[0])) /* functions pert token */ SPAREP_S * get_sparep_for_rule(char *value, int flag) { SPAREP_S *rv; rv = (SPAREP_S *) alloc_mem(sizeof(SPAREP_S)); rv->flag = flag; rv->value = value ? cpystr(value) : NULL; return rv; } void free_sparep_for_rule(void **sparep) { SPAREP_S *spare = (SPAREP_S *) *sparep; if(!spare) return; if(spare->value) fs_give((void **)&spare->value); fs_give((void **)sparep); } int context_for_function(char *name) { int i, j; for (i = 0; i < NFCN && strcmp(rule_fcns[i].name, name); i++); return i == NFCN ? 0 : rule_fcns[i].what_for; } char **functions_for_token(char *name) { int i; for (i = 0; i < NFCNFI && strcmp(fcns_for_index[i][0], name); i++); return i == NFCNFI ? NULL : fcns_for_index[i]; } void free_alloc_rule (void **voidtext, int code) { switch(code){ case FREEREGEX : regfree((regex_t *)*voidtext); break; default: break; } } void free_token_value(TOKEN_VALUE **token) { if(token && *token){ if ((*token)->testxt) fs_give((void **)&(*token)->testxt); free_alloc_rule (&(*token)->voidtxt, (*token)->codefcn); if((*token)->next) free_token_value(&(*token)->next); fs_give((void **)token); } } void free_condition_value(CONDVALUE_S **cvalue) { if(cvalue && *cvalue){ if ((*cvalue)->tname) fs_give((void **)&(*cvalue)->tname); if ((*cvalue)->value) free_token_value(&(*cvalue)->value); fs_give((void **)cvalue); } } void free_condition(CONDITION_S **condition) { if(condition && *condition){ if((*condition)->cndtype == Condition) free_condition_value((CONDVALUE_S **)&(*condition)->cndrule); else if((*condition)->cndtype == ParOpen || (*condition)->cndtype == ParClose) fs_give(&(*condition)->cndrule); if((*condition)->next) free_condition(&((*condition)->next)); fs_give((void **)condition); } } void free_ruleaction(RULEACTION_S **raction) { if(raction && *raction){ if ((*raction)->token) fs_give((void **)&((*raction)->token)); if ((*raction)->function) fs_give((void **)&((*raction)->function)); if ((*raction)->value) free_token_value(&(*raction)->value); fs_give((void **)raction); } } void free_rule(RULE_S **rule) { if(rule && *rule){ free_condition(&((*rule)->condition)); free_ruleaction(&((*rule)->action)); fs_give((void **)rule); } } void free_rule_list(RULELIST **rule) { if(!*rule) return; if((*rule)->next) free_rule_list(&((*rule)->next)); if((*rule)->prule) free_rule(&((*rule)->prule)); fs_give((void **)rule); } void free_parsed_rule_list(PRULELIST_S **rule) { if(!*rule) return; if((*rule)->next) free_parsed_rule_list(&((*rule)->next)); if((*rule)->rlist) free_rule_list(&((*rule)->rlist)); fs_give((void **)rule); } void * alloc_mem (size_t amount) { void *genmem; memset(genmem = fs_get(amount), 0, amount); return genmem; } void parse_patterns_into_action(TOKEN_VALUE **tokenp) { if(!*tokenp) return; if((*tokenp)->testxt){ regex_t preg; (*tokenp)->voidtxt = NULL; (*tokenp)->voidtxt = fs_get(sizeof(regex_t)); if (regcomp((regex_t *)(*tokenp)->voidtxt, (*tokenp)->testxt, REG_EXTENDED) != 0){ regfree((regex_t *)(*tokenp)->voidtxt); (*tokenp)->voidtxt = NULL; } } if((*tokenp)->voidtxt) (*tokenp)->codefcn = FREEREGEX; if((*tokenp)->next) parse_patterns_into_action(&(*tokenp)->next); } int isolate_condition (char *data, char **cvalue, int *len) { char *p = data; int done = 0, error = 0, next_condition = 0, l; if(*p == '"' && p[strlen(p) - 1] == '"'){ p[strlen(p) - 1] = '\0'; p++; } *cvalue = NULL; while (*p && !done){ switch (*p){ case '_': *cvalue = advance_to_char(p,'}', STRICTLY, NULL); if(*cvalue){ strcat(*cvalue,"}"); p += strlen(*cvalue); } else error++; done++; case ' ': p++; break; case '&': case '|': if (*(p+1) == *p){ /* looking for && or ||*/ p += 2; next_condition++; } else{ error++; done++; } break; case '=': /* looking for => or -> */ case '-': if (*(p+1) != '>' || next_condition) error++; done++; break; default : done++; error++; break; } } *len = p - data; return error ? -1 : (*cvalue ? 1 : 0); } TOKEN_VALUE * parse_group_data (char *data, int *error) { TOKEN_VALUE *rvalue; char *p, *d; int offset, err = 0, freeme = 0; if(error) *error = 0; if (!data) return (TOKEN_VALUE *) NULL; if(*data == '_'){ d = detoken_src(data, FOR_RULE, NULL, NULL, NULL, NULL); freeme++; } else d = data; rvalue = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); if (p = advance_to_char(d,';', STRICTLY, &offset)){ rvalue->testxt = p; rvalue->next = parse_group_data(d + strlen(p) + 1 + offset, error); } else if (p = advance_to_char(d,'}', STRICTLY, NULL)) rvalue->testxt = p; else if (d && *d == '}') rvalue->testxt = cpystr(""); else{ err++; free_token_value(&rvalue); } if (error) *error += err; if(freeme != 0 && d != NULL) fs_give((void **)&d); return(rvalue); } CONDVALUE_S * fill_condition_value(char *data) { CONDVALUE_S *condition; int i, done, error = 0; char *group; for (i = 0, done = 0; done == 0 && token_rules[i] != NULL; i++) done = strncmp(data,token_rules[i], strlen(token_rules[i])) ? 0 : 1; if (done){ condition = alloc_mem(sizeof(CONDVALUE_S)); condition->tname = cpystr(token_rules[--i]); data += strlen(token_rules[i]); } else if (*data == '_') { char *itokname; for (i = 0, done = 0; done == 0 && (itokname = itoken(i)->name) != NULL; i++) done = strncmp(data+1, itokname, strlen(itokname)) ? 0 : data[strlen(itokname) + 1] == '_'; if (done){ condition = (CONDVALUE_S *) alloc_mem(sizeof(CONDVALUE_S)); condition->tname = fs_get(strlen(itokname) + 3); sprintf(condition->tname, "_%s_", itokname); data += strlen(itokname) + 2; } else return NULL; } else return NULL; for (; *data && *data == ' '; data++); if (*data){ for (i = 0, done = 0; done == 0 && rel_rules_test[i].value != NULL; i++) done = strncmp(data, rel_rules_test[i].value, 2) ? 0 : 1; if (done) condition->ttype = rel_rules_test[--i].ttype; else{ free_condition_value(&condition); return NULL; } } else{ free_condition_value(&condition); return NULL; } data += 2; for (; *data && *data == ' '; data++); if (*data++ != '{'){ free_condition_value(&condition); return NULL; } group = advance_to_char(data,'}', STRICTLY, &error); if (group || (!group && error < 0)){ condition->value = parse_group_data(data, &error); if(group && error) free_condition_value(&condition); if(group) fs_give((void **) &group); } else free_condition_value(&condition); return condition; } char * canonicalize_condition(char *data, int *eoc) { char *p = data, *s, *t, c; char *q = fs_get((5*strlen(data)+1)*sizeof(char)); char tmp[10]; int level, done, error, i; if(eoc) *eoc = -1; /* assume error */ *q = '\0'; if(*p == '"'){ if(p[strlen(p) - 1] == '"') p[strlen(p) - 1] = '\0'; p++; } for(level = done = error = 0; *p && !done && !error; ){ switch(*p){ case ' ' : p++; break; case '(' : strcat(q, CSEP_S); strcat(q, "("); sprintf(tmp, "%d ", level++); strcat(q, tmp); p++; break; case ')' : strcat(q, CSEP_S); strcat(q, ")"); sprintf(tmp, "%d ", --level); strcat(q, tmp); p++; if(level < 0) error++; break; case '_' : for(s = p+1; *s >= 'A' && *s <= 'Z'; s++); for(i = 0; token_rules[i] != NULL; i++) if(!strncmp(token_rules[i], p, s-p)) break; if(token_rules[i] == NULL) error++; else if(*s++ == '_'){ for(; *s == ' '; s++); if(*s && *(s+1)){ for(i = 0; rel_rules_test[i].value != NULL; i++) if(!strncmp(rel_rules_test[i].value, s, 2)) break; if (rel_rules_test[i].value == NULL) error++; else{ s += 2; for(; *s == ' '; s++); if(*s == '{'){ if(*(s+1) != '}') t = advance_to_char(s+1,'}', STRICTLY, NULL); else t = cpystr(""); if(t != NULL){ for(i = 0; t[i] != '\0' && t[i] != CSEP_C; i++); if(t[i] == CSEP_C) error++; if(error == 0){ strcat(q, CSEP_S); strcat(q, "C["); s += strlen(t) + 1; /* get past '{' */ *s = '\0'; strcat(q, p); strcat(q, "}] "); *s++ = '}'; p = s; } fs_give((void **) &t); } else error++; } else error++; } } } else error++; break; case '|': case '&': if(*(p+1) = *p){ strcat(q, CSEP_S); strcat(q, *p == '|' ? "OR " : "AND "); p += 2; } else error++; break; case '-': case '=': if (*(p+1) == '>'){ if(eoc) *eoc = p - data; done++; } else error++; break; default : error++; break; } } if(error || level > 0) /*simplistic approach by now */ fs_give((void **)&q); else q[strlen(q)-1] = '\0'; return q; } /* for a canonical condition, return if it is constructed according * to logical rules such as AND or OR between conditions, etc. We assume * we already canonicalized data, or else this will not work. */ int sanity_check_condition(char *data) { int i, error; char *s, *t, *d; if(data == NULL || *data == '\0') /* no data in, no data out */ return 0; d = fs_get((strlen(data)+1)*sizeof(char)); for(s = data,i = 0; (t = strchr(s, CSEP_C))!= NULL && (d[i] = *(t+1)); s = t+1, i++); d[i] = '\0'; for(i = 0, error = 0; d[i] != '\0' && error == 0; i++){ switch(d[i]){ case 'C': if((d[i+1] != '\0' && (d[i+1] == '(' || d[i+1] == 'C')) || (i == 0 && d[1] != 'A' && d[1] != 'O' && d[1] != '\0')) error++; break; case ')': if(i == 0 || d[i+1] != '\0' && (d[i+1] == 'C' || d[i+1] == '(')) error++; break; case '(': if(d[i+1] == '\0' || d[i+1] == ')' || d[i+1] == 'A' || d[i+1] == 'O') error++; break; case 'O': case 'A': if(i == 0 || d[i+1] == '\0' || d[i+1] == ')' || d[i+1] == 'A' || d[i+1] == 'O') error++; break; default : error++; } } if(d) fs_give((void **)&d); return error ? 0 : 1; } /* given a parsed data that satisfies sanity checks, parse it * into a condition we can check later on. */ CONDITION_S * fill_condition(char *data) { char *s, *t, *u; CONDITION_S *rv = NULL; CONDVALUE_S *cvalue; int *i; if(data == NULL || *data == '\0' || (s = strchr(data, CSEP_C)) == NULL) return NULL; rv = (CONDITION_S *) alloc_mem(sizeof(CONDITION_S)); switch(*++s){ case ')': case '(': i = fs_get(sizeof(int)); *i = atoi(s+1); rv->cndrule = (void *) i; rv->cndtype = *s == '(' ? ParOpen : ParClose; break; case 'C': if((u = strchr(s+2, CSEP_C)) != NULL){ *u = '\0'; t = strrchr(s, ']'); t = '\0'; *u = CSEP_C; } else s[strlen(s) - 1] = '\0'; rv->cndrule = (void *) fill_condition_value(s+2); rv->cndtype = Condition; break; case 'A': case 'O': rv->cndtype = *s == 'A' ? And : Or; break; default : fs_give((void **)&rv); break; } rv->next = fill_condition(strchr(s, CSEP_C)); return rv; } /* eoc = end of condition, equal to -1 on error */ CONDITION_S * parse_condition (char *data, int *eoc) { CONDITION_S *condition = NULL; char *pvalue; if((pvalue = canonicalize_condition(data, eoc)) != NULL && sanity_check_condition(pvalue) > 0) condition = fill_condition(pvalue); if(pvalue) fs_give((void **)&pvalue); if (condition == NULL && eoc) *eoc = -1; return condition; } RULEACTION_S * parse_action (char *data, int context) { int i, done, is_save; RULEACTION_S *raction = NULL; char *function, *p = data; if (p == NULL || *p == '\0') return NULL; is_save = *p == '-'; p += 2; for (; *p == ' '; p++); if (is_save){ /* got "->", a save-rule separator */ raction = (RULEACTION_S *) alloc_mem(sizeof(RULEACTION_S)); raction->function = cpystr("_SAVE_"); raction->value = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); raction->context |= FOR_SAVE; raction->exec = extended_value; raction->value->testxt = cpystr(p); return raction; } for (i = 0, done = 0; !done && (i < NFCN); i++) done = (strstr(p,rule_fcns[i].name) == p); p += done ? strlen(rule_fcns[--i].name) + 1 : 0; if(!*p || (rule_fcns[i].what_for && !(rule_fcns[i].what_for & context))) return NULL; if (done){ raction = alloc_mem(sizeof(RULEACTION_S)); /* We assign raction->token to be subject. This is not necessary for most rules. It is done only for rules that need it and will not make any difference in rules that do not need it. It will hopefully reduce complexity in the language */ raction->token = cpystr(SUBJ_TOKEN); raction->function = cpystr(rule_fcns[i].name); raction->context = rule_fcns[i].what_for; raction->exec = rule_fcns[i].execute; raction->value = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); raction->value->testxt = advance_to_char(p,'}', STRICTLY, NULL); if(!raction->value->testxt) free_ruleaction(&raction); return raction; } done = (((function = strstr(p, "_TRIM_")) != NULL) ? 1 : ((function = strstr(p, "_COPY_")) != NULL) ? 2 : ((function = strstr(p, "_EXEC_")) != NULL) ? 3 : ((function = strstr(p, "_REXTRIM_")) != NULL) ? 4 : ((function = strstr(p, "_REPLACE_")) != NULL) ? 5 : 0); if(!function) return (RULEACTION_S *) NULL; *function = '\0'; raction = (RULEACTION_S *) alloc_mem(sizeof(RULEACTION_S)); raction->token = get_name_token(p); *function = '_'; p += strlen(raction->token) + 1; for (; *p && *p == ' '; p++); if (!strncmp(p, ":=", 2)) p += 2; else{ free_ruleaction(&raction); return NULL; } for (; *p && *p == ' '; p++); if (p != function){ free_ruleaction(&raction); return NULL; } p += done <= 3 ? 6 : 9; /* 6 = strlen("_EXEC_"), 9 = strlen("_REPLACE_") */ if (*p != '{'){ free_ruleaction(&raction); return NULL; } *p = '\0'; for(i = 0; i < NFCN && strcmp(function, rule_fcns[i].name);i++); raction->function = cpystr(function); raction->is_trim = strcmp(function,"_TRIM_") ? 0 : 1; raction->is_rextrim = strcmp(function,"_REXTRIM_") ? 0 : 1; raction->is_replace = strcmp(function,"_REPLACE_") ? 0 : 1; raction->context = rule_fcns[i].what_for; raction->exec = rule_fcns[i].execute; *p++ = '{'; if((raction->value = parse_group_data(p, NULL)) == NULL || raction->value->testxt == NULL) free_ruleaction(&raction); if(raction && raction->is_rextrim) parse_patterns_into_action(&raction->value); return raction; } RULE_S * parse_rule (char *data, int context) { RULE_S *prule; /*parsed rule */ int len = 0; if (!(prule = (RULE_S *) alloc_mem(sizeof(RULE_S))) || !(prule->condition = parse_condition(data, &len)) || !(prule->action = parse_action(data+len, context))) free_rule(&prule); return prule; } RULELIST * get_rule_list(char **list, int context, int i) { RULE_S *rule; RULELIST *trulelist = NULL; if (list[i] && *list[i]){ if(rule = parse_rule(list[i], context)){ trulelist = (RULELIST *)alloc_mem(sizeof(RULELIST)); trulelist->prule = rule; trulelist->next = get_rule_list(list, context, i+1); } else trulelist = get_rule_list(list, context, i+1); } return trulelist; } PRULELIST_S * add_prule(PRULELIST_S *rule_list, PRULELIST_S *rule) { if (!rule_list) rule_list = (PRULELIST_S *) alloc_mem(sizeof(PRULELIST_S)); if(rule_list->next) rule_list->next = add_prule(rule_list->next, rule); else{ if (rule_list->rlist) rule_list->next = rule; else rule_list = rule; } return rule_list; } void add_rule(int code, int context) { char **list = ps_global->vars[code].current_val.l; PRULELIST_S *prulelist, *trulelist, *orulelist; if (list && *list && **list){ trulelist = (PRULELIST_S *)alloc_mem(sizeof(PRULELIST_S)); trulelist->varnum = code; if (trulelist->rlist = get_rule_list(list, context, 0)) ps_global->rule_list = add_prule(ps_global->rule_list, trulelist); else free_parsed_rule_list(&trulelist); } } /* see create_rule_list below */ void set_rule_list(struct variable *vars) { set_current_val(&vars[V_THREAD_DISP_STYLE_RULES], TRUE, TRUE); set_current_val(&vars[V_THREAD_INDEX_STYLE_RULES], TRUE, TRUE); set_current_val(&vars[V_COMPOSE_RULES], TRUE, TRUE); set_current_val(&vars[V_FORWARD_RULES], TRUE, TRUE); set_current_val(&vars[V_INDEX_RULES], TRUE, TRUE); set_current_val(&vars[V_KEY_RULES], FALSE, TRUE); set_current_val(&vars[V_REPLACE_RULES], TRUE, TRUE); set_current_val(&vars[V_REPLY_INDENT_RULES], TRUE, TRUE); set_current_val(&vars[V_REPLY_LEADIN_RULES], TRUE, TRUE); set_current_val(&vars[V_RESUB_RULES], TRUE, TRUE); set_current_val(&vars[V_SAVE_RULES], TRUE, TRUE); set_current_val(&vars[V_SMTP_RULES], TRUE, TRUE); set_current_val(&vars[V_SORT_RULES], TRUE, TRUE); set_current_val(&vars[V_STARTUP_RULES], TRUE, TRUE); } /* see set_rule_list above */ void create_rule_list(struct variable *vars) { set_rule_list(vars); add_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD); add_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD); add_rule(V_COMPOSE_RULES, FOR_COMPOSE); add_rule(V_FORWARD_RULES, FOR_COMPOSE); add_rule(V_INDEX_RULES, FOR_INDEX); add_rule(V_KEY_RULES, FOR_KEY); add_rule(V_REPLACE_RULES, FOR_REPLACE); add_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE); add_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO); add_rule(V_RESUB_RULES, FOR_RESUB|FOR_TRIM); add_rule(V_SAVE_RULES, FOR_SAVE); add_rule(V_SMTP_RULES, FOR_COMPOSE); add_rule(V_SORT_RULES, FOR_SORT); add_rule(V_STARTUP_RULES, FOR_STARTUP); } int condition_contains_token(CONDITION_S *condition, char *token) { while(condition && condition->cndtype != Condition) condition = condition->next; return condition ? (!strcmp(COND(condition)->tname, token) ? 1 : condition_contains_token(condition->next, token)) : 0; } RULELIST * get_rulelist_from_code(int code, PRULELIST_S *list) { return list ? (list->varnum == code ? list->rlist : get_rulelist_from_code(code, list->next)) : (RULELIST *) NULL; } char * test_rule(RULELIST *rlist, int ctxt, ENVELOPE *env, int *n) { char *result; if(!rlist) return NULL; if (result = process_rule(rlist->prule, ctxt, env)) return result; else{ (*n)++; return test_rule(rlist->next, ctxt, env, n); } } RULE_S * get_rule (RULELIST *rule, int n) { return rule ? (n ? get_rule(rule->next, n-1) : rule->prule) : NULL; } /* get_result_rule: * Parameters: list: the list of rules to be passed to the function to check * rule_context: context of the rule * env : envelope used to check the rule, if needed. * * Returns: The value of the first rule that is satisfied in the list, or * NULL if not. This function should be called in the following * way (notice that memory is freed by caller). * * You should use this function to obtain the result of a rule. You can * also call directly "process_rule", but I advice to use this function if * there's no difference on which function to call. RULE_RESULT *rule; rule = (RULE_RESULT *) get_result_rule(V_SOME_RULE, context, envelope); if (rule){ assign the value of rule->result; if (rule->result) fs_give((void **)&rule->result); fs_give((void **)&rule); } */ RULE_RESULT * get_result_rule(int code, int rule_context, ENVELOPE *env) { char *rule_result; RULE_RESULT *rule = NULL; RULELIST *rlist; int n = 0; if(!(rule_context & FOR_RULE)) rule_context |= FOR_RULE; rlist = get_rulelist_from_code(code, ps_global->rule_list); if (rlist){ rule_result = test_rule(rlist, rule_context, env, &n); if (rule_result && *rule_result){ rule = (RULE_RESULT *) fs_get (sizeof(RULE_RESULT)); rule->result = rule_result; rule->number = n; } } return rule; } char *get_rule_result(int rule_context, char *newfolder, int code) { char *rule_result = NULL; ENVELOPE *news_envelope; RULE_RESULT *rule; if (IS_NEWS(ps_global->mail_stream)){ news_envelope = mail_newenvelope(); news_envelope->newsgroups = cpystr(newfolder); } else news_envelope = NULL; rule = get_result_rule(code, rule_context, news_envelope); if (news_envelope) mail_free_envelope (&news_envelope); if (rule){ rule_result = cpystr(rule->result); if (rule->result) fs_give((void **)&rule->result); fs_give((void **)&rule); } return rule_result; } /* process_rule: Parameters: prule, a processed rule, ready to be tested rule_context: context of the rule, and env: An envelope if needed. Returns : The value of the processed rule_data if the processing was successful and matches context and possibly the envelope, or NULL if there's no match */ char * process_rule (RULE_S *prule, int rule_context, ENVELOPE *env) { if(!prule) return NULL; if(!(rule_context & FOR_RULE)) rule_context |= FOR_RULE; return test_condition(prule->condition, rule_context, env) ? (prule->action->exec)(prule->action, rule_context, env) : NULL; } TOKEN_VALUE * copy_parsed_value(TOKEN_VALUE *value, int ctxt, ENVELOPE *env) { TOKEN_VALUE *tval = NULL; if(!value) return NULL; if(value->testxt){ tval = (TOKEN_VALUE *) alloc_mem(sizeof(TOKEN_VALUE)); tval->testxt = detoken_src(value->testxt, ctxt, env, NULL, NULL, NULL); tval->voidtxt = value->voidtxt; tval->codefcn = value->codefcn; } if(value->next) tval->next = copy_parsed_value(value->next, ctxt, env); return tval; } void free_parsed_value(TOKEN_VALUE **value) { TOKEN_VALUE *tval = NULL; if(!*value) return; if((*value)->testxt) fs_give((void **)&(*value)->testxt); if((*value)->next) free_parsed_value(&(*value)->next); fs_give((void **)value); } int test_condition_work(CONDITION_S *bc, CONDITION_S *ec, int rcntxt, ENVELOPE *env) { int rv,level; TOKEN_VALUE *group; CONDITION_S *cend; switch(bc->cndtype){ case Condition: group = copy_parsed_value(COND(bc)->value, rcntxt, env); rv = (*rel_rules_test[COND(bc)->ttype].execute)(bc, group, env, rcntxt); free_parsed_value(&group); if(bc == ec) return rv; if(bc->next == NULL) return rv; else switch(bc->next->cndtype){ case And: return rv ? test_condition_work(bc->next->next, ec, rcntxt, env) : 0; break; case Or : return rv ? 1 : test_condition_work(bc->next->next, ec, rcntxt, env); break; case ')': return rv; default : rv = 0; break; /* fail, we should not be here */ } break; case ParOpen: level = ((int *)bc->cndrule)[0]; for(cend = bc; cend->next && (cend->next->cndtype != ParClose || ((int *)cend->next->cndrule)[0] != level); cend = cend->next); rv = test_condition_work(bc->next, cend, rcntxt, env); cend = cend->next; /* here we are at ')' */ if(cend->next == NULL) return rv; else{ switch(cend->next->cndtype){ case And: return rv ? test_condition_work(cend->next->next, ec, rcntxt, env) : 0; break; case Or : return rv ? 1 : test_condition_work(cend->next->next, ec, rcntxt, env); break; default : rv = 0; break; /* fail, we should not be here */ } } break; default: rv = 0; break; /* fail, we should not be here */ } return rv; /* we never ever get here */ } int test_condition(CONDITION_S *condition, int rcntxt, ENVELOPE *env) { return test_condition_work(condition, NULL, rcntxt, env); } /* returns the name of the token it found or NULL if there is no token, the * real value of the token is obtained by calling the detoken_src function. */ char * get_name_token (char *condition) { char *p = NULL, *q, *s; if ((q = strchr(condition,'_')) && (s = strchr(q+1,'_'))){ char c = *++s; *s = '\0'; p = cpystr(q); *s = c; } return p; } /* This function tests if a string contained in the variable "group" is * in the "condition" */ int test_in (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, int context) { int rv = 0; char *test; TOKEN_VALUE *test_group = group; test = env && env->sparep && ((SPAREP_S *)env->sparep)->flag & USE_RAW_SP ? cpystr(((SPAREP_S *)env->sparep)->value) : detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL); if (test){ while (rv == 0 && test_group){ if(!*test || strstr(test_group->testxt, test)) rv++; else test_group = test_group->next; } fs_give((void **)&test); } return rv; } int test_ni (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, int context) { int rv = 0; char *test; TOKEN_VALUE *test_group = group; test = env && env->sparep && ((SPAREP_S *)env->sparep)->flag & USE_RAW_SP ? cpystr(((SPAREP_S *)env->sparep)->value) : detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL); if (test){ if(!test_group) rv++; while (rv == 0 && test_group){ if(!*test_group->testxt || strstr(test, test_group->testxt)) rv++; else test_group = test_group->next; } fs_give((void **)&test); } return rv; } int test_not_in (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, int context) { return !test_in(condition, group, env, context); } int test_not_ni (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, int context) { return !test_ni(condition, group, env, context); } int test_eq (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, int context) { int rv = 0; char *test; TOKEN_VALUE *test_group = group; test = env && env->sparep && ((SPAREP_S *)env->sparep)->flag & USE_RAW_SP ? cpystr(((SPAREP_S *)env->sparep)->value) : detoken_src(COND(condition)->tname, context, env, NULL, NULL, NULL); if (test){ while (rv == 0 && test_group){ if((!*test && !*test_group->testxt) || !strcmp(test_group->testxt, test)) rv++; else test_group = test_group->next; } fs_give((void **)&test); } return rv; } int test_not_eq (CONDITION_S *condition, TOKEN_VALUE *group, ENVELOPE *env, int context) { return !test_eq(condition, group, env, context); } char * do_trim (char *test, TOKEN_VALUE *tval) { char *begin_text; int offset = 0; if (!tval) return test; while(begin_text = strstr(test+offset,tval->testxt)){ memmove(begin_text, begin_text+strlen(tval->testxt), strlen(begin_text) - strlen(tval->testxt)); offset = begin_text - test; } return do_trim(test, tval->next); } char * trim (RULEACTION_S *action, int context, ENVELOPE *env) { char *begin_text, *test; RULEACTION_S *taction = action; int offset; if (taction->context & context){ if (test = detoken_src(taction->token, context, env, NULL, NULL, NULL)) test = do_trim(test, taction->value); return test; } return NULL; } char * do_rextrim (char *test, TOKEN_VALUE *tval) { char *begin_text, *trim_text; int offset = 0; if (!tval) return test; trim_text = expand(test, tval->voidtxt); while(trim_text && (begin_text = strstr(test+offset,trim_text))){ strcpy(begin_text, begin_text+strlen(trim_text)); offset = begin_text - test; } return do_rextrim(test, tval->next); } char * rextrim (RULEACTION_S *action, int context, ENVELOPE *env) { char *test = NULL; RULEACTION_S *taction = action; if (taction->context & context && (test = detoken_src(taction->token, context, env, NULL, NULL, NULL))) test = do_rextrim(test, taction->value); return test; } char * raw_value (RULEACTION_S *action, int context, ENVELOPE *env) { return (action->context & context) ? cpystr(action->value->testxt) : NULL; } char * extended_value (RULEACTION_S *action, int ctxt, ENVELOPE *env) { return (action->context & ctxt) ? detoken_src(action->value->testxt, ctxt, env, NULL, NULL, NULL) : NULL; } /* advances given_string until it finds given_char, memory freed by caller */ char * advance_to_char(char *given_string, char given_char, int flag, int *error) { char *b, *s, c; int i, err = 0, quoted ; if (error) *error = 0; if (!given_string || !*given_string) return NULL; b = s = cpystr(given_string); for(i = 0, quoted = 0, c = *s; c ; c = *++s){ if(c == '\\'){ quoted++; continue; } if(quoted){ quoted = 0; if (c == given_char){ err += flag & STRICTLY ? 0 : 1; err++; break; } b[i++] = '\\'; } if(c == given_char){ err += flag & STRICTLY ? 0 : 1; break; } b[i++] = c; } b[i] = '\0'; if (b && (strlen(b) == strlen(given_string)) && (flag & STRICTLY)){ fs_give((void **)&b); return NULL; /* character not found */ } if(b && !*b){ fs_give((void **)&b); err = -1; } if (error) *error = err; return b; } /* Regular Expressions Support */ char * expand (char *string, void *pattern) { char c, *ret_string = NULL; regmatch_t pmatch; if((regex_t *)pattern == NULL) return NULL; if(regexec((regex_t *)pattern, string , 1, &pmatch, 0) == 0 && pmatch.rm_so < pmatch.rm_eo){ c = string[pmatch.rm_eo]; string[pmatch.rm_eo] = '\0'; ret_string = cpystr(string+pmatch.rm_so); string[pmatch.rm_eo] = c; } return ret_string; } char * exec_fcn (RULEACTION_S *action, int ctxt, ENVELOPE *env) { STORE_S *output_so; gf_io_t gc, pc; char *status, *rv, *cmd, *test; if(!(action->context & ctxt)) return NULL; if((test = detoken_src(action->token, ctxt, env, NULL, NULL, NULL)) != NULL) gf_set_readc(&gc, test, (unsigned long)strlen(test), CharStar, 0); if((output_so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL) gf_set_so_writec(&pc, output_so); cmd = (char *)fs_get((strlen(action->value->testxt) + strlen("_TMPFILE_") + 2)*sizeof(char)); sprintf(cmd,"%s _TMPFILE_", action->value->testxt); status = (*ps_global->tools.exec_rule)(cmd, gc, pc); so_seek(output_so, 0L, 0); rv = cpystr(output_so->dp); gf_clear_so_writec(output_so); so_give(&output_so); if(test) fs_give((void **)&test); return status ? NULL : rv; } ENVELOPE * rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear) { ENVELOPE *env; if (idata->no_fetch){ if(we_clear) *we_clear = 1; env = mail_newenvelope(); env->from = copyaddrlist(idata->from); env->to = copyaddrlist(idata->to); env->cc = copyaddrlist(idata->cc); env->sender = copyaddrlist(idata->sender); env->subject = cpystr(idata->subject); env->date = cpystr((unsigned char *) idata->date); env->newsgroups = cpystr(idata->newsgroups); return env; } if(we_clear) *we_clear = 0; env = pine_mail_fetchenvelope(idata->stream, idata->rawno); return env; }