diff -rc alpine-2.21/alpine/adrbkcmd.c alpine-2.21.rules/alpine/adrbkcmd.c *** alpine-2.21/alpine/adrbkcmd.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/adrbkcmd.c Sun Feb 5 16:15:21 2017 *************** *** 4128,4133 **** --- 4128,4135 ---- * won't do anything, but will cause compose_mail to think there's * already a role so that it won't try to confirm the default. */ + if (ps_global->role) + fs_give((void **)&ps_global->role); if(role) role = copy_action(role); else{ *************** *** 4135,4140 **** --- 4137,4143 ---- memset((void *)role, 0, sizeof(*role)); role->nick = cpystr("Default Role"); } + ps_global->role = cpystr(role->nick); } compose_mail(addr, fcc, role, NULL, NULL); diff -rc alpine-2.21/alpine/alpine.c alpine-2.21.rules/alpine/alpine.c *** alpine-2.21/alpine/alpine.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/alpine.c Sun Feb 5 16:15:21 2017 *************** *** 485,490 **** --- 485,491 ---- /* Set up optional for user-defined display filtering */ pine_state->tools.display_filter = dfilter; pine_state->tools.display_filter_trigger = dfilter_trigger; + pine_state->tools.exec_rule = exec_function_rule; #ifdef _WINDOWS if(ps_global->install_flag){ *************** *** 3162,3167 **** --- 3163,3171 ---- extern KBESC_T *kbesc; dprint((2, "goodnight_gracey:\n")); + strncpy(pine_state->cur_folder, pine_state->inbox_name, + sizeof(pine_state->cur_folder)); + pine_state->cur_folder[sizeof(pine_state->cur_folder) - 1] = '\0'; /* We want to do this here before we close up the streams */ trim_remote_adrbks(); diff -rc alpine-2.21/alpine/confscroll.c alpine-2.21.rules/alpine/confscroll.c *** alpine-2.21/alpine/confscroll.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/confscroll.c Sun Feb 5 16:15:21 2017 *************** *** 51,56 **** --- 51,57 ---- #include "../pith/tempfile.h" #include "../pith/pattern.h" #include "../pith/charconv/utf8.h" + #include "../pith/rules.h" #define CONFIG_SCREEN_HELP_TITLE _("HELP FOR SETUP CONFIGURATION") *************** *** 2436,2441 **** --- 2437,2445 ---- * Now go and set the current_val based on user_val changes * above. Turn off command line settings... */ + set_current_val((*cl)->var, + (strcmp((*cl)->var->name,"key-definition-rules") ? TRUE : FALSE), + FALSE); set_current_val((*cl)->var, TRUE, FALSE); fix_side_effects(ps, (*cl)->var, 0); *************** *** 5200,5205 **** --- 5204,5233 ---- var == &ps->vars[V_ABOOK_FORMATS]){ addrbook_reset(); } + else if(var == &ps->vars[V_INDEX_RULES]){ + if(ps_global->rule_list) + free_parsed_rule_list(&ps_global->rule_list); + create_rule_list(ps->vars); + reset_index_format(); + clear_index_cache(ps->mail_stream, 0); + } + else if(var == &ps->vars[V_COMPOSE_RULES] || + var == &ps->vars[V_FORWARD_RULES] || + var == &ps->vars[V_KEY_RULES] || + var == &ps->vars[V_REPLACE_RULES] || + var == &ps->vars[V_REPLY_INDENT_RULES] || + var == &ps->vars[V_REPLY_LEADIN_RULES] || + var == &ps->vars[V_RESUB_RULES] || + var == &ps->vars[V_SAVE_RULES] || + var == &ps->vars[V_SMTP_RULES] || + var == &ps->vars[V_SORT_RULES] || + var == &ps->vars[V_STARTUP_RULES] || + var == &ps->vars[V_THREAD_DISP_STYLE_RULES] || + var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){ + if(ps_global->rule_list) + free_parsed_rule_list(&ps_global->rule_list); + create_rule_list(ps->vars); + } else if(var == &ps->vars[V_INDEX_FORMAT]){ reset_index_format(); clear_index_cache(ps->mail_stream, 0); diff -rc alpine-2.21/alpine/dispfilt.c alpine-2.21.rules/alpine/dispfilt.c *** alpine-2.21/alpine/dispfilt.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/dispfilt.c Sun Feb 5 16:15:21 2017 *************** *** 461,463 **** --- 461,523 ---- return(passed); } + + char * + exec_function_rule(char *rawcmd, gf_io_t input_gc, gf_io_t output_pc) + { + char *status = NULL, *cmd, *tmpfile = NULL; + + if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,NULL,NULL,NULL,NULL,NULL)) != NULL){ + suspend_busy_cue(); + ps_global->mangled_screen = 1; + if(tmpfile){ + PIPE_S *filter_pipe; + FILE *fp; + gf_io_t gc, pc; + STORE_S *tmpf_so; + + /* write the tmp file */ + if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){ + /* copy input to tmp file */ + gf_set_so_writec(&pc, tmpf_so); + gf_filter_init(); + status = gf_pipe(input_gc, pc); + gf_clear_so_writec(tmpf_so); + if(so_give(&tmpf_so) != 0 && status == NULL) + status = error_description(errno); + + /* prepare the terminal in case the filter uses it */ + if(status == NULL){ + if((filter_pipe = open_system_pipe(cmd, NULL, NULL, + PIPE_USER|PIPE_PROT|PIPE_NOSHELL|PIPE_SILENT, + 0, pipe_callback, NULL)) != NULL){ + if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){ + /* pull result out of tmp file */ + if((fp = our_fopen(tmpfile, "rb")) != NULL){ + gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE); + gf_filter_init(); + status = gf_pipe(gc, output_pc); + fclose(fp); + } + else + status = "Can't read result of EXEC command"; + } + else + status = "EXEC command command returned error."; + } + else + status = "Can't open pipe for EXEC command"; + } + + our_unlink(tmpfile); + } + else + status = "Can't open EXEC command tmp file"; + } + + resume_busy_cue(0); + fs_give((void **)&cmd); + } + + return(status); + } diff -rc alpine-2.21/alpine/dispfilt.h alpine-2.21.rules/alpine/dispfilt.h *** alpine-2.21/alpine/dispfilt.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/dispfilt.h Sun Feb 5 16:15:21 2017 *************** *** 25,31 **** char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *); char *filter_session_key(void); char *filter_data_file(int); ! #endif /* PINE_DISPFILT_INCLUDED */ --- 25,31 ---- char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *); char *filter_session_key(void); char *filter_data_file(int); ! char *exec_function_rule(char *, gf_io_t, gf_io_t); #endif /* PINE_DISPFILT_INCLUDED */ diff -rc alpine-2.21/alpine/folder.c alpine-2.21.rules/alpine/folder.c *** alpine-2.21/alpine/folder.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/folder.c Sun Feb 5 16:15:21 2017 *************** *** 248,254 **** dprint((1, "=== folder_screen called ====\n")); mailcap_free(); /* free resources we won't be using for a while */ ps->next_screen = SCREEN_FUN_NULL; ! /* Initialize folder state and dispatches */ memset(&fs, 0, sizeof(FSTATE_S)); fs.context = cntxt; --- 248,254 ---- dprint((1, "=== folder_screen called ====\n")); mailcap_free(); /* free resources we won't be using for a while */ ps->next_screen = SCREEN_FUN_NULL; ! strcpy(ps->screen_name, "folder"); /* Initialize folder state and dispatches */ memset(&fs, 0, sizeof(FSTATE_S)); fs.context = cntxt; *************** *** 345,350 **** --- 345,351 ---- pine_mail_close(*fs.cache_streamp); ps->prev_screen = folder_screen; + strcpy(ps->screen_name, "unknown"); } diff -rc alpine-2.21/alpine/mailcmd.c alpine-2.21.rules/alpine/mailcmd.c *** alpine-2.21/alpine/mailcmd.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/mailcmd.c Sun Feb 5 16:15:21 2017 *************** *** 73,78 **** --- 73,79 ---- #include "../pith/tempfile.h" #include "../pith/search.h" #include "../pith/margin.h" + #include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" #endif *************** *** 2657,2662 **** --- 2658,2666 ---- role->nick = cpystr("Default Role"); } + if(state->role) + fs_give((void **)&state->role); + state->role = cpystr(role->nick); /* remember the role */ state->redrawer = NULL; switch(action){ case 'c': *************** *** 2707,2718 **** char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section, SaveDel *dela, SavePreserveOrder *prea) { ! int rc, ku = -1, n, flags, last_rc = 0, saveable_count = 0, done = 0; int delindex, preindex, r; char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN]; char *buf = tmp_20k_buf; char shortbuf[200]; ! char *folder; HelpType help; SaveDel del = DontAsk; SavePreserveOrder pre = DontAskPreserve; --- 2711,2722 ---- char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section, SaveDel *dela, SavePreserveOrder *prea) { ! int rc, ku = -1, n = 0, flags, last_rc = 0, saveable_count = 0, done = 0; int delindex, preindex, r; char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN]; char *buf = tmp_20k_buf; char shortbuf[200]; ! char *folder, folder2[MAXPATH]; HelpType help; SaveDel del = DontAsk; SavePreserveOrder pre = DontAskPreserve; *************** *** 2720,2725 **** --- 2724,2730 ---- static HISTORY_S *history = NULL; CONTEXT_S *tc; ESCKEY_S ekey[10]; + RULE_RESULT *rule; if(!cntxt) alpine_panic("no context ptr in save_prompt"); *************** *** 2729,2734 **** --- 2734,2748 ---- if(!(folder = save_get_default(state, env, rawmsgno, section, cntxt))) return(0); /* message expunged! */ + if (rule = get_result_rule(V_SAVE_RULES, FOR_SAVE, env)){ + strncpy(folder2,rule->result,sizeof(folder2)-1); + folder2[sizeof(folder2)-1] = '\0'; + folder = folder2; + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + /* how many context's can be saved to... */ for(tc = state->context_list; tc; tc = tc->next) if(!NEWS_TEST(tc)) diff -rc alpine-2.21/alpine/mailindx.c alpine-2.21.rules/alpine/mailindx.c *** alpine-2.21/alpine/mailindx.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/mailindx.c Sun Feb 5 16:15:21 2017 *************** *** 229,234 **** --- 229,236 ---- state->prev_screen = mail_index_screen; state->next_screen = SCREEN_FUN_NULL; + setup_threading_display_style(); + if(THRD_AUTO_VIEW() && sp_viewing_a_thread(state->mail_stream) && state->view_skipped_index *************** *** 240,249 **** --- 242,255 ---- adjust_cur_to_visible(state->mail_stream, state->msgmap); + strcpy(state->screen_name,"index"); + if(THRD_INDX()) thread_index_screen(state); else index_index_screen(state); + + strcpy(state->screen_name,"unknown"); } diff -rc alpine-2.21/alpine/mailview.c alpine-2.21.rules/alpine/mailview.c *** alpine-2.21/alpine/mailview.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/mailview.c Sun Feb 5 16:15:21 2017 *************** *** 243,248 **** --- 243,250 ---- ps->prev_screen = mail_view_screen; ps->force_prefer_plain = ps->force_no_prefer_plain = 0; + strcpy(ps->screen_name, "text"); + if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){ q_status_message(SM_ORDER | SM_DING, 0, 3, _("Screen too small to view message")); *************** *** 479,484 **** --- 481,488 ---- } while(ps->next_screen == SCREEN_FUN_NULL); + strcpy(ps->screen_name, "unknown"); + if (prefix && *prefix) fs_give((void **)&prefix); if(we_cancel) diff -rc alpine-2.21/alpine/osdep/termin.gen.c alpine-2.21.rules/alpine/osdep/termin.gen.c *** alpine-2.21/alpine/osdep/termin.gen.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/osdep/termin.gen.c Sun Feb 5 16:15:21 2017 *************** *** 33,38 **** --- 33,40 ---- #include "../../pith/newmail.h" #include "../../pith/conf.h" #include "../../pith/busy.h" + #include "../../pith/list.h" + #include "../../pith/rules.h" #include "../../pico/estruct.h" #include "../../pico/pico.h" *************** *** 72,78 **** * Generic tty input routines */ ! /*---------------------------------------------------------------------- Read a character from keyboard with timeout Input: none --- 74,81 ---- * Generic tty input routines */ ! void process_init_cmds(struct pine *, char **); ! void queue_init_errors(struct pine *); /*---------------------------------------------------------------------- Read a character from keyboard with timeout Input: none *************** *** 114,119 **** --- 117,157 ---- *utf8str = NULL; ucs = read_char(tm); + if(!ps_global->initial_cmds){ + RULE_RESULT *rule; + char **list = NULL, *error = NULL; + int commas = 0, k; /* From args.c */ + + ps_global->pressed_key = cpystr(pretty_command(ucs)); + rule = (RULE_RESULT *)get_result_rule(V_KEY_RULES, FOR_KEY, NULL); + if(ps_global->pressed_key) + fs_give((void **)&ps_global->pressed_key); + if (rule){ + for(k = 0; rule->result[k]; k++) + if(rule->result[k] == ',') commas++; + list = parse_list(rule->result, commas+1, 0, &error); + if(error) + sprintf(tmp_20k_buf, "Error in parsing command list: %s, %s", + rule->result, error); + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + if(error){ + q_status_message(SM_ORDER | SM_DING, 0, 2, tmp_20k_buf); + return (NO_OP_COMMAND); + } + process_init_cmds(ps_global, list); + if(ps_global->init_errs){ + queue_init_errors(ps_global); + return (NO_OP_COMMAND); + } + ucs = read_char(tm); + ps_global->in_init_seq = 1; /* no output please */ + for(k = 0; k < commas; k++) + if(list[k]) fs_give((void **)&list[k]); + if (list) fs_give((void **)list); + } + } if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE) zero_new_mail_count(); *************** *** 1158,1163 **** --- 1196,1202 ---- if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){ fs_give((void **) &ps_global->free_initial_cmds); ps_global->initial_cmds = NULL; + firsttime = (char) 1; } return(ret); diff -rc alpine-2.21/alpine/reply.c alpine-2.21.rules/alpine/reply.c *** alpine-2.21/alpine/reply.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/reply.c Sun Feb 5 16:15:21 2017 *************** *** 62,68 **** #include "../pith/tempfile.h" #include "../pith/busy.h" #include "../pith/ablookup.h" ! /* * Internal Prototypes --- 62,69 ---- #include "../pith/tempfile.h" #include "../pith/busy.h" #include "../pith/ablookup.h" ! #include "../pith/copyaddr.h" ! #include "../pith/rules.h" /* * Internal Prototypes *************** *** 109,119 **** long msgno, j, totalm, rflags, *seq = NULL; int i, include_text = 0, times = -1, warned = 0, rv = 0, flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0; ! int rolemsg = 0, copytomsg = 0; gf_io_t pc; PAT_STATE dummy; REDRAFT_POS_S *redraft_pos = NULL; ACTION_S *role = NULL, *nrole; #if defined(DOS) && !defined(_WINDOWS) char *reserve; #endif --- 110,121 ---- long msgno, j, totalm, rflags, *seq = NULL; int i, include_text = 0, times = -1, warned = 0, rv = 0, flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0; ! int rolemsg = 0, copytomsg = 0, do_role_early = 0; gf_io_t pc; PAT_STATE dummy; REDRAFT_POS_S *redraft_pos = NULL; ACTION_S *role = NULL, *nrole; + RULE_RESULT *rule; #if defined(DOS) && !defined(_WINDOWS) char *reserve; #endif *************** *** 139,144 **** --- 141,209 ---- && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) reply_raw_body = 1; + /* Setup possible role */ + if(role_arg) + role = copy_action(role_arg); + + if(!role && F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state)){ + for(msgno = mn_first_cur(pine_state->msgmap); + msgno > 0L; msgno = mn_next_cur(pine_state->msgmap)){ + + env = pine_mail_fetchstructure(pine_state->mail_stream, + mn_m2raw(pine_state->msgmap, msgno), + NULL); + if(!env) { + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't reply to it."), + long2string(msgno)); + goto done_early; + } + + if(rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env)){ + RULELIST *list = get_rulelist_from_code(V_REPLY_INDENT_RULES, + ps_global->rule_list); + RULE_S *prule = get_rule(list, rule->number); + if(condition_contains_token(prule->condition, ROLE_TOKEN)) + do_role_early++; + if(rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + } + + if(do_role_early){ + rflags = ROLE_REPLY; + if(nonempty_patterns(rflags, &dummy)){ + /* setup default role */ + nrole = NULL; + j = mn_first_cur(pine_state->msgmap); + do { + role = nrole; + nrole = set_role_from_msg(pine_state, rflags, + mn_m2raw(pine_state->msgmap, j), + NULL); + } while(nrole && (!role || nrole == role) + && (j=mn_next_cur(pine_state->msgmap)) > 0L); + + if(!role || nrole == role) + role = nrole; + else + role = NULL; + + if(confirm_role(rflags, &role)) + role = combine_inherited_role(role); + else{ /* cancel reply */ + role = NULL; + cmd_cancelled("Reply"); + goto done_early; + } + } + } + + if (role) + ps_global->role = cpystr(role->nick); /* remember the role */ + /* * We may have to loop through first to figure out what default * reply-indent-string to offer... *************** *** 287,294 **** outgoing->subject = cpystr("Re: several messages"); } } ! else ! outgoing->subject = reply_subject(env->subject, NULL, 0); } /* fill reply header */ --- 352,369 ---- outgoing->subject = cpystr("Re: several messages"); } } ! else{ ! RULE_RESULT *rule; ! rule = get_result_rule(V_RESUB_RULES,FOR_RESUB|FOR_TRIM , env); ! if (rule){ ! outgoing->subject = reply_subject(rule->result, NULL, 0); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! outgoing->subject = reply_subject(env->subject, NULL, 0); ! } } /* fill reply header */ *************** *** 307,319 **** if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */ goto done_early; ! /* Setup possible role */ ! if (ps_global->reply.role_chosen) ! role = ps_global->reply.role_chosen; ! else if(role_arg) ! role = copy_action(role_arg); ! ! if(!role){ rflags = ROLE_REPLY; if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){ /* setup default role */ --- 382,388 ---- if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */ goto done_early; ! if(!do_role_early){ rflags = ROLE_REPLY; if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){ /* setup default role */ *************** *** 724,729 **** --- 793,801 ---- if(prefix) fs_give((void **)&prefix); + if (ps_global->role) + fs_give((void **)&ps_global->role); + if(fcc) fs_give((void **) &fcc); *************** *** 1598,1606 **** } } ! if(role) q_status_message1(SM_ORDER, 3, 4, _("Forwarding using role \"%s\""), role->nick); if(role && role->template){ char *filtered; --- 1670,1683 ---- } } ! if (ps_global->role) ! fs_give((void **)&ps_global->role); ! ! if(role){ q_status_message1(SM_ORDER, 3, 4, _("Forwarding using role \"%s\""), role->nick); + ps_global->role = cpystr(role->nick); + } if(role && role->template){ char *filtered; *************** *** 1832,1837 **** --- 1909,1915 ---- #if defined(DOS) && !defined(_WINDOWS) free((void *)reserve); #endif + outgoing->sparep = env && env->from ? copyaddr(env->from) : NULL; pine_send(outgoing, &body, "FORWARD MESSAGE", role, NULL, &reply, redraft_pos, NULL, NULL, 0); diff -rc alpine-2.21/alpine/roleconf.c alpine-2.21.rules/alpine/roleconf.c *** alpine-2.21/alpine/roleconf.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/roleconf.c Sun Feb 5 16:15:21 2017 *************** *** 7706,7711 **** --- 7706,7716 ---- if(apval) *apval = (role && role->nick) ? cpystr(role->nick) : NULL; + if (ps_global->role) + fs_give((void **)&ps_global->role); + if (role && role->nick) + ps_global->role = cpystr(role->nick); + if((*cl)->value) fs_give((void **)&((*cl)->value)); diff -rc alpine-2.21/alpine/send.c alpine-2.21.rules/alpine/send.c *** alpine-2.21/alpine/send.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/alpine/send.c Sun Feb 5 16:15:21 2017 *************** *** 63,69 **** #include "../pith/mimetype.h" #include "../pith/send.h" #include "../pith/smime.h" ! typedef struct body_particulars { unsigned short type, encoding, had_csp; --- 63,69 ---- #include "../pith/mimetype.h" #include "../pith/send.h" #include "../pith/smime.h" ! #include "../pith/rules.h" typedef struct body_particulars { unsigned short type, encoding, had_csp; *************** *** 240,245 **** --- 240,250 ---- role->nick = cpystr("Default Role"); } + if (ps_global->role) + fs_give((void **)&ps_global->role); + + ps_global->role = cpystr(role->nick); + pine_state->redrawer = NULL; compose_mail(NULL, NULL, role, NULL, NULL); free_action(&role); *************** *** 449,456 **** ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; ! if(role) role = combine_inherited_role(role); } break; --- 454,465 ---- ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; ! if (ps_global->role) ! fs_give((void **)&ps_global->role); ! if(role){ role = combine_inherited_role(role); + ps_global->role = cpystr(role->nick); + } } break; *************** *** 645,653 **** } } ! if(role) q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), role->nick); /* * The type of storage object allocated below is vitally --- 654,667 ---- } } ! if (ps_global->role) ! fs_give((void **)&ps_global->role); ! ! if(role){ q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), role->nick); + ps_global->role = cpystr(role->nick); + } /* * The type of storage object allocated below is vitally *************** *** 2476,2481 **** --- 2490,2515 ---- removing_trailing_white_space(pf->textbuf); (void)removing_double_quotes(pf->textbuf); build_address(pf->textbuf, &addr, NULL, NULL, NULL); + if (!strncmp(pf->name,"Lcc",3) && addr && *addr){ + RULE_RESULT *rule; + + outgoing->date = (unsigned char *) cpystr(addr); + ps_global->procid = cpystr("fwd-lcc"); + rule = get_result_rule(V_FORWARD_RULES, + FOR_COMPOSE|FOR_TRIM, outgoing); + if (rule){ + addr = cpystr(rule->result); + removing_trailing_white_space(addr); + (void)removing_extra_stuff(addr); + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + fs_give((void **)&ps_global->procid); + if (outgoing->date) + fs_give((void **)&outgoing->date); + } + rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain); fs_give((void **)&addr); diff -rc alpine-2.21/pith/Makefile.am alpine-2.21.rules/pith/Makefile.am *** alpine-2.21/pith/Makefile.am Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/Makefile.am Sun Feb 5 16:15:21 2017 *************** *** 26,32 **** filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c --- 26,32 ---- filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c diff -rc alpine-2.21/pith/Makefile.in alpine-2.21.rules/pith/Makefile.in *** alpine-2.21/pith/Makefile.in Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/Makefile.in Sun Feb 5 16:15:21 2017 *************** *** 147,153 **** margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ ! remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \ save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \ send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ --- 147,153 ---- margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \ msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \ pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \ ! remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) rules.$(OBJEXT) \ save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \ send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \ *************** *** 442,448 **** filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c --- 442,448 ---- filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \ ! readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c *************** *** 574,579 **** --- 574,580 ---- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff -rc alpine-2.21/pith/adrbklib.c alpine-2.21.rules/pith/adrbklib.c *** alpine-2.21/pith/adrbklib.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/adrbklib.c Sun Feb 5 16:15:21 2017 *************** *** 5136,5143 **** if(as.cur >= as.how_many_personals) pab->type |= GLOBAL; ! pab->access = adrbk_access(pab); ! /* global address books are forced readonly */ if(pab->type & GLOBAL && pab->access != NoAccess) pab->access = ReadOnly; --- 5136,5149 ---- if(as.cur >= as.how_many_personals) pab->type |= GLOBAL; ! if(ps_global->mail_stream && ! ps_global->mail_stream->lock && (pab->type & REMOTE_VIA_IMAP)){ ! as.initialized = 0; ! pab->access = NoAccess; ! } ! else{ ! pab->access = adrbk_access(pab); ! } /* global address books are forced readonly */ if(pab->type & GLOBAL && pab->access != NoAccess) pab->access = ReadOnly; diff -rc alpine-2.21/pith/conf.c alpine-2.21.rules/pith/conf.c *** alpine-2.21/pith/conf.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/conf.c Sun Feb 5 16:15:21 2017 *************** *** 29,34 **** --- 29,35 ---- #include "../pith/remote.h" #include "../pith/keyword.h" #include "../pith/mailview.h" + #include "../pith/rules.h" #include "../pith/list.h" #include "../pith/status.h" #include "../pith/ldap.h" *************** *** 222,227 **** --- 223,258 ---- CONF_TXT_T cf_text_editor[] = "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature."; + CONF_TXT_T cf_text_compose_rules[] = "Allows a user to set rules when composing messages."; + + CONF_TXT_T cf_text_forward_rules[] = "Allows a user to set rules when forwarding messages."; + + CONF_TXT_T cf_text_reply_rules[] = "Allows a user to set rules when replying messages."; + + CONF_TXT_T cf_text_index_rules[] = "Allows a user to supersede global index format variable in designated folders."; + + CONF_TXT_T cf_text_key_def_rules[] = "Allows a user to override keystrokes in certain screens."; + + CONF_TXT_T cf_text_replace_rules[] = "Allows a user to change the form a specify field in the index-format is \n# displayed."; + + CONF_TXT_T cf_text_reply_indent_rules[] = "Allows a user to change the form a specify a reply-indent-string\n# based of rules."; + + CONF_TXT_T cf_text_reply_leadin_rules[] = "Allows a user to replace the reply-leadin message based on different parameters."; + + CONF_TXT_T cf_text_reply_subject_rules[] = "Allows a user to replace the subject of a message in a customs based way"; + + CONF_TXT_T cf_text_thread_displaystyle_rule[] = "Allows a user to specify the threading style of specific folders"; + + CONF_TXT_T cf_text_thread_indexstyle_rule[] = "Allows a user to specify the threading index style of specific folders"; + + CONF_TXT_T cf_text_save_rules[] = "Allows a user to specify a save folder message for specific senders or folders."; + + CONF_TXT_T cf_text_smtp_rules[] = "Allows a user to specify a smtp server to be used when sending e-mail,\n# according to the rules specified here."; + + CONF_TXT_T cf_text_sort_rules[] = "Allows a user to specify the sort default order of a specific folder."; + + CONF_TXT_T cf_text_startup_rules[] = "Allows a user to specify the position of a highlighted message when opening a \n# folder."; + CONF_TXT_T cf_text_speller[] = "Specifies the program invoked by ^T in the Composer."; #ifdef _WINDOWS *************** *** 550,555 **** --- 581,614 ---- NULL, cf_text_thread_exp_char}, {"threading-lastreply-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, "Threading Last Reply Character", cf_text_thread_lastreply_char}, + {"threading-display-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Threading Display Style Rule", cf_text_thread_displaystyle_rule}, + {"threading-index-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Threading Index Style Rule", cf_text_thread_indexstyle_rule}, + {"compose-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Compose Rules", cf_text_compose_rules}, + {"forward-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Forward Rules", cf_text_forward_rules}, + {"index-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Index Rules", cf_text_index_rules}, + {"key-definition-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Key Definition Rules", cf_text_key_def_rules}, + {"replace-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Replace Rules", cf_text_replace_rules}, + {"reply-indent-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Reply Indent Rules", cf_text_reply_indent_rules}, + {"reply-leadin-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Reply Leadin Rules", cf_text_reply_leadin_rules}, + {"reply-subject-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, + "Reply Subject Rules", cf_text_reply_subject_rules}, + {"save-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Save Rules", cf_text_save_rules}, + {"smtp-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Smtp Rules", cf_text_smtp_rules}, + {"sort-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Sort Rules", cf_text_sort_rules}, + {"startup-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, + "Startup Rules", cf_text_startup_rules}, #ifndef _WINDOWS {"display-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL, cf_text_disp_char_set}, *************** *** 2635,2640 **** --- 2694,2700 ---- if(cmds_f) (*cmds_f)(ps, VAR_INIT_CMD_LIST); + (void)create_rule_list(ps_global->vars); #ifdef _WINDOWS mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global)); #endif /* _WINDOWS */ *************** *** 3081,3086 **** --- 3141,3148 ---- F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD, 0}, {"auto-move-read-msgs", "Auto Move Read Messages", F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC, 0}, + {"auto-move-read-msgs-using-rules", "Auto Move Read Messages Using Rules", + F_AUTO_READ_MSGS_RULES, h_config_auto_read_msgs_rules, PREF_MISC, 0}, {"auto-unselect-after-apply", NULL, F_AUTO_UNSELECT, h_config_auto_unselect, PREF_MISC, 0}, {"auto-unzoom-after-apply", NULL, *************** *** 7699,7704 **** --- 7761,7794 ---- return(h_config_ab_sort_rule); case V_FLD_SORT_RULE : return(h_config_fld_sort_rule); + case V_THREAD_DISP_STYLE_RULES: + return(h_config_thread_display_style_rule); + case V_THREAD_INDEX_STYLE_RULES: + return(h_config_thread_index_style_rule); + case V_COMPOSE_RULES: + return(h_config_compose_rules); + case V_FORWARD_RULES: + return(h_config_forward_rules); + case V_INDEX_RULES: + return(h_config_index_rules); + case V_KEY_RULES: + return(h_config_key_macro_rules); + case V_REPLACE_RULES: + return(h_config_replace_rules); + case V_REPLY_INDENT_RULES: + return(h_config_reply_indent_rules); + case V_REPLY_LEADIN_RULES: + return(h_config_reply_leadin_rules); + case V_RESUB_RULES: + return(h_config_resub_rules); + case V_SAVE_RULES: + return(h_config_save_rules); + case V_SMTP_RULES: + return(h_config_smtp_rules); + case V_SORT_RULES: + return(h_config_sort_rules); + case V_STARTUP_RULES: + return(h_config_startup_rules); case V_POST_CHAR_SET : return(h_config_post_char_set); case V_UNK_CHAR_SET : diff -rc alpine-2.21/pith/conf.h alpine-2.21.rules/pith/conf.h *** alpine-2.21/pith/conf.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/conf.h Sun Feb 5 16:15:21 2017 *************** *** 148,153 **** --- 148,193 ---- #define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p #define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p #define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p + #define VAR_COMPOSE_RULES vars[V_COMPOSE_RULES].current_val.l + #define GLO_COMPOSE_RULES vars[V_COMPOSE_RULES].global_val.l + #define USR_COMPOSE_RULES vars[V_COMPOSE_RULES].user_val.l + #define VAR_FORWARD_RULES vars[V_FORWARD_RULES].current_val.l + #define GLO_FORWARD_RULES vars[V_FORWARD_RULES].global_val.l + #define USR_FORWARD_RULES vars[V_FORWARD_RULES].user_val.l + #define VAR_INDEX_RULES vars[V_INDEX_RULES].current_val.l + #define GLO_INDEX_RULES vars[V_INDEX_RULES].global_val.l + #define USR_INDEX_RULES vars[V_INDEX_RULES].user_val.l + #define VAR_KEY_RULES vars[V_KEY_RULES].current_val.l + #define GLO_KEY_RULES vars[V_KEY_RULES].global_val.l + #define USR_KEY_RULES vars[V_KEY_RULES].user_val.l + #define VAR_REPLACE_RULES vars[V_REPLACE_RULES].current_val.l + #define GLO_REPLACE_RULES vars[V_REPLACE_RULES].global_val.l + #define USR_REPLACE_RULES vars[V_REPLACE_RULES].user_val.l + #define VAR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].current_val.l + #define GLO_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].global_val.l + #define USR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].user_val.l + #define VAR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].current_val.l + #define GLO_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].global_val.l + #define USR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].user_val.l + #define VAR_RESUB_RULES vars[V_RESUB_RULES].current_val.l + #define GLO_RESUB_RULES vars[V_RESUB_RULES].global_val.l + #define USR_RESUB_RULES vars[V_RESUB_RULES].user_val.l + #define VAR_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].current_val.l + #define GLO_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].global_val.l + #define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l + #define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l + #define VAR_SAVE_RULES vars[V_SAVE_RULES].current_val.l + #define GLO_SAVE_RULES vars[V_SAVE_RULES].global_val.l + #define USR_SAVE_RULES vars[V_SAVE_RULES].user_val.l + #define VAR_SMTP_RULES vars[V_SMTP_RULES].current_val.l + #define GLO_SMTP_RULES vars[V_SMTP_RULES].global_val.l + #define USR_SMTP_RULES vars[V_SMTP_RULES].user_val.l + #define VAR_SORT_RULES vars[V_SORT_RULES].current_val.l + #define GLO_SORT_RULES vars[V_SORT_RULES].global_val.l + #define USR_SORT_RULES vars[V_SORT_RULES].user_val.l + #define VAR_STARTUP_RULES vars[V_STARTUP_RULES].current_val.l + #define GLO_STARTUP_RULES vars[V_STARTUP_RULES].global_val.l + #define USR_STARTUP_RULES vars[V_STARTUP_RULES].user_val.l #ifndef _WINDOWS #define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p #define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p diff -rc alpine-2.21/pith/conftype.h alpine-2.21.rules/pith/conftype.h *** alpine-2.21/pith/conftype.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/conftype.h Sun Feb 5 16:15:21 2017 *************** *** 70,75 **** --- 70,89 ---- , V_THREAD_MORE_CHAR , V_THREAD_EXP_CHAR , V_THREAD_LASTREPLY_CHAR + , V_THREAD_DISP_STYLE_RULES + , V_THREAD_INDEX_STYLE_RULES + , V_COMPOSE_RULES + , V_FORWARD_RULES + , V_INDEX_RULES + , V_KEY_RULES + , V_REPLACE_RULES + , V_REPLY_INDENT_RULES + , V_REPLY_LEADIN_RULES + , V_RESUB_RULES + , V_SAVE_RULES + , V_SMTP_RULES + , V_SORT_RULES + , V_STARTUP_RULES #ifndef _WINDOWS , V_CHAR_SET , V_OLD_CHAR_SET *************** *** 332,337 **** --- 346,352 ---- F_FULL_AUTO_EXPUNGE, F_EXPUNGE_MANUALLY, F_AUTO_READ_MSGS, + F_AUTO_READ_MSGS_RULES, F_AUTO_FCC_ONLY, F_READ_IN_NEWSRC_ORDER, F_SELECT_WO_CONFIRM, diff -rc alpine-2.21/pith/detoken.c alpine-2.21.rules/pith/detoken.c *** alpine-2.21/pith/detoken.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/detoken.c Sun Feb 5 16:15:21 2017 *************** *** 25,31 **** #include "../pith/reply.h" #include "../pith/mailindx.h" #include "../pith/options.h" ! /* * Hook to read signature from local file --- 25,31 ---- #include "../pith/reply.h" #include "../pith/mailindx.h" #include "../pith/options.h" ! #include "../pith/rules.h" /* * Hook to read signature from local file *************** *** 91,96 **** --- 91,98 ---- if(is_sig){ /* + * First we check if there is a rule about signatures, if there is + * use it, otherwise keep going and do the following: * If role->litsig is set, we use it; * Else, if VAR_LITERAL_SIG is set, we use that; * Else, if role->sig is set, we use that; *************** *** 104,117 **** * there is no reason to mix them, so we don't provide support to * do so. */ ! if(role && role->litsig) ! literal_sig = role->litsig; ! else if(ps_global->VAR_LITERAL_SIG) ! literal_sig = ps_global->VAR_LITERAL_SIG; ! else if(role && role->sig) ! sigfile = role->sig; ! else ! sigfile = ps_global->VAR_SIGNATURE_FILE; } else if(role && role->template) sigfile = role->template; --- 106,130 ---- * there is no reason to mix them, so we don't provide support to * do so. */ ! { RULE_RESULT *rule; ! rule = get_result_rule(V_COMPOSE_RULES, FOR_COMPOSE, env); ! if (rule){ ! sigfile = cpystr(rule->result); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! } ! if (!sigfile){ ! if(role && role->litsig) ! literal_sig = role->litsig; ! else if(ps_global->VAR_LITERAL_SIG) ! literal_sig = ps_global->VAR_LITERAL_SIG; ! else if(role && role->sig) ! sigfile = role->sig; ! else ! sigfile = ps_global->VAR_SIGNATURE_FILE; ! } } else if(role && role->template) sigfile = role->template; *************** *** 302,308 **** } } } ! else if(pt->what_for & FOR_REPLY_INTRO) repl = get_reply_data(env, role, pt->ctype, subbuf, sizeof(subbuf)-1); --- 315,321 ---- } } } ! else if(pt->what_for & (FOR_REPLY_INTRO | FOR_RULE)) repl = get_reply_data(env, role, pt->ctype, subbuf, sizeof(subbuf)-1); diff -rc alpine-2.21/pith/indxtype.h alpine-2.21.rules/pith/indxtype.h *** alpine-2.21/pith/indxtype.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/indxtype.h Sun Feb 5 16:15:21 2017 *************** *** 84,89 **** --- 84,94 ---- iCurNews, iArrow, iMailbox, iAddress, iInit, iCursorPos, iDay2Digit, iMon2Digit, iYear2Digit, + iFolder, iFlag, iCollection, iRole, iProcid, iScreen, iPkey, + iNick, iFccFrom, iFccSender, iAltAddress, + iAddressTo, iAddressCc, iAddressRecip, iAddressSender, + iBcc, iLcc, + iFfrom, iFadd, iSTime, iSTime24, iKSize, iRoleNick, iNewLine, iHeader, iText, *************** *** 105,119 **** /* these are flags for the what_for field in INDEX_PARSE_T */ ! #define FOR_NOTHING 0x00 ! #define FOR_INDEX 0x01 ! #define FOR_REPLY_INTRO 0x02 ! #define FOR_TEMPLATE 0x04 /* or for signature */ ! #define FOR_FILT 0x08 ! #define DELIM_USCORE 0x10 ! #define DELIM_PAREN 0x20 ! #define DELIM_COLON 0x40 ! #define DEFAULT_REPLY_INTRO "default" --- 110,135 ---- /* these are flags for the what_for field in INDEX_PARSE_T */ ! #define FOR_NOTHING 0x00000 ! #define FOR_INDEX 0x00001 ! #define FOR_REPLY_INTRO 0x00002 ! #define FOR_TEMPLATE 0x00004 /* or for signature */ ! #define FOR_FILT 0x00008 ! #define DELIM_USCORE 0x00010 ! #define DELIM_PAREN 0x00020 ! #define DELIM_COLON 0x00040 ! #define FOR_FOLDER 0x00080 /* for rules */ ! #define FOR_RULE 0x00100 /* for rules */ ! #define FOR_TRIM 0x00200 /* for rules */ ! #define FOR_RESUB 0x00400 /* for rules */ ! #define FOR_REPLACE 0x00800 /* for rules */ ! #define FOR_SORT 0x01000 /* for rules */ ! #define FOR_FLAG 0x02000 /* for rules */ ! #define FOR_COMPOSE 0x04000 /* for rules */ ! #define FOR_THREAD 0x08000 /* for rules */ ! #define FOR_STARTUP 0x10000 /* for rules */ ! #define FOR_KEY 0x20000 /* for rules */ ! #define FOR_SAVE 0x40000 /* for rules */ #define DEFAULT_REPLY_INTRO "default" diff -rc alpine-2.21/pith/mailcmd.c alpine-2.21.rules/pith/mailcmd.c *** alpine-2.21/pith/mailcmd.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/mailcmd.c Sun Feb 5 16:15:21 2017 *************** *** 39,44 **** --- 39,45 ---- #include "../pith/ablookup.h" #include "../pith/search.h" #include "../pith/charconv/utf8.h" + #include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" *************** *** 665,670 **** --- 666,672 ---- strncpy(ps_global->cur_folder, p, sizeof(ps_global->cur_folder)-1); ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0'; ps_global->context_current = ps_global->context_list; + setup_threading_index_style(); reset_index_format(); clear_index_cache(ps_global->mail_stream, 0); /* MUST sort before restoring msgno! */ *************** *** 991,996 **** --- 993,999 ---- clear_index_cache(ps_global->mail_stream, 0); reset_index_format(); + setup_threading_index_style(); /* * Start news reading with messages the user's marked deleted *************** *** 1114,1120 **** if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){ ! perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream); if(ps_global->start_entry > 0){ mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap) --- 1117,1126 ---- if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){ ! perfolder_startup_rule = get_perfolder_startup_rule(ps_global->mail_stream, ! V_STARTUP_RULES, newfolder); ! ! reset_startup_rule(ps_global->mail_stream); if(ps_global->start_entry > 0){ mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap) *************** *** 1136,1259 **** else use_this_startup_rule = ps_global->inc_startup_rule; ! switch(use_this_startup_rule){ ! /* ! * For news in incoming collection we're doing the same thing ! * for first-unseen and first-recent. In both those cases you ! * get first-unseen if FAKE_NEW is off and first-recent if ! * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the ! * same as first recent because all recent msgs are unseen ! * and all unrecent msgs are seen (see pine_mail_open). ! */ ! case IS_FIRST_UNSEEN: ! first_unseen: ! mn_set_cur(ps_global->msgmap, ! (sp_first_unseen(m) ! && mn_get_sort(ps_global->msgmap) == SortArrival ! && !mn_get_revsort(ps_global->msgmap) ! && !get_lflag(ps_global->mail_stream, NULL, ! sp_first_unseen(m), MN_EXLD) ! && (n = mn_raw2m(ps_global->msgmap, ! sp_first_unseen(m)))) ! ? n ! : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! break; ! ! case IS_FIRST_RECENT: ! first_recent: ! /* ! * We could really use recent for news but this is the way ! * it has always worked, so we'll leave it. That is, if ! * the FAKE_NEW feature is on, recent and unseen are ! * equivalent, so it doesn't matter. If the feature isn't ! * on, all the undeleted messages are unseen and we start ! * at the first one. User controls with the FAKE_NEW feature. ! */ ! if(IS_NEWS(ps_global->mail_stream)){ ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! } ! else{ ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_RECENT | F_UNSEEN ! | F_UNDEL, ! m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! } ! break; ! ! case IS_FIRST_IMPORTANT: ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! break; ! ! case IS_FIRST_IMPORTANT_OR_UNSEEN: ! ! if(IS_NEWS(ps_global->mail_stream)) ! goto first_unseen; ! ! { ! MsgNo flagged, first_unseen; ! ! flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! first_unseen = (sp_first_unseen(m) ! && mn_get_sort(ps_global->msgmap) == SortArrival ! && !mn_get_revsort(ps_global->msgmap) ! && !get_lflag(ps_global->mail_stream, NULL, ! sp_first_unseen(m), MN_EXLD) ! && (n = mn_raw2m(ps_global->msgmap, ! sp_first_unseen(m)))) ! ? n ! : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! mn_set_cur(ps_global->msgmap, ! (MsgNo) MIN((int) flagged, (int) first_unseen)); ! ! } ! ! break; ! ! case IS_FIRST_IMPORTANT_OR_RECENT: ! ! if(IS_NEWS(ps_global->mail_stream)) ! goto first_recent; ! ! { ! MsgNo flagged, first_recent; ! ! flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN ! | F_UNDEL, ! m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID); ! mn_set_cur(ps_global->msgmap, ! (MsgNo) MIN((int) flagged, (int) first_recent)); ! } ! ! break; ! ! case IS_FIRST: ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_UNDEL, m, pc, ! THREADING() ? 0 : FSF_SKIP_CHID)); ! break; ! ! case IS_LAST: ! mn_set_cur(ps_global->msgmap, ! first_sorted_flagged(F_UNDEL, m, pc, ! FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); ! break; ! ! default: ! alpine_panic("Unexpected incoming startup case"); ! break; ! ! } } else if(IS_NEWS(ps_global->mail_stream)){ /* --- 1142,1148 ---- else use_this_startup_rule = ps_global->inc_startup_rule; ! find_startup_position(use_this_startup_rule, m, pc); } else if(IS_NEWS(ps_global->mail_stream)){ /* *************** *** 1431,1439 **** /* Save read messages? */ if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0] && sp_flagged(stream, SP_INBOX) ! && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){ if(F_ON(F_AUTO_READ_MSGS,ps_global) || (pith_opt_read_msg_prompt && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER))) /* move inbox's read messages */ --- 1320,1330 ---- /* Save read messages? */ if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0] && sp_flagged(stream, SP_INBOX) ! && (F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || ! (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL)))){ if(F_ON(F_AUTO_READ_MSGS,ps_global) + || F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || (pith_opt_read_msg_prompt && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER))) /* move inbox's read messages */ *************** *** 1716,1721 **** --- 1607,1615 ---- char *bufp = NULL; MESSAGECACHE *mc; + if (F_ON(F_AUTO_READ_MSGS_RULES, ps_global)) + return move_read_msgs_using_rules(stream, dstfldr, buf); + if(!is_absolute_path(dstfldr) && !(save_context = default_save_context(ps_global->context_list))) save_context = ps_global->context_list; *************** *** 1755,1762 **** snprintf(buf, buflen, "Moving %s read message%s to \"%s\"", comatose(searched), plural(searched), dstfldr); we_cancel = busy_cue(buf, NULL, 0); ! if(save(ps_global, stream, save_context, dstfldr, msgmap, ! SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched) strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */ buf[buflen-1] = '\0'; --- 1649,1657 ---- snprintf(buf, buflen, "Moving %s read message%s to \"%s\"", comatose(searched), plural(searched), dstfldr); we_cancel = busy_cue(buf, NULL, 0); ! ps_global->exiting = 1; ! if((save(ps_global, stream, save_context, dstfldr, msgmap, ! SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched)) strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */ buf[buflen-1] = '\0'; *************** *** 1794,1800 **** && ((context_isambig(folder) && folder_is_nick(folder, FOLDERS(context), 0)) || folder_index(folder, context, FI_FOLDER) > 0) ! && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){ for(; f && *archive; archive++){ char *p; --- 1689,1697 ---- && ((context_isambig(folder) && folder_is_nick(folder, FOLDERS(context), 0)) || folder_index(folder, context, FI_FOLDER) > 0) ! && ((seen_undel = count_flagged(stream, F_SEEN | F_UNDEL)) ! || (F_ON(F_AUTO_READ_MSGS,ps_global) && ! F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){ for(; f && *archive; archive++){ char *p; *************** *** 2755,2757 **** --- 2652,2946 ---- return(*target ? target : NULL); } + + char * + move_read_msgs_using_rules(MAILSTREAM *stream, char *dstfldr, char *buf) + { + CONTEXT_S *save_context = NULL; + char **folder_to_save = NULL; + int num, we_cancel; + long i, j, success; + MSGNO_S *msgmap = NULL; + unsigned long nmsgs = 0L, stream_nmsgs; + + if(!is_absolute_path(dstfldr) + && !(save_context = default_save_context(ps_global->context_list))) + save_context = ps_global->context_list; + + folder_to_save = (char **)fs_get((stream->nmsgs + 1)*sizeof(char *)); + folder_to_save[0] = NULL; + mn_init(&msgmap, stream->nmsgs); + stream_nmsgs = stream->nmsgs; + for (i = 1L; i <= stream_nmsgs ; i++){ + set_lflag(stream, msgmap, i, MN_SLCT, 0); + folder_to_save[i] = get_lflag(stream, NULL, i, MN_EXLD) + ? NULL : get_folder_to_save(stream, i, dstfldr); + } + for (i = 1L; i <= stream_nmsgs; i++){ + num = 0; + if (folder_to_save[i]){ + mn_init(&msgmap, stream_nmsgs); + for (j = i; j <= stream_nmsgs ; j++){ + if (folder_to_save[j]){ + if (!strcmp(folder_to_save[i], folder_to_save[j])){ + set_lflag(stream, msgmap, j, MN_SLCT, 1); + num++; + if (j != i) + fs_give((void **)&folder_to_save[j]); + } + } + } + pseudo_selected(stream, msgmap); + sprintf(buf, "Moving %s read message%s to \"%.45s\"", + comatose(num), plural(num), folder_to_save[i]); + we_cancel = busy_cue(buf, NULL, 1); + ps_global->exiting = 1; + if(success = save(ps_global, stream,save_context, folder_to_save[i], + msgmap, SV_DELETE | SV_FIX_DELS)) + nmsgs += success; + if(we_cancel) + cancel_busy_cue(success ? 0 : -1); + for (j = i; j <= stream_nmsgs ; j++) + set_lflag(stream, msgmap, j, MN_SLCT, 0); + fs_give((void **)&folder_to_save[i]); + mn_give(&msgmap); + } + } + ps_global->exiting = 0; /* useful if we call from aggregate operations */ + sprintf(buf, "Moved automatically %s message%s", + comatose(nmsgs), plural(nmsgs)); + if (folder_to_save) + fs_give((void **)folder_to_save); + rule_curpos = 0L; + return buf; + } + + char * + get_folder_to_save(MAILSTREAM *stream, long i, char *dstfldr) + { + MESSAGECACHE *mc = NULL; + RULE_RESULT *rule; + MSGNO_S *msgmap = NULL; + char *folder_to_save = NULL, *save_folder = NULL; + int n; + long msgno; + + /* The plan is as follows: Select each message of the folder. We + * need to set the cursor correctly so that iFlag gets the value + * correctly too, otherwise iFlag will get the value of the position + * of the cursor. After that we need to look for a rule that applies + * to the message and get the saving folder. If we get a saving folder, + * and we used the _FLAG_ token, use that folder, if no + * _FLAG_ token was used, move only if seen and not deleted, to the + * folder specified in the saving rule. If we did not get a saving + * folder from the rule, just save in the default folder. + */ + mn_init(&msgmap, stream->nmsgs); + rule_curpos = i; + msgno = mn_m2raw(msgmap, i); + if (msgno > 0L){ + mc = mail_elt(stream, msgno); + rule = (RULE_RESULT *) + get_result_rule(V_SAVE_RULES, FOR_SAVE, mc->private.msg.env); + if (rule){ + folder_to_save = cpystr(rule->result); + n = rule->number; + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + + if (folder_to_save && *folder_to_save){ + RULELIST *list = get_rulelist_from_code(V_SAVE_RULES, + ps_global->rule_list); + RULE_S *prule = get_rule(list, n); + if (condition_contains_token(prule->condition, "_FLAG_") + || (mc->valid && mc->seen && !mc->deleted) + || (!mc->valid && mc->searched)) + save_folder = cpystr(folder_to_save); + else + save_folder = NULL; + } + else + if (!mc || (mc->seen && !mc->deleted)) + save_folder = cpystr(dstfldr); + mn_give(&msgmap); + rule_curpos = 0L; + return save_folder; + } + + unsigned long + rules_cursor_pos(MAILSTREAM *stream) + { + MSGNO_S *msgmap = sp_msgmap(stream); + return rule_curpos != 0L ? rule_curpos : mn_m2raw(msgmap,mn_get_cur(msgmap)); + } + + void + setup_threading_index_style(void) + { + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD, NULL); + if (rule || ps_global->VAR_THREAD_INDEX_STYLE){ + for(i = 0; v = thread_index_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_INDEX_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_index_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + } + + unsigned + get_perfolder_startup_rule(MAILSTREAM *stream, int rule_type, char *folder) + { + unsigned startup_rule; + char *rule_result; + + startup_rule = reset_startup_rule(stream); + rule_result = get_rule_result(FOR_STARTUP, folder, rule_type); + if (rule_result && *rule_result){ + int i; + NAMEVAL_S *v; + + for(i = 0; v = incoming_startup_rules(i); i++) + if(!strucmp(rule_result, v->name)){ + startup_rule = v->value; + break; + } + fs_give((void **)&rule_result); + } + return startup_rule; + } + + void + find_startup_position(int rule, MAILSTREAM *m, long pc) + { + long n; + switch(rule){ + /* + * For news in incoming collection we're doing the same thing + * for first-unseen and first-recent. In both those cases you + * get first-unseen if FAKE_NEW is off and first-recent if + * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the + * same as first recent because all recent msgs are unseen + * and all unrecent msgs are seen (see pine_mail_open). + */ + case IS_FIRST_UNSEEN: + first_unseen: + mn_set_cur(ps_global->msgmap, + (sp_first_unseen(m) + && mn_get_sort(ps_global->msgmap) == SortArrival + && !mn_get_revsort(ps_global->msgmap) + && !get_lflag(ps_global->mail_stream, NULL, + sp_first_unseen(m), MN_EXLD) + && (n = mn_raw2m(ps_global->msgmap, + sp_first_unseen(m)))) + ? n + : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_FIRST_RECENT: + first_recent: + /* + * We could really use recent for news but this is the way + * it has always worked, so we'll leave it. That is, if + * the FAKE_NEW feature is on, recent and unseen are + * equivalent, so it doesn't matter. If the feature isn't + * on, all the undeleted messages are unseen and we start + * at the first one. User controls with the FAKE_NEW feature. + */ + if(IS_NEWS(ps_global->mail_stream)){ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + } + else{ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_RECENT | F_UNSEEN + | F_UNDEL, + m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + } + break; + + case IS_FIRST_IMPORTANT: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_FIRST_IMPORTANT_OR_UNSEEN: + + if(IS_NEWS(ps_global->mail_stream)) + goto first_unseen; + + { + MsgNo flagged, first_unseen; + + flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + first_unseen = (sp_first_unseen(m) + && mn_get_sort(ps_global->msgmap) == SortArrival + && !mn_get_revsort(ps_global->msgmap) + && !get_lflag(ps_global->mail_stream, NULL, + sp_first_unseen(m), MN_EXLD) + && (n = mn_raw2m(ps_global->msgmap, + sp_first_unseen(m)))) + ? n + : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + mn_set_cur(ps_global->msgmap, + (MsgNo) MIN((int) flagged, (int) first_unseen)); + + } + + break; + + case IS_FIRST_IMPORTANT_OR_RECENT: + + if(IS_NEWS(ps_global->mail_stream)) + goto first_recent; + + { + MsgNo flagged, first_recent; + + flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN + | F_UNDEL, + m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + mn_set_cur(ps_global->msgmap, + (MsgNo) MIN((int) flagged, (int) first_recent)); + } + + break; + + case IS_FIRST: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_LAST: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNDEL, m, pc, + FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); + break; + + default: + alpine_panic("Unexpected incoming startup case"); + break; + + } + } diff -rc alpine-2.21/pith/mailcmd.h alpine-2.21.rules/pith/mailcmd.h *** alpine-2.21/pith/mailcmd.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/mailcmd.h Sun Feb 5 16:15:21 2017 *************** *** 42,47 **** --- 42,49 ---- #define DB_FROMTAB 0x02 /* opening because of TAB command */ #define DB_INBOXWOCNTXT 0x04 /* interpret inbox as one true inbox */ + static MAILSTREAM *saved_stream; + static unsigned long rule_curpos = 0L; /* * generic "is aggregate message command?" test *************** *** 63,69 **** --- 65,77 ---- void expunge_and_close(MAILSTREAM *, char **, unsigned long); void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int); char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long); + char *move_read_msgs_using_rules (MAILSTREAM *, char *, char *); + unsigned get_perfolder_startup_rule (MAILSTREAM *, int, char *); + void setup_threading_index_style (void); + void find_startup_position (int, MAILSTREAM *, long); + char *get_folder_to_save (MAILSTREAM *, long, char *); char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t); + unsigned long rules_cursor_pos (MAILSTREAM *); void cross_delete_crossposts(MAILSTREAM *); long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int); int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *); diff -rc alpine-2.21/pith/mailindx.c alpine-2.21.rules/pith/mailindx.c *** alpine-2.21/pith/mailindx.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/mailindx.c Sun Feb 5 16:15:21 2017 *************** *** 40,45 **** --- 40,46 ---- #include "../pith/send.h" #include "../pith/options.h" #include "../pith/ablookup.h" + #include "../pith/rules.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" #endif *************** *** 377,382 **** --- 378,390 ---- PAT_STATE pstate; PAT_S *pat; int we_set_it = 0; + char *rule; + + if(rule = get_rule_result(FOR_INDEX, ps_global->cur_folder, V_INDEX_RULES)){ + init_index_format(rule, &ps_global->index_disp_format); + fs_give((void **)&rule); + return; + } if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ *************** *** 450,464 **** static INDEX_PARSE_T itokens[] = { {"STATUS", iStatus, FOR_INDEX}, {"MSGNO", iMessNo, FOR_INDEX}, ! {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"FROMORTO", iFromTo, FOR_INDEX}, {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, {"SIZE", iSize, FOR_INDEX}, {"SIZECOMMA", iSizeComma, FOR_INDEX}, {"SIZETHREAD", iSizeThread, FOR_INDEX}, {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, ! {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"SHORTSUBJECT", iShortSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"FULLSTATUS", iFStatus, FOR_INDEX}, {"IMAPSTATUS", iIStatus, FOR_INDEX}, --- 458,472 ---- static INDEX_PARSE_T itokens[] = { {"STATUS", iStatus, FOR_INDEX}, {"MSGNO", iMessNo, FOR_INDEX}, ! {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, {"FROMORTO", iFromTo, FOR_INDEX}, {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, {"SIZE", iSize, FOR_INDEX}, {"SIZECOMMA", iSizeComma, FOR_INDEX}, {"SIZETHREAD", iSizeThread, FOR_INDEX}, {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, ! {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_TRIM}, {"SHORTSUBJECT", iShortSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"FULLSTATUS", iFStatus, FOR_INDEX}, {"IMAPSTATUS", iIStatus, FOR_INDEX}, *************** *** 469,524 **** {"SUBJECTTEXT", iSubjectText, FOR_INDEX}, {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX}, {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX}, ! {"OPENINGTEXT", iOpeningText, FOR_INDEX}, ! {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX}, ! {"KEY", iKey, FOR_INDEX}, ! {"KEYINIT", iKeyInit, FOR_INDEX}, {"DESCRIPSIZE", iDescripSize, FOR_INDEX}, {"ATT", iAtt, FOR_INDEX}, {"SCORE", iScore, FOR_INDEX}, {"PRIORITY", iPrio, FOR_INDEX}, {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX}, ! {"PRIORITY!", iPrioBang, FOR_INDEX}, ! {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"SMARTTIME24", iSTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, --- 477,536 ---- {"SUBJECTTEXT", iSubjectText, FOR_INDEX}, {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX}, {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX}, ! {"OPENINGTEXT", iOpeningText, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM}, ! {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM}, ! {"KEY", iKey, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE}, ! {"KEYINIT", iKeyInit, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE}, {"DESCRIPSIZE", iDescripSize, FOR_INDEX}, {"ATT", iAtt, FOR_INDEX}, {"SCORE", iScore, FOR_INDEX}, {"PRIORITY", iPrio, FOR_INDEX}, {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX}, ! {"PRIORITY!", iPrioBang, FOR_INDEX}, ! {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, {"SMARTTIME24", iSTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE}, ! {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE}, ! {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_SAVE}, ! {"ADDRESSTO", iAddressTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESSCC", iAddressCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESSRECIPS", iAddressRecip, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESSSENDER", iAddressSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, *************** *** 527,582 **** {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"CURPREFDATETIME", iCurPrefDateTime, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, ! {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, {"HEADER", iHeader, FOR_INDEX}, {"TEXT", iText, FOR_INDEX}, {"ARROW", iArrow, FOR_INDEX}, {"NEWLINE", iNewLine, FOR_REPLY_INTRO}, {"CURSORPOS", iCursorPos, FOR_TEMPLATE}, {NULL, iNothing, FOR_NOTHING} }; --- 539,609 ---- {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, ! {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE}, {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, ! {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"CURPREFDATETIME", iCurPrefDateTime, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit, ! FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, ! {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE}, {"HEADER", iHeader, FOR_INDEX}, {"TEXT", iText, FOR_INDEX}, {"ARROW", iArrow, FOR_INDEX}, {"NEWLINE", iNewLine, FOR_REPLY_INTRO}, {"CURSORPOS", iCursorPos, FOR_TEMPLATE}, + {"NICK", iNick, FOR_RULE|FOR_SAVE}, + {"FCCFROM", iFccFrom, FOR_RULE|FOR_SAVE}, + {"FCCSENDER", iFccSender, FOR_RULE|FOR_SAVE}, + {"ALTADDRESS", iAltAddress, FOR_RULE|FOR_SAVE}, + {"FOLDER", iFolder, FOR_RULE|FOR_SAVE|FOR_FOLDER}, + {"ROLE", iRole, FOR_RULE|FOR_RESUB|FOR_TRIM|FOR_TEMPLATE}, + {"PROCID", iProcid, FOR_RULE|FOR_RESUB|FOR_FLAG|FOR_COMPOSE|FOR_TRIM|FOR_TEMPLATE}, + {"PKEY", iPkey, FOR_RULE|FOR_KEY}, + {"SCREEN", iScreen, FOR_RULE|FOR_KEY}, + {"FLAG", iFlag, FOR_RULE|FOR_SAVE|FOR_FLAG}, + {"COLLECTION", iCollection, FOR_RULE|FOR_SAVE|FOR_COMPOSE|FOR_FOLDER}, + {"BCC", iBcc, FOR_COMPOSE|FOR_RULE}, + {"LCC", iLcc, FOR_COMPOSE|FOR_RULE}, + {"FORWARDFROM", iFfrom, FOR_COMPOSE|FOR_RULE}, + {"FORWARDADDRESS", iFadd, FOR_COMPOSE|FOR_RULE}, {NULL, iNothing, FOR_NOTHING} }; *************** *** 2483,2488 **** --- 2510,2533 ---- from_str(cdesc->ctype, idata, str, sizeof(str), ice); break; + case iAddressTo: + case iAddressCc: + case iAddressRecip: + {ENVELOPE *env; + int we_clear; + env = rules_fetchenvelope(idata, &we_clear); + sprintf(str, "%-*.*s", ifield->width, ifield->width, + detoken_src((cdesc->ctype == iAddressTo + ? "_ADDRESSTO_" + : (cdesc->ctype == iAddressCc + ? "_ADRESSCC_" + : "_ADRESSRECIPS_")), FOR_INDEX, + env, NULL, NULL, NULL)); + if(we_clear) + mail_free_envelope(&env); + } + break; + case iTo: if(((field = ((addr = fetch_to(idata)) ? "To" *************** *** 3833,3839 **** --- 3878,3894 ---- if(p > buf){ size_t l; + ENVELOPE *env; + char *rule_result; + if(rule_result = find_value((delete_quotes + ? "_OPENINGTEXTNQ_" : "_OPENINGTEXT_"), + buf, PROCESS_SP, idata, 4)){ + collspaces(rule_result); + strncpy(buf, rule_result, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + fs_give((void **) &rule_result); + } l = strlen(buf); l += 100; firsttext = fs_get((l+1) * sizeof(char)); *************** *** 5407,5416 **** { char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL; char *p, *border, *q = NULL, *free_subj = NULL; ! char *sp; size_t len; int width = -1; ! int depth = 0, mult = 2; int save; int do_subj = 0, truncated_tree = 0; PINETHRD_S *thd, *thdorig; --- 5462,5471 ---- { char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL; char *p, *border, *q = NULL, *free_subj = NULL; ! char *sp, *rule_result; size_t len; int width = -1; ! int depth = 0, mult = 2, collapsed, i, we_clear = 0; int save; int do_subj = 0, truncated_tree = 0; PINETHRD_S *thd, *thdorig; *************** *** 5466,5471 **** --- 5521,5534 ---- * to free it at the end of this routine. */ + if (rule_result = find_value("_SUBJECT_", origsubj, PROCESS_SP, idata, 4)){ + if(origsubj) + fs_give((void **)&origsubj); + we_clear++; + origsubj = cpystr(rule_result); + fs_give((void **)&rule_result); + } + if(shorten) shorten_subject(origsubj); *************** *** 5908,5913 **** --- 5971,5979 ---- if(free_subj) fs_give((void **) &free_subj); + + if (we_clear && origsubj) + fs_give((void **)&origsubj); } *************** *** 6276,6291 **** ? "To" : (addr = fetch_cc(idata)) ? "Cc" ! : NULL)) ! && set_index_addr(idata, field, addr, "To: ", ! strsize-1, fptr)) ! break; if(ctype == iFromTo && (newsgroups = fetch_newsgroups(idata)) && *newsgroups){ ! snprintf(fptr, strsize, "To: %-*.*s", (int)(strsize-1-4), (int)(strsize-1-4), ! newsgroups); break; } --- 6342,6374 ---- ? "To" : (addr = fetch_cc(idata)) ? "Cc" ! : NULL))){ ! char *rule_result; ! rule_result = find_value("_FROM_", NULL, 0, idata, 1); ! if (!rule_result) ! set_index_addr(idata, field, addr, "To: ", ! strsize-1, fptr); ! else{ ! sprintf(str, "%-*.*s", strsize-1, strsize-1, ! rule_result); ! fs_give((void **)&rule_result); ! } + break; + } if(ctype == iFromTo && (newsgroups = fetch_newsgroups(idata)) && *newsgroups){ ! char *rule_result; ! rule_result = find_value("_FROM_", NULL, 0, idata, 1); ! if (!rule_result) ! sprintf(str, "To: %-*.*s", strsize-1-4, ! strsize-1-4, newsgroups); ! else{ ! sprintf(str, "%-*.*s", strsize-1, strsize-1, ! rule_result); ! fs_give((void **)&rule_result); ! } break; } *************** *** 6298,6304 **** break; case iFrom: ! set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr); break; case iAddress: --- 6381,6395 ---- break; case iFrom: ! { char *rule_result; ! rule_result = find_value("_FROM_", NULL, 0, idata, 4); ! if (!rule_result) ! set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr); ! else{ ! sprintf(str, "%-*.*s", strsize-1, strsize-1, rule_result); ! fs_give((void **)&rule_result); ! } ! } break; case iAddress: *************** *** 6596,6598 **** --- 6687,6750 ---- } } } + + void + setup_threading_display_style(void) + { + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD, NULL); + if (rule || ps_global->VAR_THREAD_DISP_STYLE){ + for(i = 0; v = thread_disp_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_DISP_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_disp_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + } + + char * + find_value(char *token, char *use_this, int flag, INDEXDATA_S *idata, int nfcn) + { + int n = 0, i, rule_context, we_clear; + char *rule_result = NULL, **list; + ENVELOPE *env; + RULELIST *rule; + RULE_S *prule; + + env = rules_fetchenvelope(idata, &we_clear); + if(env && env->sparep) + fs_give((void **)&env->sparep); + if(we_clear) + mail_free_envelope(&env); + if(rule = get_rulelist_from_code(V_REPLACE_RULES, ps_global->rule_list)){ + list = functions_for_token(token); + while(rule_result == NULL && (prule = get_rule(rule,n++))){ + rule_context = 0; + if (prule->action->token && !strcmp(prule->action->token, token)){ + for (i = 0; i < nfcn; i++) + if(list[i+1] && !strcmp(prule->action->function, list[i+1])) + rule_context |= context_for_function(list[i+1]); + if (rule_context){ + env = rules_fetchenvelope(idata, &we_clear); + if(use_this) + env->sparep = get_sparep_for_rule(use_this, flag); + rule_result = process_rule(prule, rule_context, env); + if(env->sparep) + free_sparep_for_rule(&env->sparep); + if(we_clear) + mail_free_envelope(&env); + } + } + } + } + return rule_result; + } diff -rc alpine-2.21/pith/mailindx.h alpine-2.21.rules/pith/mailindx.h *** alpine-2.21/pith/mailindx.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/mailindx.h Sun Feb 5 16:15:21 2017 *************** *** 30,35 **** --- 30,38 ---- /* exported prototypes */ + SortOrder translate (char *, int); + char *find_value (char *, char *, int, INDEXDATA_S *, int); + void setup_threading_display_style (void); int msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int); void adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *); unsigned long line_hash(char *); diff -rc alpine-2.21/pith/makefile.wnt alpine-2.21.rules/pith/makefile.wnt *** alpine-2.21/pith/makefile.wnt Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/makefile.wnt Sun Feb 5 16:15:21 2017 *************** *** 45,51 **** init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \ mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \ options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \ ! rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \ state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \ thread.h url.h user.h util.h --- 45,51 ---- init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \ mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \ options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \ ! rfc2231.h rules.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \ state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \ thread.h url.h user.h util.h *************** *** 54,60 **** filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \ keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \ margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \ ! readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \ status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \ thread.obj adjtime.obj url.obj util.obj --- 54,60 ---- filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \ keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \ margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \ ! readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \ status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \ thread.obj adjtime.obj url.obj util.obj diff -rc alpine-2.21/pith/pine.hlp alpine-2.21.rules/pith/pine.hlp *** alpine-2.21/pith/pine.hlp Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/pine.hlp Sun Feb 5 16:15:21 2017 *************** *** 3855,3860 **** --- 3855,3861 ----
  • FEATURE:
  • FEATURE:
  • FEATURE: +
  • FEATURE:
  • FEATURE:
  • FEATURE:
  • FEATURE: *************** *** 19045,19050 **** --- 19046,19052 ---- "" option, in the "" option, in signature files, + in the "new-rules" option, in template files used in "roles", and in the folder name that is the target of a Filter Rule. *************** *** 19057,19063 ****

    !

    Tokens Available for all Cases (except Filter Rules)

    SUBJECT
    --- 19059,19065 ----

    !

    Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)

    SUBJECT
    *************** *** 19091,19096 **** --- 19093,19114 ---- For example, "mailbox@domain". +
    ADDRESSTO
    +
    + This is similar to the "TO" token, only it is always the + email address of all people listed in the TO: field of the messages. Addresses + are separated by a blank space. Example, "mailbox@domain" when + the e-mail message contains only one person in the To: field, or + "peter@flintstones.com president@world.com". +
    + +
    ADDRESSSENDER
    +
    + This is similar to the "sender" token, only it is always the + email address of all person listed in the Sender: field of the message. + Example: "mailbox@domain". +
    +
    MAILBOX
    This is the same as the "ADDRESS" except that the *************** *** 19138,19143 **** --- 19156,19170 ---- message's "Cc:" header field.
    +
    ADDRESSCC
    +
    + This is similar to the "CC" token, only it is always the + email address of all people listed in the Cc: field of the messages. Addresses + are separated by a blank space. Example: "mailbox@domain" when + the e-mail message contains only one person in the Cc: field, or + "peter@flintstones.com president@world.com". +
    +
    RECIPS
    This token represents the personal names (or email addresses if the names *************** *** 19146,19151 **** --- 19173,19186 ---- the message's "Cc:" header field.
    +
    ADDRESSRECIPS
    +
    + This token represent the e-mail addresses of the people in the To: and + Cc: fields, exactly in that order separated by a space. It is almost obtained + by concatenating the ADDRESSTO and ADDRESSCC tokens. +
    + +
    NEWSANDRECIPS
    This token represents the newsgroups from the *************** *** 20270,20275 **** --- 20305,20414 ----

    +

    Tokens Available Only for New-Rules

    + +
    +
    FCCFROM
    +
    + The Fcc: folder assigned to the email address in the From: field in the + addressbook. +
    +
    + +
    +
    FCCSENDER
    +
    + The Fcc: folder assigned to the email address in the Sender: field in the + addressbook. +
    +
    + +
    +
    ALTADDRESS
    +
    + The value of your + + variable. At this time, no expansion of regular expressions is supported. +
    +
    + +
    +
    NICK
    +
    + Nickname of the person in the From field in your addressbook. +
    +
    + +
    +
    FOLDER
    +
    + Name of the folder where the rule will be applied. +
    +
    + +
    +
    COLLECTION
    +
    + Name of the collection list where the rule will be applied. +
    +
    + +
    +
    ROLE
    +
    + Name of the Role used to reply a message. +
    +
    + +
    +
    BCC
    +
    + Not implemented yet, but it will be implemented in future versions. It will + be used for compose + reply + forward + rules. +
    +
    + +
    +
    LCC
    +
    + This is the value of the Lcc: field at the moment that you start the composition. +
    +
    + +
    +
    FORWARDFROM
    +
    + This corresponds to the personal name (or address if there's no personal + name) of the person who sent the message that you are forwarding. +
    +
    + +
    +
    FORWARDADDRESS
    +
    + This is the address of the person that sent the message that you + are forwarding. +
    +
    + + + + +
    +
    FLAG
    +
    + A string containing the value of all the flags associated to a specific + message. The possible values of allowed flags are "*" for Important, "N" + for recent or new, "U" for unseen or unread, "R" for seen or read, "A" for + answered and "D" for deleted. See an example of its use in the + new rules explanation and example help. +
    +
    + +

    Token Available Only for Templates and Signatures

    *************** *** 23397,23402 **** --- 23536,24432 ---- <End of help on this topic> + ====== h_config_procid ===== + + + Token: PROCID + + +

    TOKEN: PROCID explained

    + +

    + The PROCID token is a way in which the user and the program can differentiate + between different parts of a program. It allows the user to tell the + program when to use a specific rule, and only use it at that specific + moment. + +

    The normal way in which this is done is by adding a new configuration + variable. The idea behind the PROCID token is that instead of adding a new + configuration variable (which means the user has to go through more + configuration variables just to tune the program to his liking), we reuse + an old variable and let the user look inside that variable for the desired + behavior, which is actually set by setting the PROCID token. + +

    + Consider the following examples for forward-rules: + +

    + _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} + +

    + and + +

    + _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +

    + both are triggered by the same condition. Since both are configured in the + same variable, only one of them will be executed all the time (whichever + is first). Therefore in order to differentiate, we add a _PROCID_ token. + So, for example, the first example above will be executed only when we are + determining the subject. In this case, the following rule will accomplish + this task + +

    + _PROCID_ == {fwd-subject} && _ROLE_ == {work} => _SUBJECT_ := _COPY_{[tag] _SUBJECT_} + +

    + In this case, this rule will be tested fully only when we are determining + the subject line of a forwarded message, not otherwise. + +

    + It is wise to add the _PROCID_ token as the first condition in a rule, so + that other conditions will not be tested in a long list of rules. + +

    <End of help on this topic> + + + ====== h_config_compose_rules ===== + + + OPTION: <!--#echo var="VAR_compose-rules"--> + + +

    OPTION:

    + +

    At this time, this option is used to generate values for signature + files that is not possible to do with the use of + roles. + +

    For example, you can have a rule like:
    + _TO_ >> {Peter Flintstones} => _SIGNATURE_{~/.petersignature} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_forward_rules ===== + + + OPTION: <!--#echo var="VAR_forward-rules"--> + + +

    OPTION:

    + +

    This option has several uses. This feature uses the PROCID function + to identify different features of forwarding. You can read more about PROCID + by following this link. + +

    If you want to edit the subject of a forwarded message, use the + PROCID fwd-subject. For example you could have a rule like + +

    + _ROLE_ == {admin} && _SUBJECT_ !> {[tag] } => _COPY_{[tag] _SUBJECT_} + +

    Another way in which this option can be used, is to trim the values of + some fields. For this application the PROCID is fwd-lcc. For + example it can be used in the following way: + +

    + _ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +

    Other functions that can be used in this option are _EXEC_ and _REXTRIM_. + +

    You can also use the _EXEC_ function. The documentation for this function + is in the + + help text. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_index_rules ===== + + + OPTION: <!--#echo var="VAR_index-rules"--> + + +

    OPTION:

    + +

    This option is used to supersede the value of the option for specific folders. In + this form you can have different index-formats for different folders. For + example an entry here may be: + +

    + _FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_pretty_command ===== + + + Pretty-Command Explained + + +

    Pretty Command Explained

    + +

    This text explains how to encode keys so that they will be recognized + by Alpine in the _PKEY_ token. Most direct keystrokes are recognized in the + same way. For example, the key ~ is recognized by the same character. The + issue is how control, or functions keys are recognized. The internal code + is most times easy to find out. If the key you want to use is not already + recognized by Alpine simply press it. Alpine will print its code. For example, + the return key is not recognized in this screen, so if you press it, you + will see the following message. + +

    [Command "RETURN" not defined for this screen. Use ? for help] + +

    from here you can guess that the code for the return command is + RETURN. You can try other commands, like Control-C, the TAB key, F4, etc. + to see their codes. + +

    <End of help on this topic> + + + ====== h_config_key_macro_rules ===== + + + OPTION: <!--#echo var="VAR_key-definition-rules"--> + + +

    OPTION:

    + +

    This option can be used to define macros, that is, to define a key that + when pressed executes a group of predetermined keystrokes. Since Alpine is + a menu driven program, sometimes the same key may have different meanings + in different screens, so a global redefinition of a key although possible + is not advisable. + +

    Always use the _SCREEN_ token as defined below.. You have been + warned! + +

    In each screen, every time you press a recognized key, a command is + activated. In order to understand this feature, think of commands instead + of keystrokes. For example, you can think of the sort by thread command. + This command is associated to the keystrokes $ and h. You may want to + associate this command to a specific keystroke, like ~, so every time you + press the ~ key, Alpine understand the $ and h keystrokes, which activates + the sort by thread command. + +

    Therefore, in order to use this option you must think of three + components. The screen where you will use the macro, the keystroke you + want to use and the set of keystrokes used by Alpine to accomplish the task + you want to accomplish. We will talk about these three components in what + follows. + +

    First you must decide in which screen the macro will be used. This + feature is currently only available for the screen where your messages + are listed in index form (MESSAGE INDEX), + the screen where your message is displayed + (MESSAGE TEXT) and the screen where the list of + folders is displayed (FOLDER LIST). The + internal names of these screens for this patch are "index", + "text" and + "folder" respectively. Please note that the internal names are + all in lowercase and are case sensitive. + +

    In order to define the screen, you use the _SCREEN_ token, so for + example, you can write _SCREEN_ == {index}. + +

    Second you must think of which key you will use to activate the macro. + Here you can use any key of your choice. The token you use to designate a + key is the _PKEY_ token (PKEY stands for "pressed key"). For + example you could use _PKEY_ == {~}, to designate the "~" + key to activate the command. Some keystrokes (like control, or + function keys) are encoded in special ways. You should read the + full explanation on how to find + out the encoding for each keystroke. + +

    Last, you must think of the list of keys you will use to accomplish + the task you want Alpine to perform. Say for example you want to have the + folder sorted by thread. That means you want Aline to execute the keys + "$" and "h". You use the _COMMAND_ function to specify + this. The syntax in this case is _COMMAND_{$,h}. + +

    Observe that in the above example the different inputs are separated + by commas. This is the standard way in which the + command works from + the command line. Due to restrictions in the way Alpine works, a comma is a + special character, which when added to a configuration option like this + will cause the configuration to split into several lines in the + configuration screen. This has the effect of producing several + configuration options, all of which are incorrect. This is undesirable + because what you want is to have it all in one line. In order to force the + configuration into one line you must quote the comma. The best way to + accomplish this is by quoting the full definition of the rule. For + example. + +

    + "_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}" + +

    Another way to accomplish the same effect is by quoting the command and + not using quotes for the full command, nor commas to separate the + keystrokes in the command, for example + +

    + _SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{"$h"} + +

    For more information on how to define the argument of the _COMMAND_ + token see the help of + . + +

    Because the $ command can also be used as the first character in the + definition of an environemnt variable, no expansion of environment variables + is done when parsing this variable. The $ character does not need quoting + and quoting it will make Alpine fail to produce the correct result. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_replace_rules ===== + + + OPTION: <!--#echo var="VAR_replace-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine print different values for specific + tokens in the . For example you + can replace strings like "To: newsgroup" by your name. + +

    Here are examples of possible rules: + +

    _FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)} + +

    or if you receive messages with tags that contain arbitrary numbers, and + you want them removed from the index (but not from the subject), use a rule + like the following + +

    _FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{\[some-tag-here #[0-9].*\]} + +

    You can also use this configuration option to remove specific strings of + the index display screen, so that you can trim unnecessary information in + your index, like the reply leadin string in the OPENINGTEXTNQ token of the index.
    + +

    _FOLDER_ == {some-folder} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: } + +

    You can also use the _EXEC_ function. The documentation for this function + is in the + + help text. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_reply_leadin_rules ===== + + + OPTION: <!--#echo var="VAR_reply-leadin-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine generate a different + string dependent either on + the person you are replying to, or the folder where the message is being + replied is in, or both. + +

    Here there are examples of how this can be used. One can use the definition + below to post to newsgroups and the pine-info mailing list, say: +

    + _FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):} + +

    Here there is an example that one can use to change the reply indent string + to reply people that speak spanish. +

    + _FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribió _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_resub_rules ===== + + + OPTION: <!--#echo var="VAR_reply-subject-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine generate a different subject when + replying rather than the one Alpine would generate automatically. + +

    Here there are a couple of examples about how to use this + configuration option: + +

    In order to have messages with empty subject to be replied with the message + "your message" use the rule
    +

    _SUBJECT_ == {} => _RESUB_{Re: your message}
    + +

    If you want to trim some parts of the subject when you reply use the + rule
    +

    _SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}
    + +

    this rule removes the brackets "[" and "]" whenever the string "[one]" + appears in it, it also removes the word "two" from it. + +

    Another example where you may want to use this rule is when you + correspond with people that change the reply string from "Re:" + to "AW:" or "Sv:". In this case a rule like
    +

    _SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }
    +

    + would eliminate undesired strings in replies. + +

    Another interesting use of this option is the use of the _EXEC_ function. + This function takes as an argument a program or a script. This program + must take as the input a file, and write its output to that file. For example, + below is a sample of a script that removes the letter "a" of a file. + +

    + #!/bin/sh
    + sed 's/a//g' $1 > /tmp/mytest
    + mv /tmp/mytest $1
    + 
    + +

    + As you can see this script took "$1" as input file, the sed program + wrote its output to /tmp/mytest, and then the move program moved the file + /tmp/mytest to the input file "$1". This is the kind of behavior + that your program is expected to have. + +

    + The content of the input file ("$1" above) is the value of a token + like _SUBJECT_. In order to indicate this, we use the notation + +

    + _SUBJECT_ := _EXEC_{/path/to/script} + +

    for the action. So for example + +

    + _FOLDER_ := {sent-mail} => _SUBJECT_ := _EXEC_{/path/to/script} + +

    is a valid rule. + +

    You can also use this configuration option to customize reply subjects + according to the sender of the message. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + ====== h_config_sort_rules ===== + + + OPTION: <!--#echo var="VAR_sort-rules"--> + + +

    OPTION:

    + +

    This option is used to have Alpine sort different folders in different orders + and thus override the value already set in the + configuration option. + +

    Here's an example of the way it can be used. In this case all incoming + folders are mailing lists, except for INBOX, so we sort INBOX by arrival + (which is the default type of sort), but we want all the rest of mailing + lists and newsgroups to be sorted by thread. + +

    + _COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread} + +

    Another example could be
    + _FOLDER_ == {Mailing List} => _SORT_{Reverse tHread} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_save_rules ===== + + + OPTION: <!--#echo var="VAR_save-rules"--> + + +

    OPTION:

    + +

    This option is used to specify which folder should be used to save a + message depending either on the folder the message is in, who the message + is from, or text that the message contains in specific headers (Cc:, + Subject:, etc). + +

    If this option is set and the + configuration + option is also enabled then these definitions will be used to move messages + from your INBOX when exiting Alpine. + +

    Here there are some examples
    + _FLAG_ >> {D} -> Trash
    + _FROM_ == {U2} -> Bono
    + _FOLDER_ == {comp.mail.pine} -> pine-stuff
    + _NICK_ != {} -> _NICK_/_NICK_
    + _DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002 + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_reply_indent_rules ===== + + + OPTION: <!--#echo var="VAR_reply-indent-rules"--> + + +

    OPTION:

    + +

    This option is used to specify which reply-indent-string is to be used + when replying to an e-mail. If none of the rules are successful, the result in + the variable + is used. + +

    The associated function to this configuration option is called "RESTR" (for + REply STRing). Some examples of its use are:
    + _FROM_ == {Your Boss} => _RESTR_{"> "}
    + _FROM_ == {My Wife} => _RESTR_{":* "}
    + _FROM_ == {Perter Flintstone;Wilma Flintstone} => _RESTR_{"_INIT_ > "}
    + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_smtp_rules ===== + + + OPTION: <!--#echo var="VAR_smtp-rules"--> + + +

    OPTION:

    + +

    This option is used to specify which SMTP server should be used when + sending a message, if this rule is not defined, or the execution of the rule + results in no server selected, then Alpine will look for + the value from the role that is being used to compose the message. If no smtp + server is defined in that role or you are not using a role, then Alpine will get + the name of the server from the + "" configuration + option according to the rules used in that variable. + +

    The function associated to this configuration option is _SMTP_, an example + of the use of this function is
    + _ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_startup_rules ===== + + + OPTION: <!--#echo var="VAR_startup-rules"--> + + +

    OPTION:

    + +

    This option is used when a folder is being opened. You can use it to specify its and override + Alpine's global value set for all folders. + +

    An example of the usage of this option is:
    + _FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen} + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    <End of help on this topic> + + + + ====== h_config_new_rules ===== + + + OPTION: New Rules Explained + + +

    OPTION: New Rules Explained

    + + This is a quite powerful option. Here you can define rules that override + the values of any other option you have set in Alpine. + +

    + For example, you can set your folders to be sorted in a certain way when + you open them (say by Arrival). You may want, however, your newsgroups to + be sorted by thread. The set of "rules" options allows you to + configure this and many other options, including the index-format for + specific folders, the way the subject is displayed in the index screen or + the reply-leadin-string, to name a few. + +

    + Every rule has three parts: a condition, a separator and an action. The + action is what will happen if the condition of the rule is satisfied. + +

    + Here is an example: + +

    + _FROM_ == {Fred Flintstone} => _SAVE_{Fred} + +

    + Here the separator is "=>". Whatever is to the left of the separator + is the condition (that is _FROM_ == {Fred Flintstone}) and to the right is + the action (_SAVE_{Fred}). The condition means that the rule will be + applied only if the message that you are reading is from "Fred + Flintstone", and the action will be that you will be offered to save + it in the folder "Fred", whenever you press the letter + "S" to save a message. + +

    + The separator is always "=>", with one exception to be seen + later. But for the most part this will be the only one you will ever need. + +

    + Now let us see how to do it. There are 13 functions already defined for + you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_, + _SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _THREADSTYLE and + _THREADINDEX_. The parameter of a function has to be enclosed between + "{" and "}", so for example you can specify + _SAVE_{saved-messages} as a valid sentence. + +

    + Later in the document you will find examples. Here is a short + description of what each function does: + +

    +

      +
    • _EXEC_ : This function takes as an argument a program. This program + gets as the input a file and must rewrite its output to that file, which + is then taken as the value to replace from the contents of that file. You + can use this function with + , + and + . + See the help of those options for examples of how to use this function + and configure these rules. +
       
      +
    • _INDEX_ : This function takes as an argument an index-format, and + makes that the index-format for the specified folder. +
       
      +
    • _REPLACE_ : This function replaces the subject/from of the given e-mail by + another subject/from only when displaying the index. +
       
      +
    • _REPLY_ : This function takes as an argument a definition of a + reply-leadin-string and makes this the reply-leading-string of the + specified folder or person. +
       
      +
    • _RESTR_ : This function takes as an argument the value of the + reply-indent-string to be used to answer the message being replied to. +
       
      +
    • _RESUB_ : This function replaces the subject of the given e-mail by + another subject only when replying to a message. +
       
      +
    • _SAVE_ : The save function takes as an argument the name of a + possibly non existing folder, whenever you want to save a message, that + folder will be offered for you to save. +
       
      +
    • _SIGNATURE_ : This function takes as an argument a signature file and + uses that file as the signature for the message you are about to + compose/reply/forward. +
       
      +
    • _SMTP_ : This function takes as an argument the definition of a + SMTP server. +
       
      +
    • _SORT_ : This function takes as an argument a Sort Style, and sorts a + specified folder in that sort order. +
       
      +
    • _TRIM_ : This function takes as an argument a list of strings that + you want removed from another string. At this time this only works for + _FROM_ and _SUBJECT_. +
       
      +
    • _REXTRIM_ : Same as _TRIM_ but its argument is one and + only one extended regular expression. +
       
      +
    • _STARTUP_ : This function takes as an argument an + incoming-startup-rule, and open an specified folder using that rule. +
       
      +
    • _THREADSTYLE_ : This function takes as an argument a + threading-display-style and uses it to display threads in a folder. +
       
      +
    • _THREADINDEX_ : This function takes as an argument a + threading-index-style and uses it to display threads in a folder. +
    + +

    + You must me wondering how to define the person/folder over who to apply + the action. This is done in the condition. When you specify a rule, the + rule is only executed if the condition is satisfied. In another words for + the rule: + +

    + _FROM_ == {Fred Flintstone} => _SAVE_{Fred} + +

    it will only be applied if the from is "Fred Flintstone". If + the From is "Wilma Flintstone" the rule will be skipped. + +

    In order to test a condition you can use the following tokens (in + alphabetical order): _ADDRESS_, _CC_, _FOLDER_, _FROM_,_NICK_, _ROLE, + _SENDER_, _SUBJECT_ and _TO_. The token will always be tested against what + it is between "{" and "}" in the condition, this part + of the condition is called the "condition set". The definition + of each token can be found here. + +

    A special testing token called _PROCID_ can be used to differentiate + inside a rule, between two rules that are triggered by the same condition. + A full explanation of the _PROCID_ token can be found in + this link. + +

    There are two more tokens related to the option + key-definition-rules. Those tokens + are only specific to that option, and hence are not explained here. + +

    You can also test in different ways, you can use the following + "test operands": <<, !<, >>, !>, == and !=. + All of them are two characters long. Here is the meaning of them: + +

    +

      +
    • << : It tests if the value of the token is contained in + the condition set. Here for example if the condition set were equal to + "Freddy", then the condition: _NICK_ << {Freddy}, would be true if + the value of _NICK_ were "Fred", "red" or "Freddy". You are just looking + for substrings here. +
    • >> : It tests if the value of the token contains the value of + the condition set. Here for example if the condittion set were equal to + "Fred", then the condition: _FROM_ >> {Fred}, would be true if + the value of _FROM_ were "Fred Flintstone" or "Fred P. Flintstone" or "Freddy". +
    • == : It tests if the value of the token is exactly equal to the value + of the set condition. For example _NICK_ == {Fred} will be false if the value + of _NICK_ is "Freddy" or "red". +
    • !< : This is true only when << is false and vice versa. +
    • !> : This is true only when >> is false and vice versa. +
    • != : This is true only when == is false and vice versa. +
    + +

    + Now let us say that you want the same action to be applied to more than + one person or folder, say you want "folder1" and "folder2" to be sorted by + Ordered Subject upon entering. Then you can list them all of them in the + condition part separting them by a ";". Here is the way to do it. + +

    + _FOLDER_ << {folder1; folder2} => _SORT_{OrderedSubj} + +

    + Here is the first subtlety about these definitions. Notice that the + following rule: + +

    + _FOLDER_ == {folder1; folder2} => _SORT_{Reverse OrderedSubj} + +

    works only for "folder1" but not for "folder2". This is because the + comparison of the name of the folder is done with whatever is in between + "{", ";" or "}", so in the above rule you would be testing
    + "folder2" == " folder2". The extra space makes the difference. + The reason why the first rule does not fail is because + "folder2" << " folder2" is actually + true. If something ever fails this may be something to look into. + +

    + Here are a few examples of what we have talked about before. + +

    + _NICK_ == {lisa;kika} => _SAVE_{_NICK_/_NICK_}
    + This means that if the nick is lisa, it will + save the message in the folder "lisa/lisa", and if the nick + is "kika", it will save the message in the folder "kika/kika" + +

    + _FOLDER_ == {Lynx} -> lynx
    + This, is an abbreviation of the following rule:
    + _FOLDER_ == {Lynx} => _SAVE_{lynx}
    + (note the change in separator from "=>" to "->"). In the future + I will use that abbreviation. + +

    _FOLDER_ << {comp.mail.pine; pine-info; pine-alpha} -> pine
    + Any message in the folders "comp.mail.pine", "pine-info" or "pine-alpha" + will be saved to the folder "pine". + +

    _FROM_ << {Pine Master} -> pine
    + Any message whose From field contains + "Pine Master" will be saved in the folder pine. + +

    _FOLDER_ << {Lynx; pine-info; comp.mail.pine} => + _INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)}
    Use a + different index-format for the folders "Lynx", "pine-info" and + "comp.mail.pine", where the size is not present. + +

    _FOLDER_ == {Lynx;pine-info} => _REPLY_{*** _FROM_ (_ADDRESS_) + wrote in the _FOLDER_ list _SMARTDATE_("Today" "today" "on + _LONGDATE_"):}
    If a message is in one of the incoming folders "Lynx" + or "pine-info", create a reply-leadin-string that acknowledges that. Note + the absence of "," in the function _SMARTDATE_. For example answering to a + message in the pine-info list would look like: + +

    + *** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today: + +

    + However replying for a message in the Lynx list would look: + +

    + *** mattack@area.com (mattack@area.com) wrote in the Lynx list today: + +

    + If you write in more than one language you can use this feature to create + Reply-leadin-strings in different languages. + +

    Note that at least for people you can create particular + reply-leadin-string using the role features, but it does not work as this + one does. This seems to be the right way to do it. + +

    _FOLDER_ << {Lynx; comp.mail.pine; pine_info; pine-alpha} => + _SORT_{OrderedSubj}
    This means upon opening, sort the folders "Lynx", + "comp.mail.pine", etc in ordered subject. All the others use the default + sort order. You can not sort in reverse in this form. The possible + arguments of this function are listed in the definition of the + default-sort-rule (Arrival, scorE, siZe, etc). + +

    The last examples use the function _TRIM_ which has a special form. + This function can only be used in the index list. + +

    _FOLDER_ << {Lynx} => _SUBJECT_ := _TRIM_{lynx-dev }
    In + the folder "Lynx" eliminate from the subject the string "lynx-dev " (with + the space at the end). For example a message whose subject is "Re: + lynx-dev unvisited Visited Links", would be shown in the index with + subject: "Re: unvisited Visited Links", making the subject shorter and + giving the same information. + +

    _FROM_ >> {Name (Comment)} => _FROM_ := + _TRIM_{ (Comment)}
    Remove the part " (Comment)" + from the _FROM_, so when displaying in the index the real From "Name" + will appear. + +

    _SUBJECT_ == {} => _RESUB_{Re: your mail without subject} + If there is no subject in the message, use the subject "Re: your mail + wiyhout subject" as a subject for the reply message. + +

    You can add more complexity to your rules by checking more than one + conditions before a rule is executed. More than one condition can be + checked by separating different conditions by the && (and) separator, + or using the || (or) separator. For example we could have a rule that + saves all + messages in inbox from Rubye, to the Personal folder, as + +

    _FOLDER_ == {INBOX} && _FROM_ >> {Rubye} => _SAVE_{Personal} + +

    We could also have a rule that is triggered by an "or" + condition by, sat for messages from Andres or messages in the index + to trigger a specific reply leadin string. + +

    _FOLDER_ == {INBOX} || _FROM_ >> {Andres} => _REPLY_{You wrote:} + +

    Observe that the construction + +

    _TOKEN_ == {value1} || _TOKEN_ == {value2} + +

    can be shortened to + +

    _TOKEN_ == {value1;value2} + +

    Round parentheses can be used to group some conditions, for example + +

    (_FROM_ >> {Andres} && _FOLDER_ == {INBOX}) || _FROM_ >> {Rubye} + + +

    You can also list your index by nick, in the following way:
    + _NICK_ != {} => _FROM_ := _REPLACE_{_NICK_} + +

    + If you want to open the folder "pine-info" in the first non-read message + use the rule:
    + _FOLDER_ == {pine-info} => _STARTUP_{first-unseen} + +

    + If you want to move your deleted messages to a folder, called "Trash", use + the following rule:
    + _FLAG_ >> {D} -> Trash + +

    + The reason why the above test is not "_FLAG_ == {D}" is because that would mean + that this is the only flag set in the message. It's better to test by containment in this case. + +

    If you want to use a specific signature when you are in a specific collection + use the following rule:
    + _COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature} + +

    Finally about the question of which rule will be executed. Only the + first rule that matches will be executed. It is important to notice though + that "saving" rules do not compete with "sorting" rules. So the first + "saving" rule that matches will be executed in the case of saving and so + on. + +

    +

    + <End of help on this topic> + + ====== h_config_char_set ===== *************** *** 27005,27010 **** --- 28035,28110 ---- <End of help on this topic> + ====== h_config_thread_display_style_rule ===== + + + OPTION: Threading-Display-Style-Rule + + +

    OPTION: Threading-Display-Style-Rule

    + + This option is very similar to + , but it is a rule which specifies the + display styles for a thread that you want displayed in a specific + folder or collection. +

    + The token to be used in this function is _THREADSTYLE_. Here there is + an example of its use +

    + _FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like} +

    + The values that can be given for the _THREADSTYLE_ function are the + values of the threading-display-style function, which can be found + listed in the threading-display-style + configuration option. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    +

    + <End of help on this topic> + + + ====== h_config_thread_index_style_rule ===== + + + OPTION: Threading-Index-Style-Rule + + +

    OPTION: Threading-Index-Style-Rule

    + + This option is very similar to + , but it is a rule which specifies the + index styles for a thread that you want displayed in a specific + folder or collection. +

    + The token to be used in this function is _THREADINDEX_. Here there is + an example of its use +

    + _FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads} +

    + The values that can be given for the _THREADINDEX_ function are the + values of the threading-index-display function, which can be found + listed in the + configuration option. + +

    This configuration option is just one of many that allow you to + override the value of some global configurations within Alpine. There is a + help text explaining how to define all of them, which you can read by + following this link. + +

    +

    + <End of help on this topic> + + ====== h_config_pruning_rule ===== *************** *** 30595,30600 **** --- 31695,31723 ---- them as deleted in the INBOX. Messages in the INBOX marked with an "N" (meaning New, or unseen) are not affected.

    +

    + <End of help on this topic> + + + ====== h_config_auto_read_msgs_rules ===== + + + FEATURE: auto-move-read-msgs-using-rules + + +

    FEATURE: auto-move-read-msgs-using-rules

    + This feature controls an aspect of Alpine's behavior upon quitting. If set, + and the + "" + option is also set, then Alpine will automatically transfer all read + messages to the designated folder using the rules that you have defined in + your + "" and mark + them as deleted in the INBOX. Messages in the INBOX marked with an + "N" (meaning New, or unseen) are not affected. +

    diff -rc alpine-2.21/pith/reply.c alpine-2.21.rules/pith/reply.c *** alpine-2.21/pith/reply.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/reply.c Sun Feb 5 16:15:22 2017 *************** *** 47,52 **** --- 47,54 ---- #include "../pith/mailcmd.h" #include "../pith/margin.h" #include "../pith/smime.h" + #include "../pith/copyaddr.h" + #include "../pith/rules.h" /* *************** *** 864,871 **** reply_quote_str(ENVELOPE *env) { char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1]; ! strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; /* set up the prefix to quote included text */ --- 866,892 ---- reply_quote_str(ENVELOPE *env) { char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1]; + char reply_string[MAX_PREFIX+1]; ! { RULE_RESULT *rule; ! rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env); ! if (rule){ ! strncpy(reply_string,rule->result,sizeof(reply_string)); ! reply_string[sizeof(reply_string)-1] = '\0'; ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){ ! strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1); ! reply_string[sizeof(reply_string)-1] = '\0'; ! } ! else ! strncpy(reply_string,"> ",sizeof("> ")); ! } ! ! strncpy(buf, reply_string, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; /* set up the prefix to quote included text */ *************** *** 917,926 **** int reply_quote_str_contains_tokens(void) { ! return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] && ! (strstr(ps_global->VAR_REPLY_STRING, from_token) || ! strstr(ps_global->VAR_REPLY_STRING, nick_token) || ! strstr(ps_global->VAR_REPLY_STRING, init_token))); } --- 938,966 ---- int reply_quote_str_contains_tokens(void) { ! char *reply_string; ! ! reply_string = (char *) malloc( 80*sizeof(char)); ! { RULE_RESULT *rule; ! rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE, NULL); ! if (rule){ ! reply_string = cpystr(rule->result); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){ ! strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1); ! reply_string[sizeof(reply_string)-1] = '\0'; ! } ! else ! reply_string = cpystr("> "); ! } ! return(reply_string && reply_string[0] && ! (strstr(reply_string, from_token) || ! strstr(reply_string, nick_token) || ! strstr(reply_string, init_token))); } *************** *** 1385,1390 **** --- 1425,1434 ---- buf[0] = '\0'; switch(type){ + case iFfrom: + addr = env && env->sparep ? env->sparep : NULL; + break; + case iFrom: addr = env ? env->from : NULL; break; *************** *** 1797,1818 **** break; case iFrom: case iTo: case iCc: case iSender: case iRecips: case iInit: get_addr_data(env, type, buf, maxlen); break; ! case iRoleNick: ! if(role && role->nick){ ! strncpy(buf, role->nick, maxlen); ! buf[maxlen] = '\0'; } break; case iNewLine: if(maxlen >= strlen(NEWLINE)){ strncpy(buf, NEWLINE, maxlen); --- 1841,2034 ---- break; + case iProcid: + if(ps_global->procid){ + strncpy(buf, ps_global->procid, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iRole: + if (ps_global->role){ + strncpy(buf, ps_global->role, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iRoleNick: + if(role && role->nick){ + strncpy(buf, role->nick, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iPkey: + if(ps_global->pressed_key){ + strcpy(buf, ps_global->pressed_key); + buf[maxlen] = '\0'; + } + break; + + case iScreen: + if(ps_global->screen_name){ + strncpy(buf, ps_global->screen_name, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iFfrom: case iFrom: case iTo: case iCc: case iSender: case iRecips: case iInit: + if (env) get_addr_data(env, type, buf, maxlen); break; ! case iFolder: ! if(ps_global->cur_folder){ ! strncpy(buf,ps_global->cur_folder, maxlen); ! buf[maxlen] = '\0'; ! } ! break; ! ! case iCollection: ! if(ps_global->context_current->nickname){ ! strncpy(buf,ps_global->context_current->nickname, maxlen); ! buf[maxlen] = '\0'; ! } ! break; ! ! case iFlag: ! {MAILSTREAM *stream = ps_global->mail_stream; ! MSGNO_S *msgmap = NULL; ! long msgno; ! MESSAGECACHE *mc; ! strncpy(buf, "_FLAG_", maxlen); /* default value */ ! if (stream){ ! msgmap = sp_msgmap(stream); ! msgno = mn_m2raw(msgmap, rules_cursor_pos(stream)); ! if (msgno > 0L) mc = stream ? mail_elt(stream, msgno) : NULL; ! if (mc) ! sprintf(buf,"%s%s%s%s",mc->flagged ? "*" : "", ! mc->recent ? (mc->seen ? "R" : "N") : (mc->seen) ? "R" : "U", ! mc->answered ? "A" : "", ! mc->deleted ? "D" : "" ); ! } ! buf[maxlen] = '\0'; ! } ! break; ! ! case iAltAddress: ! if(ps_global->VAR_ALT_ADDRS != NULL ! && ps_global->VAR_ALT_ADDRS[0] != NULL){ ! size_t len; ! int i, j; ! ! for(i = 0, len = 0; len < maxlen && ps_global->VAR_ALT_ADDRS[i]; i++){ ! for(j = 0; len < maxlen && ps_global->VAR_ALT_ADDRS[i][j] != '\0'; j++){ ! if(ps_global->VAR_ALT_ADDRS[i][j] == ';') ! buf[len++] = '\\'; ! buf[len++] = ps_global->VAR_ALT_ADDRS[i][j]; ! } ! if(len < maxlen){ ! if(ps_global->VAR_ALT_ADDRS[i+1] != NULL) ! buf[len++] = ';'; ! else ! buf[len++] = '\0'; ! } ! } ! buf[maxlen] = '\0'; ! } ! break; ! ! case iNick: ! case iFccFrom: ! case iFccSender: ! if (env){ ! ADDRESS *tmp_adr; ! ! switch(type){ ! case iNick: ! tmp_adr = env->from ? copyaddr(env->from) ! : env->sender ? copyaddr(env->sender) : NULL; ! break; ! case iFccFrom: ! tmp_adr = env->from ? copyaddr(env->from) : NULL; ! break; ! case iFccSender: ! tmp_adr = env->sender ? copyaddr(env->sender) : NULL; ! break; ! default: alpine_panic("Unhandled Rules case (01)"); ! } ! if(type == iNick) ! get_nickname_from_addr(tmp_adr, buf, maxlen); ! else ! get_fcc_from_addr(tmp_adr, buf, maxlen); ! mail_free_address(&tmp_adr); } break; + case iAddressSender: + case iAddressCc: + case iAddressRecip: + case iAddressTo: + case iFadd: + { + int plen = 0; /* partial length */ + ADDRESS *sparep2 = (type == iAddressTo || type == iAddressRecip) + ? ((env && env->to) + ? copyaddrlist(env->to) + : NULL) + : (type == iAddressCc) + ? ((env && env->cc) + ? copyaddrlist(env->cc) + : NULL) + : (type == iAddressSender) + ? ((env && env->sender) + ? copyaddr(env->sender) + : NULL) + : ((env && env->sparep) + ? copyaddr((ADDRESS *)env->sparep) + : NULL); + ADDRESS *sparep; + + if (type == iAddressRecip){ + ADDRESS *last_to = NULL; + + for(last_to = sparep2;last_to && last_to->next; last_to= last_to->next); + + /* Make the end of To list point to cc list */ + if(last_to) + last_to->next = (env && env->cc ? copyaddrlist(env->cc) : NULL); + + } + sparep = sparep2; + for(; sparep ; sparep = sparep->next) + if(sparep && sparep->mailbox && sparep->mailbox[0] && + (plen ? plen + 1 : plen) + strlen(sparep->mailbox) <= maxlen){ + if (plen == 0) + strcpy(buf, sparep->mailbox); + else{ + strcat(buf, " "); + strcat(buf, sparep->mailbox); + } + if(sparep->host && + sparep->host[0] && + sparep->host[0] != '.' && + strlen(buf) + strlen(sparep->host) + 1 <= maxlen){ + strcat(buf, "@"); + strcat(buf, sparep->host); + } + plen = strlen(buf); + } + mail_free_address(&sparep2); + } + + break; + case iNewLine: if(maxlen >= strlen(NEWLINE)){ strncpy(buf, NEWLINE, maxlen); *************** *** 1840,1845 **** --- 2056,2066 ---- break; + case iLcc: /* fake it, there are not enough spare pointers */ + if (env && env->date) + sprintf(buf,"%s",env->date); + break; + case iNews: case iCurNews: get_news_data(env, type, buf, maxlen); *************** *** 1889,1894 **** --- 2110,2123 ---- break; + case iOpeningText: + case iOpeningTextNQ: + if(env && env->sparep){ + strncpy(buf, ((SPAREP_S *)env->sparep)->value, maxlen); + buf[maxlen] = '\0'; + } + break; + case iSubject: case iShortSubject: if(env && env->subject){ *************** *** 1951,1957 **** if(!env) return; ! strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); buf[MAX_DELIM] = '\0'; /* preserve exact default behavior from before */ if(!strcmp(buf, DEFAULT_REPLY_INTRO)){ --- 2180,2197 ---- if(!env) return; ! { RULE_RESULT *rule; ! rule = get_result_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO, env); ! if(rule){ ! strncpy(buf, rule->result, MAX_DELIM); ! if (rule->result) ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else ! strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); ! } ! buf[MAX_DELIM] = '\0'; /* preserve exact default behavior from before */ if(!strcmp(buf, DEFAULT_REPLY_INTRO)){ *************** *** 2210,2215 **** --- 2450,2456 ---- { size_t l; char *p, buftmp[MAILTMPLEN]; + RULE_RESULT *rule; if(!env) return(NULL); *************** *** 2217,2225 **** dprint((9, "checking subject: \"%s\"\n", env->subject ? env->subject : "NULL")); ! if(env->subject && env->subject[0]){ /* add (fwd)? */ ! snprintf(buftmp, sizeof(buftmp), "%s", env->subject); ! buftmp[sizeof(buftmp)-1] = '\0'; /* decode any 8bit (copy to the temp buffer if decoding doesn't) */ if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp) --- 2458,2476 ---- dprint((9, "checking subject: \"%s\"\n", env->subject ? env->subject : "NULL")); ! buftmp[0] = '\0'; ! ps_global->procid = cpystr("fwd-subject"); ! if (rule = get_result_rule(V_FORWARD_RULES,FOR_COMPOSE, env)){ ! snprintf(buftmp, sizeof(buftmp), "%s", rule->result); ! fs_give((void **)&rule->result); ! fs_give((void **)&rule); ! } ! else if(env->subject) ! snprintf(buftmp, sizeof(buftmp), "%s", env->subject); ! buftmp[sizeof(buftmp)-1] = '\0'; ! fs_give((void **)&ps_global->procid); ! ! if(buftmp[0]){ /* add (fwd)? */ /* decode any 8bit (copy to the temp buffer if decoding doesn't) */ if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp) diff -rc alpine-2.21/pith/rules.c alpine-2.21.rules/pith/rules.c *** alpine-2.21/pith/rules.c Sun Feb 5 16:15:22 2017 --- alpine-2.21.rules/pith/rules.c Sun Feb 5 16:15:22 2017 *************** *** 0 **** --- 1,1416 ---- + /* 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; + } diff -rc alpine-2.21/pith/rules.h alpine-2.21.rules/pith/rules.h *** alpine-2.21/pith/rules.h Sun Feb 5 16:15:22 2017 --- alpine-2.21.rules/pith/rules.h Sun Feb 5 16:15:22 2017 *************** *** 0 **** --- 1,151 ---- + /* Included file rules.h */ + + #ifndef PITH_RULES_INCLUDED + #define PITH_RULES_INCLUDED + + #include "../pith/conftype.h" + #include "../pith/detoken.h" + #include "../pith/indxtype.h" + #include "../pith/rulestype.h" + + /* Exported prototypes */ + + void create_rule_list (struct variable *); + SPAREP_S *get_sparep_for_rule(char *, int); + void free_sparep_for_rule(void **); + void free_parsed_rule_list (PRULELIST_S **); + RULE_RESULT *get_result_rule (int, int, ENVELOPE *); + char *get_rule_result (int , char *, int); + char *process_rule (RULE_S *, int, ENVELOPE *); + char **functions_for_token (char *); + RULELIST *get_rulelist_from_code (int, PRULELIST_S *); + RULE_S *get_rule (RULELIST *, int); + int condition_contains_token (CONDITION_S *, char *); + int context_for_function (char *); + ENVELOPE *rules_fetchenvelope(INDEXDATA_S *idata, int *we_clear); + + /* Separators: + * + * A separator is a string that separates the rule condition with the rule + * action. Below is the list of separators + * + */ + + #define SAVE_TO_SEP "->" + #define APPLY_SEP "=>" + + /*------- Definitions of tokens -------*/ + /*------ Keep the list alphabetically sorted, thanks -------*/ + + #define ADDR_TOKEN "_ADDRESS_" + #define ADDCC_TOKEN "_ADDRESSCC_" + #define ADDRECIP_TOKEN "_ADDRESSRECIPS_" + #define ADDTO_TOKEN "_ADDRESSTO_" + #define BCC_TOKEN "_BCC_" + #define CC_TOKEN "_CC_" + #define COLLECT_TOKEN "_COLLECTION_" + #define FCCF_TOKEN "_FCCFROM_" + #define FCCS_TOKEN "_FCCSENDER_" + #define FLAG_TOKEN "_FLAG_" + #define FOLDER_TOKEN "_FOLDER_" + #define FADDRESS_TOKEN "_FORWARDADDRESS_" + #define FFROM_TOKEN "_FORWARDFROM_" + #define FROM_TOKEN "_FROM_" + #define KEY_TOKEN "_PKEY_" + #define LCC_TOKEN "_LCC_" + #define NICK_TOKEN "_NICK_" + #define OTEXT_TOKEN "_OPENINGTEXT_" + #define OTEXTNQ_TOKEN "_OPENINGTEXTNQ_" + #define PROCID_TOKEN "_PROCID_" + #define ROLE_TOKEN "_ROLE_" + #define SCREEN_TOKEN "_SCREEN_" + #define SEND_TOKEN "_SENDER_" + #define SUBJ_TOKEN "_SUBJECT_" + #define THDDSPSTY_TOKEN "_THREADSTYLE_" + #define THDNDXSTY_TOKEN "_THREADINDEX_" + #define TO_TOKEN "_TO_" + + /*------ Definitions of relational operands -------------*/ + + typedef struct { + char *value; + TestType ttype; + int (*execute)(); + } REL_TOKEN; + + /* Relational Operands */ + #define AND_REL "&&" /* For putting more than one condition */ + #define IN_REL "<<" /* For belonging relation */ + #define NI_REL ">>" /* For contain relation */ + #define NOT_IN_REL "!<" /* Negation of IN_REL */ + #define NOT_NI_REL "!>" /* Negation of NI_REL */ + #define EQ_REL "==" /* Test of equality */ + #define NOT_EQ_REL "!=" /* Test of inequality */ + #define OPEN_SET "{" /* Braces to open a set */ + #define CLOSE_SET "}" /* Braces to close a set*/ + + /*--- Context in which these variables can be used ---*/ + + typedef struct use_context { + char *name; + int what_for; + } USE_IN_CONTEXT; + + + static USE_IN_CONTEXT tokens_use[] = { + {NICK_TOKEN, FOR_SAVE}, + {FROM_TOKEN, FOR_SAVE}, + {OTEXT_TOKEN, FOR_SAVE|FOR_FOLDER}, + {OTEXTNQ_TOKEN, FOR_SAVE|FOR_FOLDER}, + {ROLE_TOKEN, FOR_COMPOSE}, + {FOLDER_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_THREAD|FOR_COMPOSE}, + {SUBJ_TOKEN, FOR_SAVE|FOR_FOLDER|FOR_COMPOSE}, + {FLAG_TOKEN, FOR_SAVE|FOR_FLAG}, + {COLLECT_TOKEN, FOR_SAVE|FOR_COMPOSE|FOR_FOLDER|FOR_THREAD}, + {THDDSPSTY_TOKEN, FOR_THREAD}, + {THDNDXSTY_TOKEN, FOR_THREAD}, + {ADDR_TOKEN, FOR_SAVE|FOR_FOLDER}, + {TO_TOKEN, FOR_SAVE}, + {ADDTO_TOKEN, FOR_SAVE|FOR_COMPOSE}, + {ADDCC_TOKEN, FOR_SAVE|FOR_COMPOSE}, + {ADDRECIP_TOKEN, FOR_SAVE|FOR_COMPOSE}, + {SCREEN_TOKEN, FOR_KEY}, + {KEY_TOKEN, FOR_KEY}, + {SEND_TOKEN, FOR_SAVE}, + {CC_TOKEN, FOR_SAVE}, + {BCC_TOKEN, FOR_COMPOSE}, + {LCC_TOKEN, FOR_COMPOSE}, + {FFROM_TOKEN, FOR_COMPOSE}, + {FADDRESS_TOKEN, FOR_COMPOSE}, + {NULL, FOR_NOTHING} + }; + + + typedef struct { + char *name; + char* (*execute)(); + int what_for; + } RULE_FCN; + + #define COMMAND_FCN "_COMMAND_" + #define COPY_FCN "_COPY_" + #define EXEC_FCN "_EXEC_" + #define INDEX_FCN "_INDEX_" + #define REPLACE_FCN "_REPLACE_" + #define REPLYSTR_FCN "_RESTR_" + #define REPLY_FCN "_REPLY_" + #define RESUB_FCN "_RESUB_" + #define REXTRIM_FCN "_REXTRIM_" + #define SAVE_FCN "_SAVE_" + #define SIGNATURE_FCN "_SIGNATURE_" + #define SMTP_FCN "_SMTP_" + #define SORT_FCN "_SORT_" + #define STARTUP_FCN "_STARTUP_" + #define THRDSTYLE_FCN "_THREADSTYLE_" + #define THRDINDEX_FCN "_THREADINDEX_" + #define TRIM_FCN "_TRIM_" + + #define STRICTLY 0x1 + #define RELAXED 0x2 + + #endif /* PITH_RULES_INCLUDED */ diff -rc alpine-2.21/pith/rulestype.h alpine-2.21.rules/pith/rulestype.h *** alpine-2.21/pith/rulestype.h Sun Feb 5 16:15:22 2017 --- alpine-2.21.rules/pith/rulestype.h Sun Feb 5 16:15:22 2017 *************** *** 0 **** --- 1,85 ---- + #ifndef PITH_RULESTYPE_INCLUDED + #define PITH_RULESTYPE_INCLUDED + + typedef struct rule { + char *result; /* The result of the rule */ + int number; /* The number of the rule that succeded, -1 if not */ + } RULE_RESULT; + + typedef struct { + char *value; + int type; + } RULE_ACTION; + + + #define TOKEN_VALUE struct tokenvalue_s + #define CONDITION_S struct condition_s + #define RULEACTION_S struct ruleaction_s + #define RULE_S struct rule_s + #define RULELIST struct rulelist_s + #define PRULELIST_S struct parsedrulelist_s + + #define FREEREGEX 1 + + TOKEN_VALUE { + char *testxt; + void *voidtxt; + int codefcn; + TOKEN_VALUE *next; + }; + + typedef enum {Equal, Subset, Includes, NotEqual, NotSubset, NotIncludes, EndTypes} TestType; + + typedef enum {And, Or, ParOpen, ParClose, Condition} CondType; + + typedef struct condvalue_s { + char *tname; /* tname ttype {value} */ + TestType ttype; /* type of rule */ + TOKEN_VALUE *value; /* value to check against */ + } CONDVALUE_S; + + CONDITION_S { + void *cndrule; /* text in condition */ + CondType cndtype; /* type of object */ + CONDITION_S *next; /* next condition to test */ + }; + + #define COND(C) ((CONDVALUE_S *)((C)->cndrule)) + + RULEACTION_S { + char *token; /* token := function{value} or token = null */ + char *function; /* token := function{value} or simply function{value}*/ + TOKEN_VALUE *value; /* token := function{value} or simply function{value}*/ + int context; /* context in which this rule can be used */ + char* (*exec)(); + unsigned int is_trim:1; + unsigned int is_rextrim:1; + unsigned int is_replace:1; + }; + + RULE_S { + CONDITION_S *condition; + RULEACTION_S *action; + }; + + RULELIST { + RULE_S *prule; + RULELIST *next; + }; + + PRULELIST_S { + int varnum; /* number associated to the variable */ + RULELIST *rlist; + PRULELIST_S *next; + }; + + #define USE_RAW_SP 0x001 + #define PROCESS_SP 0x010 + + typedef struct sparep { + int flag; + char *value; + } SPAREP_S; + + + #endif /* PITH_RULESTYPE_INCLUDED */ diff -rc alpine-2.21/pith/save.c alpine-2.21.rules/pith/save.c *** alpine-2.21/pith/save.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/save.c Sun Feb 5 16:15:22 2017 *************** *** 954,960 **** *date = '\0'; rv = save_fetch_append(stream, mn_m2raw(msgmap, i), ! NULL, save_stream, save_folder, context, mc ? mc->rfc822_size : 0L, flags, date, so); if(flags) --- 954,960 ---- *date = '\0'; rv = save_fetch_append(stream, mn_m2raw(msgmap, i), ! NULL, save_stream, folder, context, mc ? mc->rfc822_size : 0L, flags, date, so); if(flags) diff -rc alpine-2.21/pith/send.c alpine-2.21.rules/pith/send.c *** alpine-2.21/pith/send.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/send.c Sun Feb 5 16:15:22 2017 *************** *** 44,49 **** --- 44,50 ---- #include "../pith/ablookup.h" #include "../pith/sort.h" #include "../pith/smime.h" + #include "../pith/rules.h" #include "../c-client/smtp.h" #include "../c-client/nntp.h" *************** *** 1742,1750 **** char error_buf[200], *error_mess = NULL, *postcmd; ADDRESS *a; ENVELOPE *fake_env = NULL; ! int addr_error_count, we_cancel = 0; long smtp_opts = 0L; ! char *verbose_file = NULL; BODY *bp = NULL; PINEFIELD *pf; BODY *origBody = body; --- 1743,1751 ---- char error_buf[200], *error_mess = NULL, *postcmd; ADDRESS *a; ENVELOPE *fake_env = NULL; ! int addr_error_count, we_cancel = 0, choice, num_rules = 0, added_rules = -1; long smtp_opts = 0L; ! char *verbose_file = NULL, **smtp_list; BODY *bp = NULL; PINEFIELD *pf; BODY *origBody = body; *************** *** 1899,1918 **** * OK, who posts what? We tried an mta_handoff above, but there * was either none specified or we decided not to use it. So, * if there's an smtp-server defined anywhere, */ ! if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){ ! /*---------- SMTP ----------*/ ! dprint((4, "call_mailer: via TCP (%s)\n", ! alt_smtp_servers[0])); ! TIME_STAMP("smtp-open start (tcp)", 1); ! sending_stream = smtp_open(alt_smtp_servers, smtp_opts); } ! else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] ! && ps_global->VAR_SMTP_SERVER[0][0]){ ! /*---------- SMTP ----------*/ ! dprint((4, "call_mailer: via TCP\n")); ! TIME_STAMP("smtp-open start (tcp)", 1); ! sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts); } else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){ char *cmdlist[2]; --- 1900,1948 ---- * OK, who posts what? We tried an mta_handoff above, but there * was either none specified or we decided not to use it. So, * if there's an smtp-server defined anywhere, + * First we check for rules and make a list using the rules. */ ! if(ps_global->VAR_SMTP_RULES && ps_global->VAR_SMTP_RULES[0] ! && ps_global->VAR_SMTP_RULES[0][0]) ! while (ps_global->VAR_SMTP_RULES[num_rules]) num_rules++; ! ! if(num_rules){ ! int i, j; ! ! added_rules = 0; ! smtp_list = (char **) fs_get ((num_rules + 1)*sizeof(char*)); ! for (i = 0, j = 0; i < num_rules; i++){ ! RULELIST *rule = get_rulelist_from_code(V_SMTP_RULES, ! ps_global->rule_list); ! RULE_S *prule = get_rule(rule, i); ! if(prule){ ! char *rule_result = process_rule(prule, FOR_COMPOSE, header->env); ! if(rule_result && *rule_result){ ! smtp_list[j++] = cpystr(rule_result); ! added_rules++; ! } ! } ! } ! } ! ! if (added_rules < 0){ ! smtp_list = (char **) fs_get (sizeof(char*)); ! added_rules = 0; } ! smtp_list[added_rules] = NULL; ! ! choice = smtp_list && smtp_list[0] && smtp_list[0][0] ? 3 : ! (alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0] ? 2 : ! (ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0] ! && ps_global->VAR_SMTP_SERVER[0][0] ? 1 : -1)); ! ! if(choice > 0){ ! /*---------- SMTP ----------*/ ! dprint((4, "call_mailer: via TCP (%s)\n",smtp_list[0])); ! TIME_STAMP("smtp-open start (tcp)", 1); ! sending_stream = smtp_open(choice == 3 ? smtp_list ! : (choice == 2 ? alt_smtp_servers ! : ps_global->VAR_SMTP_SERVER), smtp_opts); } else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){ char *cmdlist[2]; diff -rc alpine-2.21/pith/sort.c alpine-2.21.rules/pith/sort.c *** alpine-2.21/pith/sort.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/sort.c Sun Feb 5 16:15:22 2017 *************** *** 30,36 **** #include "../pith/signal.h" #include "../pith/busy.h" #include "../pith/icache.h" ! /* * global place to store mail_sort and mail_thread results --- 30,36 ---- #include "../pith/signal.h" #include "../pith/busy.h" #include "../pith/icache.h" ! #include "../pith/rules.h" /* * global place to store mail_sort and mail_thread results *************** *** 686,692 **** PAT_S *pat; SortOrder the_sort_order; int sort_is_rev; ! /* set default order */ the_sort_order = ps_global->def_sort; sort_is_rev = the_sort_order == SortThread --- 686,706 ---- PAT_S *pat; SortOrder the_sort_order; int sort_is_rev; ! char *rule_result; ! SortOrder new_sort = EndofList; ! int is_rev; ! ! rule_result = get_rule_result(FOR_SORT, ps_global->cur_folder, V_SORT_RULES); ! if (rule_result && *rule_result){ ! new_sort = (SortOrder) translate(rule_result, 1); ! is_rev = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1; ! fs_give((void **)&rule_result); ! } ! if (new_sort != EndofList){ ! the_sort_order = new_sort; ! sort_is_rev = is_rev; ! } ! else{ /* set default order */ the_sort_order = ps_global->def_sort; sort_is_rev = the_sort_order == SortThread *************** *** 704,713 **** : pat->action->revsort; } } ! if(the_sort_order == SortThread && !(flags & SRT_MAN)) ps_global->thread_cur_sort = ps_global->thread_def_sort; sort_folder(ps_global->mail_stream, ps_global->msgmap, the_sort_order, sort_is_rev, flags, 1); } --- 718,763 ---- : pat->action->revsort; } } ! } if(the_sort_order == SortThread && !(flags & SRT_MAN)) ps_global->thread_cur_sort = ps_global->thread_def_sort; sort_folder(ps_global->mail_stream, ps_global->msgmap, the_sort_order, sort_is_rev, flags, 1); } + + SortOrder translate(char *order, int is_rev) + { + int rev = 0; + if (!strncmp(order,"tHread", 6) + || (rev = !strncmp(order,"Reverse tHread", 14))) + return is_rev || rev ? SortThread : EndofList; + if (!strncmp(order,"OrderedSubj", 11) + || (rev = !strncmp(order,"Reverse OrderedSubj", 19))) + return is_rev || rev ? SortSubject2 : EndofList; + if (!strncmp(order,"Subject", 7) + || (rev = !strncmp(order,"Reverse SortSubject", 15))) + return is_rev || rev ? SortSubject : EndofList; + if (!strncmp(order,"Arrival", 7) + || (rev = !strncmp(order,"Reverse Arrival", 15))) + return is_rev || rev ? SortArrival : EndofList; + if (!strncmp(order,"From", 4) + || (rev = !strncmp(order,"Reverse From", 12))) + return is_rev || rev ? SortFrom : EndofList; + if (!strncmp(order,"To", 2) + || (rev = !strncmp(order,"Reverse To", 10))) + return is_rev || rev ? SortTo : EndofList; + if (!strncmp(order,"Cc", 2) + || (rev = !strncmp(order,"Reverse Cc", 10))) + return is_rev || rev ? SortCc : EndofList; + if (!strncmp(order,"Date", 4) + || (rev = !strncmp(order,"Reverse Date", 12))) + return is_rev || rev ? SortDate : EndofList; + if (!strncmp(order,"siZe", 4) + || (rev = !strncmp(order,"Reverse siZe", 12))) + return is_rev || rev ? SortSize : EndofList; + if (!strncmp(order,"scorE", 5) + || (rev = !strncmp(order,"Reverse scorE", 13))) + return is_rev || rev ? SortScore : EndofList; + return EndofList; + } diff -rc alpine-2.21/pith/sort.h alpine-2.21.rules/pith/sort.h *** alpine-2.21/pith/sort.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/sort.h Sun Feb 5 16:15:22 2017 *************** *** 45,50 **** void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); int decode_sort(char *, SortOrder *, int *, int); void reset_sort_order(unsigned); ! #endif /* PITH_SORT_INCLUDED */ --- 45,50 ---- void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); int decode_sort(char *, SortOrder *, int *, int); void reset_sort_order(unsigned); ! SortOrder translate(char *, int); #endif /* PITH_SORT_INCLUDED */ diff -rc alpine-2.21/pith/state.c alpine-2.21.rules/pith/state.c *** alpine-2.21/pith/state.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/state.c Sun Feb 5 16:15:22 2017 *************** *** 33,39 **** #include "../pith/remote.h" #include "../pith/list.h" #include "../pith/smime.h" ! /* * Globals referenced throughout pine... --- 33,39 ---- #include "../pith/remote.h" #include "../pith/list.h" #include "../pith/smime.h" ! #include "../pith/rules.h" /* * Globals referenced throughout pine... *************** *** 239,244 **** --- 239,247 ---- if((*pps)->msgmap) msgno_give(&(*pps)->msgmap); + if((*pps)->rule_list) + free_parsed_rule_list(&(*pps)->rule_list); + free_vars(*pps); fs_give((void **) pps); diff -rc alpine-2.21/pith/state.h alpine-2.21.rules/pith/state.h *** alpine-2.21/pith/state.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/state.h Sun Feb 5 16:15:22 2017 *************** *** 33,39 **** #include "../pith/stream.h" #include "../pith/color.h" #include "../pith/user.h" ! /* * Printing control structure --- 33,39 ---- #include "../pith/stream.h" #include "../pith/color.h" #include "../pith/user.h" ! #include "../pith/rulestype.h" /* * Printing control structure *************** *** 105,110 **** --- 105,115 ---- MAILSTREAM *mail_stream; /* ptr to current folder stream */ MSGNO_S *msgmap; /* ptr to current message map */ + char screen_name[10]; /* name of current screen */ + char *role; /* role used when composing */ + char *procid; /* procedure id when needed */ + int exiting; + unsigned read_predicted:1; char cur_folder[MAXPATH+1]; *************** *** 349,354 **** --- 354,360 ---- struct { char *(*display_filter)(char *, STORE_S *, gf_io_t, FILTLIST_S *); char *(*display_filter_trigger)(BODY *, char *, size_t); + char *(*exec_rule)(char *, gf_io_t, gf_io_t); } tools; KEYWORD_S *keywords; *************** *** 359,364 **** --- 365,373 ---- char last_error[500]; INIT_ERR_S *init_errs; + PRULELIST_S *rule_list; + char *pressed_key; + PRINT_S *print; #ifdef SMIME diff -rc alpine-2.21/pith/string.c alpine-2.21.rules/pith/string.c *** alpine-2.21/pith/string.c Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/string.c Sun Feb 5 16:15:22 2017 *************** *** 20,25 **** --- 20,26 ---- string.c Misc extra and useful string functions - rplstr replace a substring with another string + - collspaces consecutive spaces are reduced to one space. - sqzspaces Squeeze out the extra blanks in a string - sqznewlines Squeeze out \n and \r. - removing_trailing_white_space *************** *** 131,136 **** --- 132,162 ---- return(x3); } + /*---------------------------------------------------------------------- + collapse blank space + ----------------------------------------------------------------------*/ + void + collspaces(char *string) + { + char *p = string; + int only_one_space = 0; + + if(!string) + return; + + for(;isspace(*p); p++); + + while(*string = *p++) + if(!isspace((unsigned char)*string)){ + only_one_space = 0; + string++; + } + else if(!only_one_space){ + string++; + only_one_space++; + } + *string = '\0'; + } /*---------------------------------------------------------------------- *************** *** 2983,2985 **** --- 3009,3043 ---- if(i < len) rn[i] = '\0'; rn[len-1] = '\0'; } + + + void + removing_extra_stuff(string) + char *string; + { + char *p = NULL; + int change = 0, length = 0; + + + if(!string) + return; + + for(; *string; string++, length++) + p = ((unsigned char)*string != ',') ? NULL : (!p) ? string : p; + + if(p) + *p = '\0'; + + string -= length; + for (; *string; string++){ + if (change){ + *string = ' '; + change = 0; + } + if ((((unsigned char)*string == ' ') || + ((unsigned char)*string == ',')) && + ((unsigned char)*(string + 1) == ',')) + change++; + } + } + diff -rc alpine-2.21/pith/string.h alpine-2.21.rules/pith/string.h *** alpine-2.21/pith/string.h Sun Feb 5 16:02:36 2017 --- alpine-2.21.rules/pith/string.h Sun Feb 5 16:15:22 2017 *************** *** 87,92 **** --- 87,93 ---- /* exported protoypes */ char *rplstr(char *, size_t, int, char *); + void collspaces(char *); void sqzspaces(char *); void sqznewlines(char *); void removing_leading_white_space(char *); *************** *** 94,99 **** --- 95,101 ---- void replace_tabs_by_space(char *); void removing_leading_and_trailing_white_space(char *); int removing_double_quotes(char *); + void removing_extra_stuff (char *); char *skip_white_space(char *); char *skip_to_white_space(char *); char *removing_quotes(char *);