1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: pattern.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
3 #endif
4 /*
5 * ========================================================================
6 * Copyright 2013-2021 Eduardo Chappa
7 * Copyright 2006-2009 University of Washington
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * ========================================================================
16 */
17
18 #include "../pith/headers.h"
19 #include "../pith/pattern.h"
20 #include "../pith/state.h"
21 #include "../pith/conf.h"
22 #include "../pith/string.h"
23 #include "../pith/msgno.h"
24 #include "../pith/status.h"
25 #include "../pith/list.h"
26 #include "../pith/flag.h"
27 #include "../pith/tempfile.h"
28 #include "../pith/addrstring.h"
29 #include "../pith/search.h"
30 #include "../pith/mailcmd.h"
31 #include "../pith/filter.h"
32 #include "../pith/save.h"
33 #include "../pith/mimedesc.h"
34 #include "../pith/reply.h"
35 #include "../pith/folder.h"
36 #include "../pith/maillist.h"
37 #include "../pith/sort.h"
38 #include "../pith/copyaddr.h"
39 #include "../pith/pipe.h"
40 #include "../pith/list.h"
41 #include "../pith/news.h"
42 #include "../pith/util.h"
43 #include "../pith/sequence.h"
44 #include "../pith/detoken.h"
45 #include "../pith/busy.h"
46 #include "../pith/indxtype.h"
47 #include "../pith/mailindx.h"
48 #include "../pith/send.h"
49 #include "../pith/icache.h"
50 #include "../pith/ablookup.h"
51 #include "../pith/keyword.h"
52
53
54 /*
55 * Internal prototypes
56 */
57 void open_any_patterns(long);
58 void sub_open_any_patterns(long);
59 void sub_close_patterns(long);
60 int sub_any_patterns(long, PAT_STATE *);
61 PAT_LINE_S *parse_pat_lit(char *);
62 PAT_LINE_S *parse_pat_inherit(void);
63 PAT_S *parse_pat(char *);
64 void parse_patgrp_slash(char *, PATGRP_S *);
65 void parse_action_slash(char *, ACTION_S *);
66 ARBHDR_S *parse_arbhdr(char *);
67 char *next_arb(char *);
68 PAT_S *first_any_pattern(PAT_STATE *);
69 PAT_S *last_any_pattern(PAT_STATE *);
70 PAT_S *prev_any_pattern(PAT_STATE *);
71 PAT_S *next_any_pattern(PAT_STATE *);
72 int sub_write_patterns(long);
73 int write_pattern_file(char **, PAT_LINE_S *);
74 int write_pattern_lit(char **, PAT_LINE_S *);
75 int write_pattern_inherit(char **, PAT_LINE_S *);
76 char *data_for_patline(PAT_S *);
77 int charsets_present_in_msg(MAILSTREAM *, unsigned long, STRLIST_S *);
78 void collect_charsets_from_subj(ENVELOPE *, STRLIST_S **);
79 void collect_charsets_from_body(BODY *, STRLIST_S **);
80 SEARCHPGM *next_not(SEARCHPGM *);
81 SEARCHOR *next_or(SEARCHOR **);
82 void set_up_search_pgm(char *, PATTERN_S *, SEARCHPGM *);
83 void add_type_to_pgm(char *, PATTERN_S *, SEARCHPGM *);
84 void set_srch(char *, char *, SEARCHPGM *);
85 void set_srch_hdr(char *, char *, SEARCHPGM *);
86 void set_search_by_age(INTVL_S *, SEARCHPGM *, int);
87 void set_search_by_size(INTVL_S *, SEARCHPGM *);
88 int non_eh(char *);
89 void add_eh(char **, char **, char *, int *);
90 void set_extra_hdrs(char *);
91 int is_ascii_string(char *);
92 ACTION_S *combine_inherited_role_guts(ACTION_S *);
93 int move_filtered_msgs(MAILSTREAM *, MSGNO_S *, char *, int, char *);
94 void set_some_flags(MAILSTREAM *, MSGNO_S *, long, char **, char **, int, char *);
95
96
97 /*
98 * optional hook for external-program filter test
99 */
100 void (*pith_opt_filter_pattern_cmd)(char **, SEARCHSET *, MAILSTREAM *, long, INTVL_S *);
101
102
103 void
role_process_filters(void)104 role_process_filters(void)
105 {
106 int i;
107 MAILSTREAM *stream;
108 MSGNO_S *msgmap;
109
110 for(i = 0; i < ps_global->s_pool.nstream; i++){
111 stream = ps_global->s_pool.streams[i];
112 if(stream && pine_mail_ping(stream)){
113 msgmap = sp_msgmap(stream);
114 if(msgmap)
115 reprocess_filter_patterns(stream, msgmap, MI_REFILTERING);
116 }
117 }
118 }
119
120
121 int
add_to_pattern(PAT_S * pat,long int rflags)122 add_to_pattern(PAT_S *pat, long int rflags)
123 {
124 PAT_LINE_S *new_patline, *patline;
125 PAT_S *new_pat;
126 PAT_STATE dummy;
127
128 if(!any_patterns(rflags, &dummy))
129 return(0);
130
131 /* need a new patline */
132 new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
133 memset((void *)new_patline, 0, sizeof(*new_patline));
134 new_patline->type = Literal;
135 (*cur_pat_h)->dirtypinerc = 1;
136
137 /* and a copy of pat */
138 new_pat = copy_pat(pat);
139
140 /* tie together */
141 new_patline->first = new_patline->last = new_pat;
142 new_pat->patline = new_patline;
143
144
145 /*
146 * Manipulate bits directly in pattern list.
147 * Cur_pat_h is set by any_patterns.
148 */
149
150
151 /* find last patline */
152 for(patline = (*cur_pat_h)->patlinehead;
153 patline && patline->next;
154 patline = patline->next)
155 ;
156
157 /* add new patline to end of list */
158 if(patline){
159 patline->next = new_patline;
160 new_patline->prev = patline;
161 }
162 else
163 (*cur_pat_h)->patlinehead = new_patline;
164
165 return(1);
166 }
167
168
169 /*
170 * Does pattern quoting. Takes the string that the user sees and converts
171 * it to the config file string.
172 *
173 * Args: src -- The source string.
174 *
175 * The last arg to add_escapes causes \, and \\ to be replaced with hex
176 * versions of comma and backslash. That's so we can embed commas in
177 * list variables without having them act as separators. If the user wants
178 * a literal comma, they type backslash comma.
179 * If /, \, or " appear (other than the special cases in previous sentence)
180 * they are backslash-escaped like \/, \\, or \".
181 *
182 * Returns: An allocated string with quoting added.
183 *
184 * The caller is responsible for freeing the memory allocated for the answer.
185 */
186 char *
add_pat_escapes(char * src)187 add_pat_escapes(char *src)
188 {
189 return(add_escapes(src, "/\\\"", '\\', "", ",\\"));
190 }
191
192
193 /*
194 * Undoes the escape quoting done by add_pat_escapes.
195 *
196 * Args: src -- The source string.
197 *
198 * Returns: A string with backslash quoting removed or NULL. The string starts
199 * at src and goes until the end of src or until a / is reached. The
200 * / is not included in the string. /'s may be quoted by preceding
201 * them with a backslash (\) and \'s may also be quoted by
202 * preceding them with a \. In fact, \ quotes any character.
203 * Not quite, \nnn is octal escape, \xXX is hex escape.
204 * Hex escapes are undone but left with a backslash in front.
205 *
206 * The caller is responsible for freeing the memory allocated for the answer.
207 */
208 char *
remove_pat_escapes(char * src)209 remove_pat_escapes(char *src)
210 {
211 char *ans = NULL, *q, *p;
212 int done = 0;
213
214 if(src){
215 p = q = (char *)fs_get(strlen(src) + 1);
216
217 while(!done){
218 switch(*src){
219 case '\\':
220 src++;
221 if(*src){
222 if(isdigit((unsigned char)*src)){ /* octal escape */
223 *p++ = '\\';
224 *p++ = (char)read_octal(&src);
225 }
226 else if((*src == 'x' || *src == 'X') &&
227 *(src+1) && *(src+2) && isxpair(src+1)){
228 *p++ = '\\';
229 *p++ = (char)read_hex(src+1);
230 src += 3;
231 }
232 else
233 *p++ = *src++;
234 }
235
236 break;
237
238 case '\0':
239 case '/':
240 done++;
241 break;
242
243 default:
244 *p++ = *src++;
245 break;
246 }
247 }
248
249 *p = '\0';
250
251 ans = cpystr(q);
252 fs_give((void **)&q);
253 }
254
255 return(ans);
256 }
257
258
259 /*
260 * This takes envelope data and adds the backslash escapes that the user
261 * would have been responsible for adding if editing manually.
262 * It just escapes commas and backslashes.
263 *
264 * Caller must free result.
265 */
266 char *
add_roletake_escapes(char * src)267 add_roletake_escapes(char *src)
268 {
269 return(add_escapes(src, ",\\", '\\', "", ""));
270 }
271
272 /*
273 * This function only escapes commas.
274 */
275 char *
add_comma_escapes(char * src)276 add_comma_escapes(char *src)
277 {
278 return(add_escapes(src, ",", '\\', "", ""));
279 }
280
281
282 /*
283 * These are the global pattern handles which all of the pattern routines
284 * use. Once we open one of these we usually leave it open until exiting
285 * pine. The _any versions are only used if we are altering our configuration,
286 * the _ne (NonEmpty) versions are used routinely. We open the patterns by
287 * calling either nonempty_patterns (normal use) or any_patterns (config).
288 *
289 * There are eight different pinerc variables which contain patterns. They are
290 * patterns-filters2, patterns-roles, patterns-scores2, patterns-indexcolors,
291 * patterns-other, and the old patterns, patterns-filters, and patterns-scores.
292 * The first five are the active patterns variables and the old variable are
293 * kept around so that we can convert old patterns to new. The reason we
294 * split it into five separate variables is so that each can independently
295 * be controlled by the main pinerc or by the exception pinerc. The reason
296 * for the change to filters2 and scores2 was so we could change the semantics
297 * of how rules work when there are pieces in the rule that we don't
298 * understand. We added a rule to detect 8bitSubjects. So a user might have
299 * a filter that deletes messages with 8bitSubjects. The problem was that
300 * that same filter in a old patterns-filters pine would match because it
301 * would ignore the 8bitSubject part of the pattern and match on the rest.
302 * So we changed the semantics so that rules with unknown pieces would be
303 * ignored instead of used. We had to change variable names at the same time
304 * because we were adding the 8bit thing and the old pines are still out
305 * there. Filters and Scores can both be dangerous. Roles, Colors, and Other
306 * seem less dangerous so not worth adding a new variable for them.
307 *
308 * Each of the eight variables has its own handle and status variables below.
309 * That means that they operate independently.
310 *
311 * Looking at just a single one of those variables, it has four possible
312 * values. In normal use, we use the current_val of the variable to set
313 * up the patterns. We do that by calling nonempty_patterns() with the
314 * appropriate rflags. When editing configurations, we have the other two
315 * variables to deal with: main_user_val and post_user_val.
316 * We only ever deal with one of those at a time, so we re-use the variables.
317 * However, we do sometimes want to deal with one of those and at the same
318 * time refer to the current current_val. For example, if we are editing
319 * the post or main user_val for the filters variable, we still want
320 * to check for new mail. If we find new mail we'll want to call
321 * process_filter_patterns which uses the current_val for filter patterns.
322 * That means we have to provide for the case where we are using current_val
323 * at the same time as we're using one of the user_vals. That's why we have
324 * both the _ne variables (NonEmpty) and the _any variables.
325 *
326 * In any_patterns (and first_pattern...) use_flags may only be set to
327 * one value at a time, whereas rflags may be more than one value OR'd together.
328 */
329 PAT_HANDLE **cur_pat_h;
330 static PAT_HANDLE *pattern_h_roles_ne, *pattern_h_roles_any,
331 *pattern_h_scores_ne, *pattern_h_scores_any,
332 *pattern_h_filts_ne, *pattern_h_filts_any,
333 *pattern_h_filts_cfg,
334 *pattern_h_filts_ne, *pattern_h_filts_any,
335 *pattern_h_incol_ne, *pattern_h_incol_any,
336 *pattern_h_other_ne, *pattern_h_other_any,
337 *pattern_h_srch_ne, *pattern_h_srch_any,
338 *pattern_h_oldpat_ne, *pattern_h_oldpat_any;
339
340 /*
341 * These contain the PAT_OPEN_MASK open status and the PAT_USE_MASK use status.
342 */
343 static long *cur_pat_status;
344 static long pat_status_roles_ne, pat_status_roles_any,
345 pat_status_scores_ne, pat_status_scores_any,
346 pat_status_filts_ne, pat_status_filts_any,
347 pat_status_incol_ne, pat_status_incol_any,
348 pat_status_other_ne, pat_status_other_any,
349 pat_status_srch_ne, pat_status_srch_any,
350 pat_status_oldpat_ne, pat_status_oldpat_any,
351 pat_status_oldfilt_ne, pat_status_oldfilt_any,
352 pat_status_oldscore_ne, pat_status_oldscore_any;
353
354 #define SET_PATTYPE(rflags) \
355 set_pathandle(rflags); \
356 cur_pat_status = \
357 ((rflags) & PAT_USE_CURRENT) \
358 ? (((rflags) & ROLE_DO_INCOLS) ? &pat_status_incol_ne : \
359 ((rflags) & ROLE_DO_OTHER) ? &pat_status_other_ne : \
360 ((rflags) & ROLE_DO_FILTER) ? &pat_status_filts_ne : \
361 ((rflags) & ROLE_DO_SCORES) ? &pat_status_scores_ne : \
362 ((rflags) & ROLE_DO_ROLES) ? &pat_status_roles_ne : \
363 ((rflags) & ROLE_DO_SRCH) ? &pat_status_srch_ne : \
364 ((rflags) & ROLE_OLD_FILT) ? &pat_status_oldfilt_ne : \
365 ((rflags) & ROLE_OLD_SCORE) ? &pat_status_oldscore_ne :\
366 &pat_status_oldpat_ne) \
367 : (((rflags) & ROLE_DO_INCOLS) ? &pat_status_incol_any : \
368 ((rflags) & ROLE_DO_OTHER) ? &pat_status_other_any : \
369 ((rflags) & ROLE_DO_FILTER) ? &pat_status_filts_any : \
370 ((rflags) & ROLE_DO_SCORES) ? &pat_status_scores_any : \
371 ((rflags) & ROLE_DO_ROLES) ? &pat_status_roles_any : \
372 ((rflags) & ROLE_DO_SRCH) ? &pat_status_srch_any : \
373 ((rflags) & ROLE_OLD_FILT) ? &pat_status_oldfilt_any :\
374 ((rflags) & ROLE_OLD_SCORE) ? &pat_status_oldscore_any:\
375 &pat_status_oldpat_any);
376 #define CANONICAL_RFLAGS(rflags) \
377 ((((rflags) & (ROLE_DO_ROLES | ROLE_REPLY | ROLE_FORWARD | ROLE_COMPOSE)) \
378 ? ROLE_DO_ROLES : 0) | \
379 (((rflags) & (ROLE_DO_INCOLS | ROLE_INCOL)) \
380 ? ROLE_DO_INCOLS : 0) | \
381 (((rflags) & (ROLE_DO_SCORES | ROLE_SCORE)) \
382 ? ROLE_DO_SCORES : 0) | \
383 (((rflags) & (ROLE_DO_FILTER)) \
384 ? ROLE_DO_FILTER : 0) | \
385 (((rflags) & (ROLE_DO_OTHER)) \
386 ? ROLE_DO_OTHER : 0) | \
387 (((rflags) & (ROLE_DO_SRCH)) \
388 ? ROLE_DO_SRCH : 0) | \
389 (((rflags) & (ROLE_OLD_FILT)) \
390 ? ROLE_OLD_FILT : 0) | \
391 (((rflags) & (ROLE_OLD_SCORE)) \
392 ? ROLE_OLD_SCORE : 0) | \
393 (((rflags) & (ROLE_OLD_PAT)) \
394 ? ROLE_OLD_PAT : 0))
395
396 #define SETPGMSTATUS(val,yes,no) \
397 switch(val){ \
398 case PAT_STAT_YES: \
399 (yes) = 1; \
400 break; \
401 case PAT_STAT_NO: \
402 (no) = 1; \
403 break; \
404 case PAT_STAT_EITHER: \
405 default: \
406 break; \
407 }
408
409 #define SET_STATUS(srchin,srchfor,assignto) \
410 {char *qq, *pp; \
411 int ii; \
412 NAMEVAL_S *vv; \
413 if((qq = srchstr(srchin, srchfor)) != NULL){ \
414 if((pp = remove_pat_escapes(qq+strlen(srchfor))) != NULL){ \
415 for(ii = 0; (vv = role_status_types(ii)); ii++) \
416 if(!strucmp(pp, vv->shortname)){ \
417 assignto = vv->value; \
418 break; \
419 } \
420 \
421 fs_give((void **)&pp); \
422 } \
423 } \
424 }
425
426 #define SET_MSGSTATE(srchin,srchfor,assignto) \
427 {char *qq, *pp; \
428 int ii; \
429 NAMEVAL_S *vv; \
430 if((qq = srchstr(srchin, srchfor)) != NULL){ \
431 if((pp = remove_pat_escapes(qq+strlen(srchfor))) != NULL){ \
432 for(ii = 0; (vv = msg_state_types(ii)); ii++) \
433 if(!strucmp(pp, vv->shortname)){ \
434 assignto = vv->value; \
435 break; \
436 } \
437 \
438 fs_give((void **)&pp); \
439 } \
440 } \
441 }
442
443 #define PATTERN_N (9)
444
445
446 void
set_pathandle(long int rflags)447 set_pathandle(long int rflags)
448 {
449 cur_pat_h = (rflags & PAT_USE_CURRENT)
450 ? ((rflags & ROLE_DO_INCOLS) ? &pattern_h_incol_ne :
451 (rflags & ROLE_DO_OTHER) ? &pattern_h_other_ne :
452 (rflags & ROLE_DO_FILTER) ? &pattern_h_filts_ne :
453 (rflags & ROLE_DO_SCORES) ? &pattern_h_scores_ne :
454 (rflags & ROLE_DO_ROLES) ? &pattern_h_roles_ne :
455 (rflags & ROLE_DO_SRCH) ? &pattern_h_srch_ne :
456 &pattern_h_oldpat_ne)
457 : ((rflags & PAT_USE_CHANGED)
458 ? &pattern_h_filts_cfg
459 : ((rflags & ROLE_DO_INCOLS) ? &pattern_h_incol_any :
460 (rflags & ROLE_DO_OTHER) ? &pattern_h_other_any :
461 (rflags & ROLE_DO_FILTER) ? &pattern_h_filts_any :
462 (rflags & ROLE_DO_SCORES) ? &pattern_h_scores_any :
463 (rflags & ROLE_DO_ROLES) ? &pattern_h_roles_any :
464 (rflags & ROLE_DO_SRCH) ? &pattern_h_srch_any :
465 &pattern_h_oldpat_any));
466 }
467
468
469 /*
470 * Rflags may be more than one pattern type OR'd together. It also contains
471 * the "use" parameter.
472 */
473 void
open_any_patterns(long int rflags)474 open_any_patterns(long int rflags)
475 {
476 long canon_rflags;
477
478 dprint((7, "open_any_patterns(0x%x)\n", rflags));
479
480 canon_rflags = CANONICAL_RFLAGS(rflags);
481
482 if(canon_rflags & ROLE_DO_INCOLS)
483 sub_open_any_patterns(ROLE_DO_INCOLS | (rflags & PAT_USE_MASK));
484 if(canon_rflags & ROLE_DO_FILTER)
485 sub_open_any_patterns(ROLE_DO_FILTER | (rflags & PAT_USE_MASK));
486 if(canon_rflags & ROLE_DO_OTHER)
487 sub_open_any_patterns(ROLE_DO_OTHER | (rflags & PAT_USE_MASK));
488 if(canon_rflags & ROLE_DO_SCORES)
489 sub_open_any_patterns(ROLE_DO_SCORES | (rflags & PAT_USE_MASK));
490 if(canon_rflags & ROLE_DO_ROLES)
491 sub_open_any_patterns(ROLE_DO_ROLES | (rflags & PAT_USE_MASK));
492 if(canon_rflags & ROLE_DO_SRCH)
493 sub_open_any_patterns(ROLE_DO_SRCH | (rflags & PAT_USE_MASK));
494 if(canon_rflags & ROLE_OLD_FILT)
495 sub_open_any_patterns(ROLE_OLD_FILT | (rflags & PAT_USE_MASK));
496 if(canon_rflags & ROLE_OLD_SCORE)
497 sub_open_any_patterns(ROLE_OLD_SCORE | (rflags & PAT_USE_MASK));
498 if(canon_rflags & ROLE_OLD_PAT)
499 sub_open_any_patterns(ROLE_OLD_PAT | (rflags & PAT_USE_MASK));
500 }
501
502
503 /*
504 * This should only be called with a single pattern type (plus use flags).
505 * We assume that patterns of this type are closed before this is called.
506 * This always succeeds unless we run out of memory, in which case fs_get
507 * never returns.
508 */
509 void
sub_open_any_patterns(long int rflags)510 sub_open_any_patterns(long int rflags)
511 {
512 PAT_LINE_S *patline = NULL, *pl = NULL;
513 char **t = NULL;
514 struct variable *var = NULL;
515
516 SET_PATTYPE(rflags);
517
518 *cur_pat_h = (PAT_HANDLE *)fs_get(sizeof(**cur_pat_h));
519 memset((void *)*cur_pat_h, 0, sizeof(**cur_pat_h));
520
521 if(rflags & ROLE_DO_ROLES)
522 var = &ps_global->vars[V_PAT_ROLES];
523 else if(rflags & ROLE_DO_FILTER)
524 var = &ps_global->vars[V_PAT_FILTS];
525 else if(rflags & ROLE_DO_OTHER)
526 var = &ps_global->vars[V_PAT_OTHER];
527 else if(rflags & ROLE_DO_SCORES)
528 var = &ps_global->vars[V_PAT_SCORES];
529 else if(rflags & ROLE_DO_INCOLS)
530 var = &ps_global->vars[V_PAT_INCOLS];
531 else if(rflags & ROLE_DO_SRCH)
532 var = &ps_global->vars[V_PAT_SRCH];
533 else if(rflags & ROLE_OLD_FILT)
534 var = &ps_global->vars[V_PAT_FILTS_OLD];
535 else if(rflags & ROLE_OLD_SCORE)
536 var = &ps_global->vars[V_PAT_SCORES_OLD];
537 else if(rflags & ROLE_OLD_PAT)
538 var = &ps_global->vars[V_PATTERNS];
539
540 switch(rflags & PAT_USE_MASK){
541 case PAT_USE_CURRENT:
542 t = var->current_val.l;
543 break;
544 case PAT_USE_CHANGED:
545 /*
546 * some trickery to only use changed if actually changed.
547 * otherwise, use current_val
548 */
549 t = var->is_changed_val ? var->changed_val.l : var->current_val.l;
550 break;
551 case PAT_USE_MAIN:
552 t = var->main_user_val.l;
553 break;
554 case PAT_USE_POST:
555 t = var->post_user_val.l;
556 break;
557 }
558
559 if(t){
560 for(; t[0] && t[0][0]; t++){
561 if(*t && !strncmp("LIT:", *t, 4))
562 patline = parse_pat_lit(*t + 4);
563 else if(*t && !strncmp("FILE:", *t, 5))
564 patline = parse_pat_file(*t + 5);
565 else if(rflags & (PAT_USE_MAIN | PAT_USE_POST) &&
566 patline == NULL && *t && !strcmp(INHERIT, *t))
567 patline = parse_pat_inherit();
568 else
569 patline = NULL;
570
571 if(patline){
572 if(pl){
573 pl->next = patline;
574 patline->prev = pl;
575 pl = pl->next;
576 }
577 else{
578 (*cur_pat_h)->patlinehead = patline;
579 pl = patline;
580 }
581 }
582 else
583 q_status_message1(SM_ORDER, 0, 3,
584 "Invalid patterns line \"%.200s\"", *t);
585 }
586 }
587
588 *cur_pat_status = PAT_OPENED | (rflags & PAT_USE_MASK);
589 }
590
591
592 void
close_every_pattern(void)593 close_every_pattern(void)
594 {
595 close_patterns(ROLE_DO_INCOLS | ROLE_DO_FILTER | ROLE_DO_SCORES
596 | ROLE_DO_OTHER | ROLE_DO_ROLES | ROLE_DO_SRCH
597 | ROLE_OLD_FILT | ROLE_OLD_SCORE | ROLE_OLD_PAT
598 | PAT_USE_CURRENT);
599 /*
600 * Since there is only one set of variables for the other three uses
601 * we can just close any one of them. There can only be one open at
602 * a time.
603 */
604 close_patterns(ROLE_DO_INCOLS | ROLE_DO_FILTER | ROLE_DO_SCORES
605 | ROLE_DO_OTHER | ROLE_DO_ROLES | ROLE_DO_SRCH
606 | ROLE_OLD_FILT | ROLE_OLD_SCORE | ROLE_OLD_PAT
607 | PAT_USE_MAIN);
608 }
609
610
611 /*
612 * Can be called with more than one pattern type.
613 */
614 void
close_patterns(long int rflags)615 close_patterns(long int rflags)
616 {
617 long canon_rflags;
618
619 dprint((7, "close_patterns(0x%x)\n", rflags));
620
621 canon_rflags = CANONICAL_RFLAGS(rflags);
622
623 if(canon_rflags & ROLE_DO_INCOLS)
624 sub_close_patterns(ROLE_DO_INCOLS | (rflags & PAT_USE_MASK));
625 if(canon_rflags & ROLE_DO_OTHER)
626 sub_close_patterns(ROLE_DO_OTHER | (rflags & PAT_USE_MASK));
627 if(canon_rflags & ROLE_DO_FILTER)
628 sub_close_patterns(ROLE_DO_FILTER | (rflags & PAT_USE_MASK));
629 if(canon_rflags & ROLE_DO_SCORES)
630 sub_close_patterns(ROLE_DO_SCORES | (rflags & PAT_USE_MASK));
631 if(canon_rflags & ROLE_DO_ROLES)
632 sub_close_patterns(ROLE_DO_ROLES | (rflags & PAT_USE_MASK));
633 if(canon_rflags & ROLE_DO_SRCH)
634 sub_close_patterns(ROLE_DO_SRCH | (rflags & PAT_USE_MASK));
635 if(canon_rflags & ROLE_OLD_FILT)
636 sub_close_patterns(ROLE_OLD_FILT | (rflags & PAT_USE_MASK));
637 if(canon_rflags & ROLE_OLD_SCORE)
638 sub_close_patterns(ROLE_OLD_SCORE | (rflags & PAT_USE_MASK));
639 if(canon_rflags & ROLE_OLD_PAT)
640 sub_close_patterns(ROLE_OLD_PAT | (rflags & PAT_USE_MASK));
641 }
642
643
644 /*
645 * Can be called with only a single pattern type.
646 */
647 void
sub_close_patterns(long int rflags)648 sub_close_patterns(long int rflags)
649 {
650 SET_PATTYPE(rflags);
651
652 if(*cur_pat_h != NULL){
653 free_patline(&(*cur_pat_h)->patlinehead);
654 fs_give((void **)cur_pat_h);
655 }
656
657 *cur_pat_status = PAT_CLOSED;
658
659 scores_are_used(SCOREUSE_INVALID);
660 }
661
662
663 /*
664 * Can be called with more than one pattern type.
665 * Nonempty always uses PAT_USE_CURRENT (the current_val).
666 */
667 int
nonempty_patterns(long int rflags,PAT_STATE * pstate)668 nonempty_patterns(long int rflags, PAT_STATE *pstate)
669 {
670 return(any_patterns((rflags & ROLE_MASK) | PAT_USE_CURRENT, pstate));
671 }
672
673
674 /*
675 * Initializes pstate and parses and sets up appropriate pattern variables.
676 * May be called with more than one pattern type OR'd together in rflags.
677 * Pstate will keep track of that and next_pattern et. al. will increment
678 * through all of those pattern types.
679 */
680 int
any_patterns(long int rflags,PAT_STATE * pstate)681 any_patterns(long int rflags, PAT_STATE *pstate)
682 {
683 int ret = 0;
684 long canon_rflags;
685
686 dprint((7, "any_patterns(0x%x)\n", rflags));
687
688 memset((void *)pstate, 0, sizeof(*pstate));
689 pstate->rflags = rflags;
690
691 canon_rflags = CANONICAL_RFLAGS(pstate->rflags);
692
693 if(canon_rflags & ROLE_DO_INCOLS)
694 ret += sub_any_patterns(ROLE_DO_INCOLS, pstate);
695 if(canon_rflags & ROLE_DO_OTHER)
696 ret += sub_any_patterns(ROLE_DO_OTHER, pstate);
697 if(canon_rflags & ROLE_DO_FILTER)
698 ret += sub_any_patterns(ROLE_DO_FILTER, pstate);
699 if(canon_rflags & ROLE_DO_SCORES)
700 ret += sub_any_patterns(ROLE_DO_SCORES, pstate);
701 if(canon_rflags & ROLE_DO_ROLES)
702 ret += sub_any_patterns(ROLE_DO_ROLES, pstate);
703 if(canon_rflags & ROLE_DO_SRCH)
704 ret += sub_any_patterns(ROLE_DO_SRCH, pstate);
705 if(canon_rflags & ROLE_OLD_FILT)
706 ret += sub_any_patterns(ROLE_OLD_FILT, pstate);
707 if(canon_rflags & ROLE_OLD_SCORE)
708 ret += sub_any_patterns(ROLE_OLD_SCORE, pstate);
709 if(canon_rflags & ROLE_OLD_PAT)
710 ret += sub_any_patterns(ROLE_OLD_PAT, pstate);
711
712 return(ret);
713 }
714
715
716 int
sub_any_patterns(long int rflags,PAT_STATE * pstate)717 sub_any_patterns(long int rflags, PAT_STATE *pstate)
718 {
719 SET_PATTYPE(rflags | (pstate->rflags & PAT_USE_MASK));
720
721 if(*cur_pat_h &&
722 (((pstate->rflags & PAT_USE_MASK) == PAT_USE_CURRENT &&
723 (*cur_pat_status & PAT_USE_MASK) != PAT_USE_CURRENT) ||
724 ((pstate->rflags & PAT_USE_MASK) != PAT_USE_CURRENT &&
725 ((*cur_pat_status & PAT_OPEN_MASK) != PAT_OPENED ||
726 (*cur_pat_status & PAT_USE_MASK) !=
727 (pstate->rflags & PAT_USE_MASK)))))
728 close_patterns(rflags | (pstate->rflags & PAT_USE_MASK));
729
730 /* open_any always succeeds */
731 if(!*cur_pat_h && ((*cur_pat_status & PAT_OPEN_MASK) == PAT_CLOSED))
732 open_any_patterns(rflags | (pstate->rflags & PAT_USE_MASK));
733
734 if(!*cur_pat_h){ /* impossible */
735 *cur_pat_status = PAT_CLOSED;
736 return(0);
737 }
738
739 /*
740 * Opening nonempty can fail. That just means there aren't any
741 * patterns of that type.
742 */
743 if((pstate->rflags & PAT_USE_MASK) == PAT_USE_CURRENT &&
744 !(*cur_pat_h)->patlinehead)
745 *cur_pat_status = (PAT_OPEN_FAILED | PAT_USE_CURRENT);
746
747 return(((*cur_pat_status & PAT_OPEN_MASK) == PAT_OPENED) ? 1 : 0);
748 }
749
750
751 int
edit_pattern(PAT_S * newpat,int pos,long int rflags)752 edit_pattern(PAT_S *newpat, int pos, long int rflags)
753 {
754 PAT_S *oldpat;
755 PAT_LINE_S *tpatline;
756 int i;
757 PAT_STATE pstate;
758
759 if(!any_patterns(rflags, &pstate)) return(1);
760
761 for(i = 0, tpatline = (*cur_pat_h)->patlinehead;
762 i < pos && tpatline; tpatline = tpatline->next, i++);
763 if(i != pos) return(1);
764 oldpat = tpatline->first;
765 free_pat(&oldpat);
766 tpatline->first = tpatline->last = newpat;
767 newpat->patline = tpatline;
768 tpatline->dirty = 1;
769
770 (*cur_pat_h)->dirtypinerc = 1;
771 write_patterns(rflags);
772
773 return(0);
774 }
775
776 int
add_pattern(PAT_S * newpat,long int rflags)777 add_pattern(PAT_S *newpat, long int rflags)
778 {
779 PAT_LINE_S *tpatline, *newpatline;
780 PAT_STATE pstate;
781
782 any_patterns(rflags, &pstate);
783
784 for(tpatline = (*cur_pat_h)->patlinehead;
785 tpatline && tpatline->next ; tpatline = tpatline->next);
786 newpatline = (PAT_LINE_S *)fs_get(sizeof(PAT_LINE_S));
787 if(tpatline)
788 tpatline->next = newpatline;
789 else
790 (*cur_pat_h)->patlinehead = newpatline;
791 memset((void *)newpatline, 0, sizeof(PAT_LINE_S));
792 newpatline->prev = tpatline;
793 newpatline->first = newpatline->last = newpat;
794 newpatline->type = Literal;
795 newpat->patline = newpatline;
796 newpatline->dirty = 1;
797
798 (*cur_pat_h)->dirtypinerc = 1;
799 write_patterns(rflags);
800
801 return(0);
802 }
803
804 int
delete_pattern(int pos,long int rflags)805 delete_pattern(int pos, long int rflags)
806 {
807 PAT_LINE_S *tpatline;
808 int i;
809 PAT_STATE pstate;
810
811 if(!any_patterns(rflags, &pstate)) return(1);
812
813 for(i = 0, tpatline = (*cur_pat_h)->patlinehead;
814 i < pos && tpatline; tpatline = tpatline->next, i++);
815 if(i != pos) return(1);
816
817 if(tpatline == (*cur_pat_h)->patlinehead)
818 (*cur_pat_h)->patlinehead = tpatline->next;
819 if(tpatline->prev) tpatline->prev->next = tpatline->next;
820 if(tpatline->next) tpatline->next->prev = tpatline->prev;
821 tpatline->prev = NULL;
822 tpatline->next = NULL;
823
824 free_patline(&tpatline);
825
826 (*cur_pat_h)->dirtypinerc = 1;
827 write_patterns(rflags);
828
829 return(0);
830 }
831
832 int
shuffle_pattern(int pos,int up,long int rflags)833 shuffle_pattern(int pos, int up, long int rflags)
834 {
835 PAT_LINE_S *tpatline, *shufpatline;
836 int i;
837 PAT_STATE pstate;
838
839 if(!any_patterns(rflags, &pstate)) return(1);
840
841 for(i = 0, tpatline = (*cur_pat_h)->patlinehead;
842 i < pos && tpatline; tpatline = tpatline->next, i++);
843 if(i != pos) return(1);
844
845 if(up == 1){
846 if(tpatline->prev == NULL) return(1);
847 shufpatline = tpatline->prev;
848 tpatline->prev = shufpatline->prev;
849 if(shufpatline->prev)
850 shufpatline->prev->next = tpatline;
851 if(tpatline->next)
852 tpatline->next->prev = shufpatline;
853 shufpatline->next = tpatline->next;
854 shufpatline->prev = tpatline;
855 tpatline->next = shufpatline;
856 if(shufpatline == (*cur_pat_h)->patlinehead)
857 (*cur_pat_h)->patlinehead = tpatline;
858 }
859 else if(up == -1){
860 if(tpatline->next == NULL) return(1);
861 shufpatline = tpatline->next;
862 tpatline->next = shufpatline->next;
863 if(shufpatline->next)
864 shufpatline->next->prev = tpatline;
865 if(tpatline->prev)
866 tpatline->prev->next = shufpatline;
867 shufpatline->prev = tpatline->prev;
868 shufpatline->next = tpatline;
869 tpatline->prev = shufpatline;
870 if(tpatline == (*cur_pat_h)->patlinehead)
871 (*cur_pat_h)->patlinehead = shufpatline;
872 }
873 else return(1);
874
875 shufpatline->dirty = 1;
876 tpatline->dirty = 1;
877
878 (*cur_pat_h)->dirtypinerc = 1;
879 write_patterns(rflags);
880
881 return(0);
882 }
883
884 PAT_LINE_S *
parse_pat_lit(char * litpat)885 parse_pat_lit(char *litpat)
886 {
887 PAT_LINE_S *patline;
888 PAT_S *pat;
889
890 patline = (PAT_LINE_S *)fs_get(sizeof(*patline));
891 memset((void *)patline, 0, sizeof(*patline));
892 patline->type = Literal;
893
894
895 if((pat = parse_pat(litpat)) != NULL){
896 pat->patline = patline;
897 patline->first = pat;
898 patline->last = pat;
899 }
900
901 return(patline);
902 }
903
904
905 /*
906 * This always returns a patline even if we can't read the file. The patline
907 * returned will say readonly in the worst case and there will be no patterns.
908 * If the file doesn't exist, this creates it if possible.
909 */
910 PAT_LINE_S *
parse_pat_file(char * filename)911 parse_pat_file(char *filename)
912 {
913 #define BUF_SIZE 5000
914 PAT_LINE_S *patline;
915 PAT_S *pat, *p;
916 char path[MAXPATH+1], buf[BUF_SIZE];
917 char *dir, *q;
918 FILE *fp;
919 int ok = 0, some_pats = 0;
920 struct variable *vars = ps_global->vars;
921
922 signature_path(filename, path, MAXPATH);
923
924 if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, path)){
925 q_status_message1(SM_ORDER | SM_DING, 3, 4,
926 "Can't use Roles file outside of %.200s",
927 VAR_OPER_DIR);
928 return(NULL);
929 }
930
931 patline = (PAT_LINE_S *)fs_get(sizeof(*patline));
932 memset((void *)patline, 0, sizeof(*patline));
933 patline->type = File;
934 patline->filename = cpystr(filename);
935 patline->filepath = cpystr(path);
936
937 if((q = last_cmpnt(path)) != NULL){
938 int save;
939
940 save = *--q;
941 *q = '\0';
942 dir = cpystr(*path ? path : "/");
943 *q = save;
944 }
945 else
946 dir = cpystr(".");
947
948 #if defined(DOS) || defined(OS2)
949 /*
950 * If the dir has become a drive letter and : (e.g. "c:")
951 * then append a "\". The library function access() in the
952 * win 16 version of MSC seems to require this.
953 */
954 if(isalpha((unsigned char) *dir)
955 && *(dir+1) == ':' && *(dir+2) == '\0'){
956 *(dir+2) = '\\';
957 *(dir+3) = '\0';
958 }
959 #endif /* DOS || OS2 */
960
961 /*
962 * Even if we can edit the file itself, we aren't going
963 * to be able to change it unless we can also write in
964 * the directory that contains it (because we write into a
965 * temp file and then rename).
966 */
967 if(can_access(dir, EDIT_ACCESS) != 0)
968 patline->readonly = 1;
969
970 if(can_access(path, EDIT_ACCESS) == 0){
971 if(patline->readonly)
972 q_status_message1(SM_ORDER, 0, 3,
973 "Pattern file directory (%.200s) is ReadOnly", dir);
974 }
975 else if(can_access(path, READ_ACCESS) == 0)
976 patline->readonly = 1;
977
978 if(can_access(path, ACCESS_EXISTS) == 0){
979 if((fp = our_fopen(path, "rb")) != NULL){
980 /* Check to see if this is a valid patterns file */
981 if(fp_file_size(fp) <= 0L)
982 ok++;
983 else{
984 size_t len;
985
986 len = strlen(PATTERN_MAGIC);
987 if(fread(buf, sizeof(char), len+3, fp) == len+3){
988 buf[len+3] = '\0';
989 buf[len] = '\0';
990 if(strcmp(buf, PATTERN_MAGIC) == 0){
991 if(atoi(PATTERN_FILE_VERS) < atoi(buf + len + 1))
992 q_status_message1(SM_ORDER, 0, 4,
993 "Pattern file \"%.200s\" is made by newer Alpine, will try to use it anyway",
994 filename);
995
996 ok++;
997 some_pats++;
998 /* toss rest of first line */
999 (void)fgets(buf, BUF_SIZE, fp);
1000 }
1001 }
1002 }
1003
1004 if(!ok){
1005 patline->readonly = 1;
1006 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1007 "\"%.200s\" is not a Pattern file", path);
1008 }
1009
1010 p = NULL;
1011 while(some_pats && fgets(buf, BUF_SIZE, fp) != NULL){
1012 if((pat = parse_pat(buf)) != NULL){
1013 pat->patline = patline;
1014 if(!patline->first)
1015 patline->first = pat;
1016
1017 patline->last = pat;
1018
1019 if(p){
1020 p->next = pat;
1021 pat->prev = p;
1022 p = p->next;
1023 }
1024 else
1025 p = pat;
1026 }
1027 }
1028
1029 (void)fclose(fp);
1030 }
1031 else{
1032 patline->readonly = 1;
1033 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1034 "Error \"%.200s\" reading pattern file \"%.200s\"",
1035 error_description(errno), path);
1036 }
1037 }
1038 else{ /* doesn't exist yet, try to create it */
1039 if(patline->readonly)
1040 q_status_message1(SM_ORDER, 0, 3,
1041 "Pattern file directory (%.200s) is ReadOnly", dir);
1042 else{
1043 /*
1044 * We try to create it by making up an empty patline and calling
1045 * write_pattern_file.
1046 */
1047 patline->dirty = 1;
1048 if(write_pattern_file(NULL, patline) != 0){
1049 patline->readonly = 1;
1050 patline->dirty = 0;
1051 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1052 "Error creating pattern file \"%.200s\"",
1053 path);
1054 }
1055 }
1056 }
1057
1058 if(dir)
1059 fs_give((void **)&dir);
1060
1061 return(patline);
1062 }
1063
1064
1065 PAT_LINE_S *
parse_pat_inherit(void)1066 parse_pat_inherit(void)
1067 {
1068 PAT_LINE_S *patline;
1069 PAT_S *pat;
1070
1071 patline = (PAT_LINE_S *)fs_get(sizeof(*patline));
1072 memset((void *)patline, 0, sizeof(*patline));
1073 patline->type = Inherit;
1074
1075 pat = (PAT_S *)fs_get(sizeof(*pat));
1076 memset((void *)pat, 0, sizeof(*pat));
1077 pat->inherit = 1;
1078
1079 pat->patline = patline;
1080 patline->first = pat;
1081 patline->last = pat;
1082
1083 return(patline);
1084 }
1085
1086
1087 /*
1088 * There are three forms that a PATTERN_S has at various times. There is
1089 * the actual PATTERN_S struct which is used internally and is used whenever
1090 * we are actually doing something with the pattern, like filtering or
1091 * something. There is the version that goes in the config file. And there
1092 * is the version the user edits.
1093 *
1094 * To go between these three forms we have the helper routines
1095 *
1096 * pattern_to_config
1097 * config_to_pattern
1098 * pattern_to_editlist
1099 * editlist_to_pattern
1100 *
1101 * Here's what is supposed to be happening. A PATTERN_S is a linked list
1102 * of strings with nothing escaped. That is, a backslash or a comma is
1103 * just in there as a backslash or comma.
1104 *
1105 * The version the user edits is very similar. Because we have historically
1106 * used commas as separators the user has always had to enter a \, in order
1107 * to put a real comma in one of the items. That is the only difference
1108 * between a PATTERN_S string and the editlist strings. Note that backslashes
1109 * themselves are not escaped. A backslash which is not followed by a comma
1110 * is a backslash. It doesn't escape the following character. That's a bit
1111 * odd, it is that way because most people will never know about this
1112 * backslash stuff but PC-Pine users may have backslashes in folder names.
1113 *
1114 * The version that goes in the config file has a few transformations made.
1115 * PATTERN_S intermediate_form Config string
1116 * , \, \x2C
1117 * \ \\ \x5C
1118 * / \/
1119 * " \"
1120 *
1121 * The commas are turned into hex commas so that we can tell the separators
1122 * in the comma-separated lists from those commas.
1123 * The backslashes are escaped because they escape commas.
1124 * The /'s are escaped because they separate pattern pieces.
1125 * The "'s are escaped because they are significant to parse_list when
1126 * parsing the config file.
1127 * hubert - 2004-04-01
1128 * (date is only coincidental!)
1129 *
1130 * Addendum. The not's are handled separately from all the strings. Not sure
1131 * why that is or if there is a good reason. Nevertheless, now is not the
1132 * time to figure it out so leave it that way.
1133 * hubert - 2004-07-14
1134 */
1135 PAT_S *
parse_pat(char * str)1136 parse_pat(char *str)
1137 {
1138 PAT_S *pat = NULL;
1139 char *p, *q, *astr, *pstr;
1140 int backslashed;
1141 #define PTRN "pattern="
1142 #define PTRNLEN 8
1143 #define ACTN "action="
1144 #define ACTNLEN 7
1145
1146 if(str)
1147 removing_trailing_white_space(str);
1148
1149 if(!str || !*str || *str == '#')
1150 return(pat);
1151
1152 pat = (PAT_S *)fs_get(sizeof(*pat));
1153 memset((void *)pat, 0, sizeof(*pat));
1154
1155 if((p = srchstr(str, PTRN)) != NULL){
1156 pat->patgrp = (PATGRP_S *)fs_get(sizeof(*pat->patgrp));
1157 memset((void *)pat->patgrp, 0, sizeof(*pat->patgrp));
1158 pat->patgrp->fldr_type = FLDR_DEFL;
1159 pat->patgrp->inabook = IAB_DEFL;
1160 pat->patgrp->cat_lim = -1L;
1161
1162 if((pstr = copy_quoted_string_asis(p+PTRNLEN)) != NULL){
1163 /* move to next slash */
1164 for(q=pstr, backslashed=0; *q; q++){
1165 switch(*q){
1166 case '\\':
1167 backslashed = !backslashed;
1168 break;
1169
1170 case '/':
1171 if(!backslashed){
1172 parse_patgrp_slash(q, pat->patgrp);
1173 if(pat->patgrp->bogus && !pat->raw)
1174 pat->raw = cpystr(str);
1175 }
1176
1177 /* fall through */
1178
1179 default:
1180 backslashed = 0;
1181 break;
1182 }
1183 }
1184
1185 /* we always force a nickname */
1186 if(!pat->patgrp->nick)
1187 pat->patgrp->nick = cpystr("Alternate Role");
1188
1189 fs_give((void **)&pstr);
1190 }
1191 }
1192
1193 if((p = srchstr(str, ACTN)) != NULL){
1194 pat->action = (ACTION_S *)fs_get(sizeof(*pat->action));
1195 memset((void *)pat->action, 0, sizeof(*pat->action));
1196 pat->action->startup_rule = IS_NOTSET;
1197 pat->action->repl_type = ROLE_REPL_DEFL;
1198 pat->action->forw_type = ROLE_FORW_DEFL;
1199 pat->action->comp_type = ROLE_COMP_DEFL;
1200 pat->action->nick = cpystr((pat->patgrp && pat->patgrp->nick
1201 && pat->patgrp->nick[0])
1202 ? pat->patgrp->nick : "Alternate Role");
1203
1204 if((astr = copy_quoted_string_asis(p+ACTNLEN)) != NULL){
1205 /* move to next slash */
1206 for(q=astr, backslashed=0; *q; q++){
1207 switch(*q){
1208 case '\\':
1209 backslashed = !backslashed;
1210 break;
1211
1212 case '/':
1213 if(!backslashed){
1214 parse_action_slash(q, pat->action);
1215 if(pat->action->bogus && !pat->raw)
1216 pat->raw = cpystr(str);
1217 }
1218
1219 /* fall through */
1220
1221 default:
1222 backslashed = 0;
1223 break;
1224 }
1225 }
1226
1227 fs_give((void **)&astr);
1228
1229 if(!pat->action->is_a_score)
1230 pat->action->scoreval = 0L;
1231
1232 if(pat->action->is_a_filter)
1233 pat->action->kill = (pat->action->folder
1234 || pat->action->kill == -1) ? 0 : 1;
1235 else{
1236 if(pat->action->folder)
1237 free_pattern(&pat->action->folder);
1238 }
1239
1240 if(!pat->action->is_a_role){
1241 pat->action->repl_type = ROLE_NOTAROLE_DEFL;
1242 pat->action->forw_type = ROLE_NOTAROLE_DEFL;
1243 pat->action->comp_type = ROLE_NOTAROLE_DEFL;
1244 if(pat->action->from)
1245 mail_free_address(&pat->action->from);
1246 if(pat->action->replyto)
1247 mail_free_address(&pat->action->replyto);
1248 if(pat->action->fcc)
1249 fs_give((void **)&pat->action->fcc);
1250 if(pat->action->litsig)
1251 fs_give((void **)&pat->action->litsig);
1252 if(pat->action->sig)
1253 fs_give((void **)&pat->action->sig);
1254 if(pat->action->template)
1255 fs_give((void **)&pat->action->template);
1256 if(pat->action->cstm)
1257 free_list_array(&pat->action->cstm);
1258 if(pat->action->smtp)
1259 free_list_array(&pat->action->smtp);
1260 if(pat->action->nntp)
1261 free_list_array(&pat->action->nntp);
1262 if(pat->action->inherit_nick)
1263 fs_give((void **)&pat->action->inherit_nick);
1264 }
1265
1266 if(!pat->action->is_a_incol){
1267 if(pat->action->incol)
1268 free_color_pair(&pat->action->incol);
1269 }
1270
1271 if(!pat->action->is_a_other){
1272 pat->action->sort_is_set = 0;
1273 pat->action->sortorder = 0;
1274 pat->action->revsort = 0;
1275 pat->action->startup_rule = IS_NOTSET;
1276 if(pat->action->index_format)
1277 fs_give((void **)&pat->action->index_format);
1278 }
1279 }
1280 }
1281
1282 return(pat);
1283 }
1284
1285
1286 /*
1287 * Fill in one member of patgrp from str.
1288 *
1289 * The multiple constant strings are lame but it evolved this way from
1290 * previous versions and isn't worth fixing.
1291 */
1292 void
parse_patgrp_slash(char * str,PATGRP_S * patgrp)1293 parse_patgrp_slash(char *str, PATGRP_S *patgrp)
1294 {
1295 char *p;
1296
1297 if(!patgrp)
1298 alpine_panic("NULL patgrp to parse_patgrp_slash");
1299 else if(!(str && *str)){
1300 alpine_panic("NULL or empty string to parse_patgrp_slash");
1301 patgrp->bogus = 1;
1302 }
1303 else if(!strncmp(str, "/NICK=", 6))
1304 patgrp->nick = remove_pat_escapes(str+6);
1305 else if(!strncmp(str, "/COMM=", 6))
1306 patgrp->comment = remove_pat_escapes(str+6);
1307 else if(!strncmp(str, "/TO=", 4) || !strncmp(str, "/!TO=", 5))
1308 patgrp->to = parse_pattern("TO", str, 1);
1309 else if(!strncmp(str, "/CC=", 4) || !strncmp(str, "/!CC=", 5))
1310 patgrp->cc = parse_pattern("CC", str, 1);
1311 else if(!strncmp(str, "/RECIP=", 7) || !strncmp(str, "/!RECIP=", 8))
1312 patgrp->recip = parse_pattern("RECIP", str, 1);
1313 else if(!strncmp(str, "/PARTIC=", 8) || !strncmp(str, "/!PARTIC=", 9))
1314 patgrp->partic = parse_pattern("PARTIC", str, 1);
1315 else if(!strncmp(str, "/FROM=", 6) || !strncmp(str, "/!FROM=", 7))
1316 patgrp->from = parse_pattern("FROM", str, 1);
1317 else if(!strncmp(str, "/SENDER=", 8) || !strncmp(str, "/!SENDER=", 9))
1318 patgrp->sender = parse_pattern("SENDER", str, 1);
1319 else if(!strncmp(str, "/NEWS=", 6) || !strncmp(str, "/!NEWS=", 7))
1320 patgrp->news = parse_pattern("NEWS", str, 1);
1321 else if(!strncmp(str, "/SUBJ=", 6) || !strncmp(str, "/!SUBJ=", 7))
1322 patgrp->subj = parse_pattern("SUBJ", str, 1);
1323 else if(!strncmp(str, "/ALL=", 5) || !strncmp(str, "/!ALL=", 6))
1324 patgrp->alltext = parse_pattern("ALL", str, 1);
1325 else if(!strncmp(str, "/BODY=", 6) || !strncmp(str, "/!BODY=", 7))
1326 patgrp->bodytext = parse_pattern("BODY", str, 1);
1327 else if(!strncmp(str, "/KEY=", 5) || !strncmp(str, "/!KEY=", 6))
1328 patgrp->keyword = parse_pattern("KEY", str, 1);
1329 else if(!strncmp(str, "/CHAR=", 6) || !strncmp(str, "/!CHAR=", 7))
1330 patgrp->charsets = parse_pattern("CHAR", str, 1);
1331 else if(!strncmp(str, "/FOLDER=", 8) || !strncmp(str, "/!FOLDER=", 9))
1332 patgrp->folder = parse_pattern("FOLDER", str, 1);
1333 else if(!strncmp(str, "/ABOOKS=", 8) || !strncmp(str, "/!ABOOKS=", 9))
1334 patgrp->abooks = parse_pattern("ABOOKS", str, 1);
1335 /*
1336 * A problem with arbhdrs is that more than one of them can appear in
1337 * the string. We come back here the second time, but we already took
1338 * care of the whole thing on the first pass. Hence the check for
1339 * arbhdr already set.
1340 */
1341 else if(!strncmp(str, "/ARB", 4) || !strncmp(str, "/!ARB", 5)
1342 || !strncmp(str, "/EARB", 5) || !strncmp(str, "/!EARB", 6)){
1343 if(!patgrp->arbhdr)
1344 patgrp->arbhdr = parse_arbhdr(str);
1345 /* else do nothing */
1346 }
1347 else if(!strncmp(str, "/SENTDATE=", 10))
1348 patgrp->age_uses_sentdate = 1;
1349 else if(!strncmp(str, "/SCOREI=", 8)){
1350 if((p = remove_pat_escapes(str+8)) != NULL){
1351 if((patgrp->score = parse_intvl(p)) != NULL)
1352 patgrp->do_score = 1;
1353
1354 fs_give((void **)&p);
1355 }
1356 }
1357 else if(!strncmp(str, "/AGE=", 5)){
1358 if((p = remove_pat_escapes(str+5)) != NULL){
1359 if((patgrp->age = parse_intvl(p)) != NULL)
1360 patgrp->do_age = 1;
1361
1362 fs_give((void **)&p);
1363 }
1364 }
1365 else if(!strncmp(str, "/SIZE=", 6)){
1366 if((p = remove_pat_escapes(str+6)) != NULL){
1367 if((patgrp->size = parse_intvl(p)) != NULL)
1368 patgrp->do_size = 1;
1369
1370 fs_give((void **)&p);
1371 }
1372 }
1373 else if(!strncmp(str, "/CATCMD=", 8)){
1374 if((p = remove_pat_escapes(str+8)) != NULL){
1375 int commas = 0;
1376 char *q;
1377
1378 /* count elements in list */
1379 for(q = p; q && *q; q++)
1380 if(*q == ',')
1381 commas++;
1382
1383 patgrp->category_cmd = parse_list(p, commas+1, PL_REMSURRQUOT,NULL);
1384 fs_give((void **)&p);
1385 }
1386 }
1387 else if(!strncmp(str, "/CATVAL=", 8)){
1388 if((p = remove_pat_escapes(str+8)) != NULL){
1389 if((patgrp->cat = parse_intvl(p)) != NULL)
1390 patgrp->do_cat = 1;
1391
1392 fs_give((void **)&p);
1393 }
1394 }
1395 else if(!strncmp(str, "/CATLIM=", 8)){
1396 if((p = remove_pat_escapes(str+8)) != NULL){
1397 long i;
1398
1399 i = atol(p);
1400 patgrp->cat_lim = i;
1401 fs_give((void **)&p);
1402 }
1403 }
1404 else if(!strncmp(str, "/FLDTYPE=", 9)){
1405 if((p = remove_pat_escapes(str+9)) != NULL){
1406 int i;
1407 NAMEVAL_S *v;
1408
1409 for(i = 0; (v = pat_fldr_types(i)); i++)
1410 if(!strucmp(p, v->shortname)){
1411 patgrp->fldr_type = v->value;
1412 break;
1413 }
1414
1415 fs_give((void **)&p);
1416 }
1417 }
1418 else if(!strncmp(str, "/AFROM=", 7)){
1419 if((p = remove_pat_escapes(str+7)) != NULL){
1420 int i;
1421 NAMEVAL_S *v;
1422
1423 for(i = 0; (v = inabook_fldr_types(i)); i++)
1424 if(!strucmp(p, v->shortname)){
1425 patgrp->inabook |= v->value;
1426 break;
1427 }
1428
1429 /* to match old semantics */
1430 patgrp->inabook |= IAB_FROM;
1431 patgrp->inabook |= IAB_REPLYTO;
1432
1433 fs_give((void **)&p);
1434 }
1435 }
1436 else if(!strncmp(str, "/AFROMA=", 8)){
1437 if((p = remove_pat_escapes(str+8)) != NULL){
1438
1439 /* make sure AFROMA comes after AFROM in config lines */
1440 patgrp->inabook &= ~IAB_ADDR_MASK;
1441
1442 if(strchr(p, 'F'))
1443 patgrp->inabook |= IAB_FROM;
1444 if(strchr(p, 'R'))
1445 patgrp->inabook |= IAB_REPLYTO;
1446 if(strchr(p, 'S'))
1447 patgrp->inabook |= IAB_SENDER;
1448 if(strchr(p, 'T'))
1449 patgrp->inabook |= IAB_TO;
1450 if(strchr(p, 'C'))
1451 patgrp->inabook |= IAB_CC;
1452
1453 fs_give((void **)&p);
1454 }
1455 }
1456 else if(!strncmp(str, "/STATN=", 7)){
1457 SET_STATUS(str,"/STATN=",patgrp->stat_new);
1458 }
1459 else if(!strncmp(str, "/STATR=", 7)){
1460 SET_STATUS(str,"/STATR=",patgrp->stat_rec);
1461 }
1462 else if(!strncmp(str, "/STATI=", 7)){
1463 SET_STATUS(str,"/STATI=",patgrp->stat_imp);
1464 }
1465 else if(!strncmp(str, "/STATA=", 7)){
1466 SET_STATUS(str,"/STATA=",patgrp->stat_ans);
1467 }
1468 else if(!strncmp(str, "/STATD=", 7)){
1469 SET_STATUS(str,"/STATD=",patgrp->stat_del);
1470 }
1471 else if(!strncmp(str, "/8BITS=", 7)){
1472 SET_STATUS(str,"/8BITS=",patgrp->stat_8bitsubj);
1473 }
1474 else if(!strncmp(str, "/BOM=", 5)){
1475 SET_STATUS(str,"/BOM=",patgrp->stat_bom);
1476 }
1477 else if(!strncmp(str, "/BOY=", 5)){
1478 SET_STATUS(str,"/BOY=",patgrp->stat_boy);
1479 }
1480 else{
1481 char save = '\0';;
1482
1483 patgrp->bogus = 1;
1484
1485 if((p = strindex(str, '=')) != NULL){
1486 save = *(p+1);
1487 *(p+1) = '\0';
1488 }
1489
1490 dprint((1,
1491 "parse_patgrp_slash(%.20s): unrecognized in \"%s\"\n",
1492 str ? str : "?",
1493 (patgrp && patgrp->nick) ? patgrp->nick : ""));
1494 q_status_message4(SM_ORDER, 1, 3,
1495 "Warning: unrecognized pattern element \"%.20s\"%.20s%.20s%.20s",
1496 str, patgrp->nick ? " in rule \"" : "",
1497 patgrp->nick ? patgrp->nick : "", patgrp->nick ? "\"" : "");
1498
1499 if(p)
1500 *(p+1) = save;
1501 }
1502 }
1503
1504
1505 /*
1506 * Fill in one member of action struct from str.
1507 *
1508 * The multiple constant strings are lame but it evolved this way from
1509 * previous versions and isn't worth fixing.
1510 */
1511 void
parse_action_slash(char * str,ACTION_S * action)1512 parse_action_slash(char *str, ACTION_S *action)
1513 {
1514 char *p;
1515 int stateval, i;
1516 NAMEVAL_S *v;
1517
1518 if(!action)
1519 alpine_panic("NULL action to parse_action_slash");
1520 else if(!(str && *str))
1521 alpine_panic("NULL or empty string to parse_action_slash");
1522 else if(!strncmp(str, "/ROLE=1", 7))
1523 action->is_a_role = 1;
1524 else if(!strncmp(str, "/OTHER=1", 8))
1525 action->is_a_other = 1;
1526 else if(!strncmp(str, "/ISINCOL=1", 10))
1527 action->is_a_incol = 1;
1528 else if(!strncmp(str, "/ISSRCH=1", 9))
1529 action->is_a_srch = 1;
1530 /*
1531 * This is unfortunate. If a new filter is set to only set
1532 * state bits it will be interpreted by an older pine which
1533 * doesn't have that feature like a filter that is set to Delete.
1534 * So we change the filter indicator to FILTER=2 to disable the
1535 * filter for older versions.
1536 */
1537 else if(!strncmp(str, "/FILTER=1", 9) || !strncmp(str, "/FILTER=2", 9))
1538 action->is_a_filter = 1;
1539 else if(!strncmp(str, "/ISSCORE=1", 10))
1540 action->is_a_score = 1;
1541 else if(!strncmp(str, "/SCORE=", 7)){
1542 if((p = remove_pat_escapes(str+7)) != NULL){
1543 long i;
1544
1545 i = atol(p);
1546 if(i >= SCORE_MIN && i <= SCORE_MAX)
1547 action->scoreval = i;
1548
1549 fs_give((void **)&p);
1550 }
1551 }
1552 else if(!strncmp(str, "/SCOREHDRTOK=", 13))
1553 action->scorevalhdrtok = config_to_hdrtok(str+13);
1554 else if(!strncmp(str, "/FOLDER=", 8))
1555 action->folder = parse_pattern("FOLDER", str, 1);
1556 else if(!strncmp(str, "/KEYSET=", 8))
1557 action->keyword_set = parse_pattern("KEYSET", str, 1);
1558 else if(!strncmp(str, "/KEYCLR=", 8))
1559 action->keyword_clr = parse_pattern("KEYCLR", str, 1);
1560 else if(!strncmp(str, "/NOKILL=", 8))
1561 action->kill = -1;
1562 else if(!strncmp(str, "/NOTDEL=", 8))
1563 action->move_only_if_not_deleted = 1;
1564 else if(!strncmp(str, "/NONTERM=", 9))
1565 action->non_terminating = 1;
1566 else if(!strncmp(str, "/STATI=", 7)){
1567 stateval = ACT_STAT_LEAVE;
1568 SET_MSGSTATE(str,"/STATI=",stateval);
1569 switch(stateval){
1570 case ACT_STAT_LEAVE:
1571 break;
1572 case ACT_STAT_SET:
1573 action->state_setting_bits |= F_FLAG;
1574 break;
1575 case ACT_STAT_CLEAR:
1576 action->state_setting_bits |= F_UNFLAG;
1577 break;
1578 }
1579 }
1580 else if(!strncmp(str, "/STATD=", 7)){
1581 stateval = ACT_STAT_LEAVE;
1582 SET_MSGSTATE(str,"/STATD=",stateval);
1583 switch(stateval){
1584 case ACT_STAT_LEAVE:
1585 break;
1586 case ACT_STAT_SET:
1587 action->state_setting_bits |= F_DEL;
1588 break;
1589 case ACT_STAT_CLEAR:
1590 action->state_setting_bits |= F_UNDEL;
1591 break;
1592 }
1593 }
1594 else if(!strncmp(str, "/STATA=", 7)){
1595 stateval = ACT_STAT_LEAVE;
1596 SET_MSGSTATE(str,"/STATA=",stateval);
1597 switch(stateval){
1598 case ACT_STAT_LEAVE:
1599 break;
1600 case ACT_STAT_SET:
1601 action->state_setting_bits |= F_ANS;
1602 break;
1603 case ACT_STAT_CLEAR:
1604 action->state_setting_bits |= F_UNANS;
1605 break;
1606 }
1607 }
1608 else if(!strncmp(str, "/STATN=", 7)){
1609 stateval = ACT_STAT_LEAVE;
1610 SET_MSGSTATE(str,"/STATN=",stateval);
1611 switch(stateval){
1612 case ACT_STAT_LEAVE:
1613 break;
1614 case ACT_STAT_SET:
1615 action->state_setting_bits |= F_UNSEEN;
1616 break;
1617 case ACT_STAT_CLEAR:
1618 action->state_setting_bits |= F_SEEN;
1619 break;
1620 }
1621 }
1622 else if(!strncmp(str, "/RTYPE=", 7)){
1623 /* reply type */
1624 action->repl_type = ROLE_REPL_DEFL;
1625 if((p = remove_pat_escapes(str+7)) != NULL){
1626 for(i = 0; (v = role_repl_types(i)); i++)
1627 if(!strucmp(p, v->shortname)){
1628 action->repl_type = v->value;
1629 break;
1630 }
1631
1632 fs_give((void **)&p);
1633 }
1634 }
1635 else if(!strncmp(str, "/FTYPE=", 7)){
1636 /* forward type */
1637 action->forw_type = ROLE_FORW_DEFL;
1638 if((p = remove_pat_escapes(str+7)) != NULL){
1639 for(i = 0; (v = role_forw_types(i)); i++)
1640 if(!strucmp(p, v->shortname)){
1641 action->forw_type = v->value;
1642 break;
1643 }
1644
1645 fs_give((void **)&p);
1646 }
1647 }
1648 else if(!strncmp(str, "/CTYPE=", 7)){
1649 /* compose type */
1650 action->comp_type = ROLE_COMP_DEFL;
1651 if((p = remove_pat_escapes(str+7)) != NULL){
1652 for(i = 0; (v = role_comp_types(i)); i++)
1653 if(!strucmp(p, v->shortname)){
1654 action->comp_type = v->value;
1655 break;
1656 }
1657
1658 fs_give((void **)&p);
1659 }
1660 }
1661 else if(!strncmp(str, "/FROM=", 6)){
1662 /* get the from */
1663 if((p = remove_pat_escapes(str+6)) != NULL){
1664 rfc822_parse_adrlist(&action->from, p,
1665 ps_global->maildomain);
1666 fs_give((void **)&p);
1667 }
1668 }
1669 else if(!strncmp(str, "/REPL=", 6)){
1670 /* get the reply-to */
1671 if((p = remove_pat_escapes(str+6)) != NULL){
1672 rfc822_parse_adrlist(&action->replyto, p,
1673 ps_global->maildomain);
1674 fs_give((void **)&p);
1675 }
1676 }
1677 else if(!strncmp(str, "/FCC=", 5))
1678 action->fcc = remove_pat_escapes(str+5);
1679 else if(!strncmp(str, "/LSIG=", 6))
1680 action->litsig = remove_pat_escapes(str+6);
1681 else if(!strncmp(str, "/SIG=", 5))
1682 action->sig = remove_pat_escapes(str+5);
1683 else if(!strncmp(str, "/TEMPLATE=", 10))
1684 action->template = remove_pat_escapes(str+10);
1685 /* get the custom headers */
1686 else if(!strncmp(str, "/CSTM=", 6)){
1687 if((p = remove_pat_escapes(str+6)) != NULL){
1688 int commas = 0;
1689 char *q;
1690
1691 /* count elements in list */
1692 for(q = p; q && *q; q++)
1693 if(*q == ',')
1694 commas++;
1695
1696 action->cstm = parse_list(p, commas+1, 0, NULL);
1697 fs_give((void **)&p);
1698 }
1699 }
1700 else if(!strncmp(str, "/SMTP=", 6)){
1701 if((p = remove_pat_escapes(str+6)) != NULL){
1702 int commas = 0;
1703 char *q;
1704
1705 /* count elements in list */
1706 for(q = p; q && *q; q++)
1707 if(*q == ',')
1708 commas++;
1709
1710 action->smtp = parse_list(p, commas+1, PL_REMSURRQUOT, NULL);
1711 fs_give((void **)&p);
1712 }
1713 }
1714 else if(!strncmp(str, "/NNTP=", 6)){
1715 if((p = remove_pat_escapes(str+6)) != NULL){
1716 int commas = 0;
1717 char *q;
1718
1719 /* count elements in list */
1720 for(q = p; q && *q; q++)
1721 if(*q == ',')
1722 commas++;
1723
1724 action->nntp = parse_list(p, commas+1, PL_REMSURRQUOT, NULL);
1725 fs_give((void **)&p);
1726 }
1727 }
1728 else if(!strncmp(str, "/INICK=", 7))
1729 action->inherit_nick = remove_pat_escapes(str+7);
1730 else if(!strncmp(str, "/INCOL=", 7)){
1731 if((p = remove_pat_escapes(str+7)) != NULL){
1732 char *fg = NULL, *bg = NULL, *z;
1733
1734 /*
1735 * Color should look like
1736 * /FG=white/BG=red
1737 */
1738 if((z = srchstr(p, "/FG=")) != NULL)
1739 fg = remove_pat_escapes(z+4);
1740 if((z = srchstr(p, "/BG=")) != NULL)
1741 bg = remove_pat_escapes(z+4);
1742
1743 if(fg && *fg && bg && *bg)
1744 action->incol = new_color_pair(fg, bg);
1745
1746 if(fg)
1747 fs_give((void **)&fg);
1748 if(bg)
1749 fs_give((void **)&bg);
1750 fs_give((void **)&p);
1751 }
1752 }
1753 /* per-folder sort */
1754 else if(!strncmp(str, "/SORT=", 6)){
1755 if((p = remove_pat_escapes(str+6)) != NULL){
1756 SortOrder def_sort;
1757 int def_sort_rev;
1758
1759 if(decode_sort(p, &def_sort, &def_sort_rev) != -1){
1760 action->sort_is_set = 1;
1761 action->sortorder = def_sort;
1762 action->revsort = (def_sort_rev ? 1 : 0);
1763 }
1764
1765 fs_give((void **)&p);
1766 }
1767 }
1768 /* per-folder index-format */
1769 else if(!strncmp(str, "/IFORM=", 7))
1770 action->index_format = remove_pat_escapes(str+7);
1771 /* per-folder startup-rule */
1772 else if(!strncmp(str, "/START=", 7)){
1773 if((p = remove_pat_escapes(str+7)) != NULL){
1774 for(i = 0; (v = startup_rules(i)); i++)
1775 if(!strucmp(p, S_OR_L(v))){
1776 action->startup_rule = v->value;
1777 break;
1778 }
1779
1780 fs_give((void **)&p);
1781 }
1782 }
1783 else{
1784 char save = '\0';
1785
1786 action->bogus = 1;
1787
1788 if((p = strindex(str, '=')) != NULL){
1789 save = *(p+1);
1790 *(p+1) = '\0';
1791 }
1792
1793 dprint((1,
1794 "parse_action_slash(%.20s): unrecognized in \"%s\"\n",
1795 str ? str : "?",
1796 (action && action->nick) ? action->nick : ""));
1797 q_status_message4(SM_ORDER, 1, 3,
1798 "Warning: unrecognized pattern action \"%.20s\"%.20s%.20s%.20s",
1799 str, action->nick ? " in rule \"" : "",
1800 action->nick ? action->nick : "", action->nick ? "\"" : "");
1801
1802 if(p)
1803 *(p+1) = save;
1804 }
1805 }
1806
1807
1808 /*
1809 * Str looks like (min,max) or a comma-separated list of these.
1810 *
1811 * Parens are optional if unambiguous, whitespace is ignored.
1812 * If min is left out it is -INF. If max is left out it is INF.
1813 * If only one number and no comma number is min and max is INF.
1814 *
1815 * Returns the INTVL_S list.
1816 */
1817 INTVL_S *
parse_intvl(char * str)1818 parse_intvl(char *str)
1819 {
1820 char *q;
1821 long left, right;
1822 INTVL_S *ret = NULL, **next = NULL;
1823
1824 if(!str)
1825 return(ret);
1826
1827 q = str;
1828
1829 for(;;){
1830 left = right = INTVL_UNDEF;
1831
1832 /* skip to first number */
1833 while(isspace((unsigned char) *q) || *q == LPAREN)
1834 q++;
1835
1836 /* min number */
1837 if(*q == COMMA || !struncmp(q, "-INF", 4))
1838 left = - INTVL_INF;
1839 else if(*q == '-' || isdigit((unsigned char) *q))
1840 left = atol(q);
1841
1842 if(left != INTVL_UNDEF){
1843 /* skip to second number */
1844 while(*q && *q != COMMA && *q != RPAREN)
1845 q++;
1846 if(*q == COMMA)
1847 q++;
1848 while(isspace((unsigned char) *q))
1849 q++;
1850
1851 /* max number */
1852 if(*q == '\0' || *q == RPAREN || !struncmp(q, "INF", 3))
1853 right = INTVL_INF;
1854 else if(*q == '-' || isdigit((unsigned char) *q))
1855 right = atol(q);
1856 }
1857
1858 if(left == INTVL_UNDEF || right == INTVL_UNDEF
1859 || left > right){
1860 if(left != INTVL_UNDEF || right != INTVL_UNDEF || *q){
1861 if(left != INTVL_UNDEF && right != INTVL_UNDEF
1862 && left > right)
1863 q_status_message1(SM_ORDER, 3, 5,
1864 _("Error: Interval \"%s\", min > max"), str);
1865 else
1866 q_status_message1(SM_ORDER, 3, 5,
1867 _("Error: Interval \"%s\": syntax is (min,max)"), str);
1868
1869 if(ret)
1870 free_intvl(&ret);
1871
1872 ret = NULL;
1873 }
1874
1875 break;
1876 }
1877 else{
1878 if(!ret){
1879 ret = (INTVL_S *) fs_get(sizeof(*ret));
1880 memset((void *) ret, 0, sizeof(*ret));
1881 ret->imin = left;
1882 ret->imax = right;
1883 next = &ret->next;
1884 }
1885 else{
1886 *next = (INTVL_S *) fs_get(sizeof(*ret));
1887 memset((void *) *next, 0, sizeof(*ret));
1888 (*next)->imin = left;
1889 (*next)->imax = right;
1890 next = &(*next)->next;
1891 }
1892
1893 /* skip to next interval in list */
1894 while(*q && *q != COMMA && *q != RPAREN)
1895 q++;
1896 if(*q == RPAREN)
1897 q++;
1898 while(*q && *q != COMMA)
1899 q++;
1900 if(*q == COMMA)
1901 q++;
1902 }
1903 }
1904
1905 return(ret);
1906 }
1907
1908
1909 /*
1910 * Returns string that looks like "(left,right),(left2,right2)".
1911 * Caller is responsible for freeing memory.
1912 */
1913 char *
stringform_of_intvl(INTVL_S * intvl)1914 stringform_of_intvl(INTVL_S *intvl)
1915 {
1916 char *res = NULL;
1917
1918 if(intvl && intvl->imin != INTVL_UNDEF && intvl->imax != INTVL_UNDEF
1919 && intvl->imin <= intvl->imax){
1920 char lbuf[20], rbuf[20], buf[45], *p;
1921 INTVL_S *iv;
1922 int count = 0;
1923 size_t reslen;
1924
1925 /* find a max size and allocate it for the result */
1926 for(iv = intvl;
1927 (iv && iv->imin != INTVL_UNDEF && iv->imax != INTVL_UNDEF
1928 && iv->imin <= iv->imax);
1929 iv = iv->next)
1930 count++;
1931
1932 reslen = count * 50 * sizeof(char);
1933 res = (char *) fs_get(reslen+1);
1934 memset((void *) res, 0, reslen+1);
1935 p = res;
1936
1937 for(iv = intvl;
1938 (iv && iv->imin != INTVL_UNDEF && iv->imax != INTVL_UNDEF
1939 && iv->imin <= iv->imax);
1940 iv = iv->next){
1941
1942 if(iv->imin == - INTVL_INF){
1943 strncpy(lbuf, "-INF", sizeof(lbuf));
1944 lbuf[sizeof(lbuf)-1] = '\0';
1945 }
1946 else
1947 snprintf(lbuf, sizeof(lbuf), "%ld", iv->imin);
1948
1949 if(iv->imax == INTVL_INF){
1950 strncpy(rbuf, "INF", sizeof(rbuf));
1951 rbuf[sizeof(rbuf)-1] = '\0';
1952 }
1953 else
1954 snprintf(rbuf, sizeof(rbuf), "%ld", iv->imax);
1955
1956 snprintf(buf, sizeof(buf), "%.1s(%.20s,%.20s)", (p == res) ? "" : ",",
1957 lbuf, rbuf);
1958
1959 sstrncpy(&p, buf, reslen+1 -(p-res));
1960 }
1961
1962 res[reslen] = '\0';
1963 }
1964
1965 return(res);
1966 }
1967
1968
1969 char *
hdrtok_to_stringform(HEADER_TOK_S * hdrtok)1970 hdrtok_to_stringform(HEADER_TOK_S *hdrtok)
1971 {
1972 char buf[1024], nbuf[10];
1973 char *res = NULL;
1974 char *p, *ptr;
1975
1976 if(hdrtok){
1977 snprintf(nbuf, sizeof(nbuf), "%d", hdrtok->fieldnum);
1978 ptr = buf;
1979 sstrncpy(&ptr, hdrtok->hdrname ? hdrtok->hdrname : "", sizeof(buf)-(ptr-buf));
1980 sstrncpy(&ptr, "(", sizeof(buf)-(ptr-buf));
1981 sstrncpy(&ptr, nbuf, sizeof(buf)-(ptr-buf));
1982 sstrncpy(&ptr, ",\"", sizeof(buf)-(ptr-buf));
1983 p = hdrtok->fieldseps;
1984 while(p && *p){
1985 if((*p == '\"' || *p == '\\') && ptr-buf < sizeof(buf))
1986 *ptr++ = '\\';
1987
1988 if(ptr-buf < sizeof(buf))
1989 *ptr++ = *p++;
1990 }
1991
1992 sstrncpy(&ptr, "\")", sizeof(buf)-(ptr-buf));
1993
1994 if(ptr-buf < sizeof(buf))
1995 *ptr = '\0';
1996 else
1997 buf[sizeof(buf)-1] = '\0';
1998
1999 res = cpystr(buf);
2000 }
2001
2002 return(res);
2003 }
2004
2005
2006 HEADER_TOK_S *
stringform_to_hdrtok(char * str)2007 stringform_to_hdrtok(char *str)
2008 {
2009 char *p, *q, *w, hdrname[200];
2010 HEADER_TOK_S *hdrtok = NULL;
2011
2012 if(str && *str){
2013 p = str;
2014 hdrname[0] = '\0';
2015 w = hdrname;
2016
2017 if(*p == '\"'){ /* quoted name */
2018 p++;
2019 while(w < hdrname + sizeof(hdrname)-1 && *p != '\"'){
2020 if(*p == '\\')
2021 p++;
2022
2023 *w++ = *p++;
2024 }
2025
2026 *w = '\0';
2027 if(*p == '\"')
2028 p++;
2029 }
2030 else{
2031 while(w < hdrname + sizeof(hdrname)-1 &&
2032 !(!(*p & 0x80) && isspace((unsigned char)*p)) &&
2033 *p != '(')
2034 *w++ = *p++;
2035
2036 *w = '\0';
2037 }
2038
2039 if(hdrname[0])
2040 hdrtok = new_hdrtok(hdrname);
2041
2042 if(hdrtok){
2043 if(*p == '('){
2044 p++;
2045
2046 if(*p && isdigit((unsigned char) *p)){
2047 q = p;
2048 while(*p && isdigit((unsigned char) *p))
2049 p++;
2050
2051 hdrtok->fieldnum = atoi(q);
2052
2053 if(*p == ','){
2054 int j;
2055
2056 p++;
2057 /* don't use default */
2058 if(*p && *p != ')' && hdrtok->fieldseps){
2059 hdrtok->fieldseps[0] = '\0';
2060 hdrtok->fieldsepcnt = 0;
2061 }
2062
2063 j = 0;
2064 if(*p == '\"' && strchr(p+1, '\"')){ /* quoted */
2065 p++;
2066 while(*p && *p != '\"'){
2067 if(hdrtok->fieldseps)
2068 fs_resize((void **) &hdrtok->fieldseps, j+2);
2069
2070 if(*p == '\\' && *(p+1))
2071 p++;
2072
2073 if(hdrtok->fieldseps){
2074 hdrtok->fieldseps[j++] = *p++;
2075 hdrtok->fieldseps[j] = '\0';
2076 hdrtok->fieldsepcnt = j;
2077 }
2078 }
2079 }
2080 else{
2081 while(*p && *p != ')'){
2082 if(hdrtok->fieldseps)
2083 fs_resize((void **) &hdrtok->fieldseps, j+2);
2084
2085 if(*p == '\\' && *(p+1))
2086 p++;
2087
2088 if(hdrtok->fieldseps){
2089 hdrtok->fieldseps[j++] = *p++;
2090 hdrtok->fieldseps[j] = '\0';
2091 hdrtok->fieldsepcnt = j;
2092 }
2093 }
2094 }
2095 }
2096 else{
2097 q_status_message(SM_ORDER | SM_DING, 3, 3, "Missing 2nd argument should be field number, a non-negative digit");
2098 }
2099 }
2100 else{
2101 q_status_message(SM_ORDER | SM_DING, 3, 3, "1st argument should be field number, a non-negative digit");
2102 }
2103 }
2104 else{
2105 q_status_message1(SM_ORDER | SM_DING, 3, 3, "Missing left parenthesis in %s", str);
2106 }
2107 }
2108 else{
2109 q_status_message1(SM_ORDER | SM_DING, 3, 3, "Missing header name in %s", str);
2110 }
2111 }
2112
2113 return(hdrtok);
2114 }
2115
2116
2117 char *
hdrtok_to_config(HEADER_TOK_S * hdrtok)2118 hdrtok_to_config(HEADER_TOK_S *hdrtok)
2119 {
2120 char *ptr, buf[1024], nbuf[10], *p1, *p2, *p3;
2121 char *res = NULL;
2122
2123 if(hdrtok){
2124 snprintf(nbuf, sizeof(nbuf), "%d", hdrtok->fieldnum);
2125 memset(buf, 0, sizeof(buf));
2126 ptr = buf;
2127 sstrncpy(&ptr, "/HN=", sizeof(buf)-(ptr-buf));
2128 sstrncpy(&ptr, (p1=add_pat_escapes(hdrtok->hdrname ? hdrtok->hdrname : "")), sizeof(buf)-(ptr-buf));
2129 sstrncpy(&ptr, "/FN=", sizeof(buf)-(ptr-buf));
2130 sstrncpy(&ptr, (p2=add_pat_escapes(nbuf)), sizeof(buf)-(ptr-buf));
2131 sstrncpy(&ptr, "/FS=", sizeof(buf)-(ptr-buf));
2132 sstrncpy(&ptr, (p3=add_pat_escapes(hdrtok->fieldseps ? hdrtok->fieldseps : "")), sizeof(buf)-(ptr-buf));
2133
2134 buf[sizeof(buf)-1] = '\0';
2135 res = add_pat_escapes(buf);
2136
2137 if(p1)
2138 fs_give((void **)&p1);
2139
2140 if(p2)
2141 fs_give((void **)&p2);
2142
2143 if(p3)
2144 fs_give((void **)&p3);
2145 }
2146
2147 return(res);
2148 }
2149
2150
2151 HEADER_TOK_S *
config_to_hdrtok(char * str)2152 config_to_hdrtok(char *str)
2153 {
2154 HEADER_TOK_S *hdrtok = NULL;
2155 char *p, *q;
2156 int j;
2157
2158 if(str && *str){
2159 if((q = remove_pat_escapes(str)) != NULL){
2160 char *hn = NULL, *fn = NULL, *fs = NULL, *z;
2161
2162 if((z = srchstr(q, "/HN=")) != NULL)
2163 hn = remove_pat_escapes(z+4);
2164 if((z = srchstr(q, "/FN=")) != NULL)
2165 fn = remove_pat_escapes(z+4);
2166 if((z = srchstr(q, "/FS=")) != NULL)
2167 fs = remove_pat_escapes(z+4);
2168
2169 hdrtok = new_hdrtok(hn);
2170 if(fn)
2171 hdrtok->fieldnum = atoi(fn);
2172
2173 if(fs && *fs){
2174 if(hdrtok->fieldseps){
2175 hdrtok->fieldseps[0] = '\0';
2176 hdrtok->fieldsepcnt = 0;
2177 }
2178
2179 p = fs;
2180 j = 0;
2181 if(*p == '\"' && strchr(p+1, '\"')){
2182 p++;
2183 while(*p && *p != '\"'){
2184 if(hdrtok->fieldseps)
2185 fs_resize((void **) &hdrtok->fieldseps, j+2);
2186
2187 if(*p == '\\' && *(p+1))
2188 p++;
2189
2190 if(hdrtok->fieldseps){
2191 hdrtok->fieldseps[j++] = *p++;
2192 hdrtok->fieldseps[j] = '\0';
2193 hdrtok->fieldsepcnt = j;
2194 }
2195 }
2196 }
2197 else{
2198 while(*p){
2199 if(hdrtok->fieldseps)
2200 fs_resize((void **) &hdrtok->fieldseps, j+2);
2201
2202 if(*p == '\\' && *(p+1))
2203 p++;
2204
2205 if(hdrtok->fieldseps){
2206 hdrtok->fieldseps[j++] = *p++;
2207 hdrtok->fieldseps[j] = '\0';
2208 hdrtok->fieldsepcnt = j;
2209 }
2210 }
2211 }
2212 }
2213
2214 if(hn)
2215 fs_give((void **)&hn);
2216 if(fn)
2217 fs_give((void **)&fn);
2218 if(fs)
2219 fs_give((void **)&fs);
2220
2221 fs_give((void **)&q);
2222 }
2223 }
2224
2225 return(hdrtok);
2226 }
2227
2228
2229 /*
2230 * Args -- flags - SCOREUSE_INVALID Mark scores_in_use invalid so that we'll
2231 * recalculate if we want to use it again.
2232 * - SCOREUSE_GET Return whether scores are being used or not.
2233 *
2234 * Returns -- 0 - Scores not being used at all.
2235 * >0 - Scores are used. The return value consists of flag values
2236 * OR'd together. Possible values are:
2237 *
2238 * SCOREUSE_INCOLS - scores needed for index line colors
2239 * SCOREUSE_ROLES - scores needed for roles
2240 * SCOREUSE_FILTERS - scores needed for filters
2241 * SCOREUSE_OTHER - scores needed for other stuff
2242 * SCOREUSE_INDEX - scores needed for index drawing
2243 *
2244 * SCOREUSE_STATEDEP - scores depend on message state
2245 */
2246 int
scores_are_used(int flags)2247 scores_are_used(int flags)
2248 {
2249 static int scores_in_use = -1;
2250 long type1, type2;
2251 int scores_are_defined, scores_are_used_somewhere = 0;
2252 PAT_STATE pstate1, pstate2;
2253
2254 if(flags & SCOREUSE_INVALID) /* mark invalid so we recalculate next time */
2255 scores_in_use = -1;
2256 else if(scores_in_use == -1){
2257
2258 /*
2259 * Check the patterns to see if scores are potentially
2260 * being used.
2261 * The first_pattern() in the if checks whether there are any
2262 * non-zero scorevals. The loop checks whether any patterns
2263 * use those non-zero scorevals.
2264 */
2265 type1 = ROLE_SCORE;
2266 type2 = (ROLE_REPLY | ROLE_FORWARD | ROLE_COMPOSE |
2267 ROLE_INCOL | ROLE_DO_FILTER);
2268 scores_are_defined = nonempty_patterns(type1, &pstate1)
2269 && first_pattern(&pstate1);
2270 if(scores_are_defined)
2271 scores_are_used_somewhere =
2272 ((nonempty_patterns(type2, &pstate2) && first_pattern(&pstate2))
2273 || ps_global->a_format_contains_score
2274 || mn_get_sort(ps_global->msgmap) == SortScore);
2275
2276 if(scores_are_used_somewhere){
2277 PAT_S *pat;
2278
2279 /*
2280 * Careful. nonempty_patterns() may call close_pattern()
2281 * which will set scores_in_use to -1! So we have to be
2282 * sure to reset it after we call nonempty_patterns().
2283 */
2284 scores_in_use = 0;
2285 if(ps_global->a_format_contains_score
2286 || mn_get_sort(ps_global->msgmap) == SortScore)
2287 scores_in_use |= SCOREUSE_INDEX;
2288
2289 if(nonempty_patterns(type2, &pstate2))
2290 for(pat = first_pattern(&pstate2);
2291 pat;
2292 pat = next_pattern(&pstate2))
2293 if(pat->patgrp && !pat->patgrp->bogus && pat->patgrp->do_score){
2294 if(pat->action && pat->action->is_a_incol)
2295 scores_in_use |= SCOREUSE_INCOLS;
2296 if(pat->action && pat->action->is_a_role)
2297 scores_in_use |= SCOREUSE_ROLES;
2298 if(pat->action && pat->action->is_a_filter)
2299 scores_in_use |= SCOREUSE_FILTERS;
2300 if(pat->action && pat->action->is_a_other)
2301 scores_in_use |= SCOREUSE_OTHER;
2302 }
2303
2304 /*
2305 * Note whether scores depend on message state or not.
2306 */
2307 if(scores_in_use)
2308 for(pat = first_pattern(&pstate1);
2309 pat;
2310 pat = next_pattern(&pstate1))
2311 if(patgrp_depends_on_active_state(pat->patgrp)){
2312 scores_in_use |= SCOREUSE_STATEDEP;
2313 break;
2314 }
2315
2316 }
2317 else
2318 scores_in_use = 0;
2319 }
2320
2321 return((scores_in_use == -1) ? 0 : scores_in_use);
2322 }
2323
2324
2325 int
patgrp_depends_on_state(PATGRP_S * patgrp)2326 patgrp_depends_on_state(PATGRP_S *patgrp)
2327 {
2328 return(patgrp && (patgrp_depends_on_active_state(patgrp)
2329 || patgrp->stat_rec != PAT_STAT_EITHER));
2330 }
2331
2332
2333 /*
2334 * Recent doesn't count for this function because it doesn't change while
2335 * the mailbox is open.
2336 */
2337 int
patgrp_depends_on_active_state(PATGRP_S * patgrp)2338 patgrp_depends_on_active_state(PATGRP_S *patgrp)
2339 {
2340 return(patgrp && !patgrp->bogus
2341 && (patgrp->stat_new != PAT_STAT_EITHER ||
2342 patgrp->stat_del != PAT_STAT_EITHER ||
2343 patgrp->stat_imp != PAT_STAT_EITHER ||
2344 patgrp->stat_ans != PAT_STAT_EITHER ||
2345 patgrp->keyword));
2346 }
2347
2348
2349 /*
2350 * Look for label in str and return a pointer to parsed string.
2351 * Actually, we look for "label=" or "!label=", the second means NOT.
2352 * Converts from string from patterns file which looks like
2353 * /NEWS=comp.mail.,comp.mail.pine/TO=...
2354 * This is the string that came from pattern="string" with the pattern=
2355 * and outer quotes removed.
2356 * This converts the string to a PATTERN_S list and returns
2357 * an allocated copy.
2358 */
2359 PATTERN_S *
parse_pattern(char * label,char * str,int hex_to_backslashed)2360 parse_pattern(char *label, char *str, int hex_to_backslashed)
2361 {
2362 char copy[50]; /* local copy of label */
2363 char copynot[50]; /* local copy of label, NOT'ed */
2364 char *q, *labeled_str;
2365 PATTERN_S *head = NULL;
2366
2367 if(!label || !str)
2368 return(NULL);
2369
2370 q = copy;
2371 sstrncpy(&q, "/", sizeof(copy));
2372 sstrncpy(&q, label, sizeof(copy) - (q-copy));
2373 sstrncpy(&q, "=", sizeof(copy) - (q-copy));
2374 copy[sizeof(copy)-1] = '\0';
2375 q = copynot;
2376 sstrncpy(&q, "/!", sizeof(copynot));
2377 sstrncpy(&q, label, sizeof(copynot) - (q-copynot));
2378 sstrncpy(&q, "=", sizeof(copynot) - (q-copynot));
2379 copynot[sizeof(copynot)-1] = '\0';
2380
2381 if(hex_to_backslashed){
2382 if((q = srchstr(str, copy)) != NULL){
2383 head = config_to_pattern(q+strlen(copy));
2384 }
2385 else if((q = srchstr(str, copynot)) != NULL){
2386 head = config_to_pattern(q+strlen(copynot));
2387 head->not = 1;
2388 }
2389 }
2390 else{
2391 if((q = srchstr(str, copy)) != NULL){
2392 if((labeled_str =
2393 remove_backslash_escapes(q+strlen(copy))) != NULL){
2394 head = string_to_pattern(labeled_str);
2395 fs_give((void **)&labeled_str);
2396 }
2397 }
2398 else if((q = srchstr(str, copynot)) != NULL){
2399 if((labeled_str =
2400 remove_backslash_escapes(q+strlen(copynot))) != NULL){
2401 head = string_to_pattern(labeled_str);
2402 head->not = 1;
2403 fs_give((void **)&labeled_str);
2404 }
2405 }
2406 }
2407
2408 return(head);
2409 }
2410
2411
2412 /*
2413 * Look for /ARB's in str and return a pointer to parsed ARBHDR_S.
2414 * Actually, we look for /!ARB and /!EARB as well. Those mean NOT.
2415 * Converts from string from patterns file which looks like
2416 * /ARB<fieldname1>=pattern/.../ARB<fieldname2>=pattern...
2417 * This is the string that came from pattern="string" with the pattern=
2418 * and outer quotes removed.
2419 * This converts the string to a ARBHDR_S list and returns
2420 * an allocated copy.
2421 */
2422 ARBHDR_S *
parse_arbhdr(char * str)2423 parse_arbhdr(char *str)
2424 {
2425 char *q, *s, *equals, *noesc;
2426 int not, empty, skip;
2427 ARBHDR_S *ahdr = NULL, *a, *aa;
2428 PATTERN_S *p = NULL;
2429
2430 if(!str)
2431 return(NULL);
2432
2433 aa = NULL;
2434 for(s = str; (q = next_arb(s)); s = q+1){
2435 not = (q[1] == '!') ? 1 : 0;
2436 empty = (q[not+1] == 'E') ? 1 : 0;
2437 skip = 4 + not + empty;
2438 if((noesc = remove_pat_escapes(q+skip)) != NULL){
2439 if(*noesc != '=' && (equals = strindex(noesc, '=')) != NULL){
2440 a = (ARBHDR_S *)fs_get(sizeof(*a));
2441 memset((void *)a, 0, sizeof(*a));
2442 *equals = '\0';
2443 a->isemptyval = empty;
2444 a->field = cpystr(noesc);
2445 if(empty)
2446 a->p = string_to_pattern("");
2447 else if(*(equals+1) &&
2448 (p = string_to_pattern(equals+1)) != NULL)
2449 a->p = p;
2450
2451 if(not && a->p)
2452 a->p->not = 1;
2453
2454 /* keep them in the same order */
2455 if(aa){
2456 aa->next = a;
2457 aa = aa->next;
2458 }
2459 else{
2460 ahdr = a;
2461 aa = ahdr;
2462 }
2463 }
2464
2465 fs_give((void **)&noesc);
2466 }
2467 }
2468
2469 return(ahdr);
2470 }
2471
2472
2473 char *
next_arb(char * start)2474 next_arb(char *start)
2475 {
2476 char *q1, *q2, *q3, *q4, *p;
2477
2478 q1 = srchstr(start, "/ARB");
2479 q2 = srchstr(start, "/!ARB");
2480 q3 = srchstr(start, "/EARB");
2481 q4 = srchstr(start, "/!EARB");
2482
2483 p = q1;
2484 if(!p || (q2 && q2 < p))
2485 p = q2;
2486 if(!p || (q3 && q3 < p))
2487 p = q3;
2488 if(!p || (q4 && q4 < p))
2489 p = q4;
2490
2491 return(p);
2492 }
2493
2494
2495 /*
2496 * Converts a string to a PATTERN_S list and returns an
2497 * allocated copy. The source string looks like
2498 * string1,string2,...
2499 * Commas and backslashes may be backslash-escaped in the original string
2500 * in order to include actual commas and backslashes in the pattern.
2501 * So \, is an actual comma and , is the separator character.
2502 */
2503 PATTERN_S *
string_to_pattern(char * str)2504 string_to_pattern(char *str)
2505 {
2506 char *q, *s, *workspace;
2507 PATTERN_S *p, *head = NULL, **nextp;
2508
2509 if(!str)
2510 return(head);
2511
2512 /*
2513 * We want an empty string to cause an empty substring in the pattern
2514 * instead of returning a NULL pattern. That can be used as a way to
2515 * match any header. For example, if all the patterns but the news
2516 * pattern were null and the news pattern was a substring of "" then
2517 * we use that to match any message with a newsgroups header.
2518 */
2519 if(!*str){
2520 head = (PATTERN_S *)fs_get(sizeof(*p));
2521 memset((void *)head, 0, sizeof(*head));
2522 head->substring = cpystr("");
2523 }
2524 else{
2525 nextp = &head;
2526 workspace = (char *)fs_get((strlen(str)+1) * sizeof(char));
2527 s = workspace;
2528 *s = '\0';
2529 q = str;
2530 do {
2531 switch(*q){
2532 case COMMA:
2533 case '\0':
2534 *s = '\0';
2535 removing_leading_and_trailing_white_space(workspace);
2536 p = (PATTERN_S *)fs_get(sizeof(*p));
2537 memset((void *)p, 0, sizeof(*p));
2538 p->substring = cpystr(workspace);
2539
2540 convert_possibly_encoded_str_to_utf8(&p->substring);
2541
2542 *nextp = p;
2543 nextp = &p->next;
2544 s = workspace;
2545 *s = '\0';
2546 break;
2547
2548 case BSLASH:
2549 if(*(q+1) == COMMA || *(q+1) == BSLASH)
2550 *s++ = *(++q);
2551 else
2552 *s++ = *q;
2553
2554 break;
2555
2556 default:
2557 *s++ = *q;
2558 break;
2559 }
2560 } while(*q++);
2561
2562 fs_give((void **)&workspace);
2563 }
2564
2565 return(head);
2566 }
2567
2568
2569 /*
2570 * Converts a PATTERN_S list to a string.
2571 * The resulting string is allocated here and looks like
2572 * string1,string2,...
2573 * Commas and backslashes in the original pattern
2574 * end up backslash-escaped in the string.
2575 */
2576 char *
pattern_to_string(PATTERN_S * pattern)2577 pattern_to_string(PATTERN_S *pattern)
2578 {
2579 PATTERN_S *p;
2580 char *result = NULL, *q, *s;
2581 size_t n;
2582
2583 if(!pattern)
2584 return(result);
2585
2586 /* how much space is needed? */
2587 n = 0;
2588 for(p = pattern; p; p = p->next){
2589 n += (p == pattern) ? 0 : 1;
2590 for(s = p->substring; s && *s; s++){
2591 if(*s == COMMA || *s == BSLASH)
2592 n++;
2593
2594 n++;
2595 }
2596 }
2597
2598 q = result = (char *)fs_get(++n);
2599 for(p = pattern; p; p = p->next){
2600 if(p != pattern)
2601 *q++ = COMMA;
2602
2603 for(s = p->substring; s && *s; s++){
2604 if(*s == COMMA || *s == BSLASH)
2605 *q++ = '\\';
2606
2607 *q++ = *s;
2608 }
2609 }
2610
2611 *q = '\0';
2612
2613 return(result);
2614 }
2615
2616
2617 /*
2618 * Do the escaping necessary to take a string for a pattern into a comma-
2619 * separated string with escapes suitable for the config file.
2620 * Returns an allocated copy of that string.
2621 * In particular
2622 * , -> \, -> \x2C
2623 * \ -> \\ -> \x5C
2624 * " -> \"
2625 * / -> \/
2626 */
2627 char *
pattern_to_config(PATTERN_S * pat)2628 pattern_to_config(PATTERN_S *pat)
2629 {
2630 char *s, *res = NULL;
2631
2632 s = pattern_to_string(pat);
2633 if(s){
2634 res = add_pat_escapes(s);
2635 fs_give((void **) &s);
2636 }
2637
2638 return(res);
2639 }
2640
2641 /*
2642 * Opposite of pattern_to_config.
2643 */
2644 PATTERN_S *
config_to_pattern(char * str)2645 config_to_pattern(char *str)
2646 {
2647 char *s;
2648 PATTERN_S *pat = NULL;
2649
2650 s = remove_pat_escapes(str);
2651 if(s){
2652 pat = string_to_pattern(s);
2653 fs_give((void **) &s);
2654 }
2655
2656 return(pat);
2657 }
2658
2659
2660 /*
2661 * Converts an array of strings to a PATTERN_S list and returns an
2662 * allocated copy.
2663 * The list strings may not contain commas directly, because the UI turns
2664 * those into separate list members. Instead, the user types \, and
2665 * that backslash comma is converted to a comma here.
2666 * It is a bit odd. Backslash itself is not escaped. A backslash which is
2667 * not followed by a comma is a literal backslash, a backslash followed by
2668 * a comma is a comma.
2669 */
2670 PATTERN_S *
editlist_to_pattern(char ** list)2671 editlist_to_pattern(char **list)
2672 {
2673 PATTERN_S *head = NULL;
2674
2675 if(!(list && *list))
2676 return(head);
2677
2678 /*
2679 * We want an empty string to cause an empty substring in the pattern
2680 * instead of returning a NULL pattern. That can be used as a way to
2681 * match any header. For example, if all the patterns but the news
2682 * pattern were null and the news pattern was a substring of "" then
2683 * we use that to match any message with a newsgroups header.
2684 */
2685 if(!list[0][0]){
2686 head = (PATTERN_S *) fs_get(sizeof(*head));
2687 memset((void *) head, 0, sizeof(*head));
2688 head->substring = cpystr("");
2689 }
2690 else{
2691 char *str, *s, *q, *workspace = NULL;
2692 size_t l = 0;
2693 PATTERN_S *p, **nextp;
2694 int i;
2695
2696 nextp = &head;
2697 for(i = 0; (str = list[i]); i++){
2698 if(str[0]){
2699 if(!workspace){
2700 l = strlen(str) + 1;
2701 workspace = (char *) fs_get(l * sizeof(char));
2702 }
2703 else if(strlen(str) + 1 > l){
2704 l = strlen(str) + 1;
2705 fs_give((void **) &workspace);
2706 workspace = (char *) fs_get(l * sizeof(char));
2707 }
2708
2709 s = workspace;
2710 *s = '\0';
2711 q = str;
2712 do {
2713 switch(*q){
2714 case '\0':
2715 *s = '\0';
2716 removing_leading_and_trailing_white_space(workspace);
2717 p = (PATTERN_S *) fs_get(sizeof(*p));
2718 memset((void *) p, 0, sizeof(*p));
2719 p->substring = cpystr(workspace);
2720 *nextp = p;
2721 nextp = &p->next;
2722 s = workspace;
2723 *s = '\0';
2724 break;
2725
2726 case BSLASH:
2727 if(*(q+1) == COMMA)
2728 *s++ = *(++q);
2729 else
2730 *s++ = *q;
2731
2732 break;
2733
2734 default:
2735 *s++ = *q;
2736 break;
2737 }
2738 } while(*q++);
2739 }
2740 }
2741 }
2742
2743 return(head);
2744 }
2745
2746
2747 /*
2748 * Converts a PATTERN_S to an array of strings and returns an allocated copy.
2749 * Commas are converted to backslash-comma, because the text_tool UI uses
2750 * commas to separate items.
2751 * It is a bit odd. Backslash itself is not escaped. A backslash which is
2752 * not followed by a comma is a literal backslash, a backslash followed by
2753 * a comma is a comma.
2754 */
2755 char **
pattern_to_editlist(PATTERN_S * pat)2756 pattern_to_editlist(PATTERN_S *pat)
2757 {
2758 int cnt, i;
2759 PATTERN_S *p;
2760 char **list = NULL;
2761
2762 if(!pat)
2763 return(list);
2764
2765 /* how many in list? */
2766 for(cnt = 0, p = pat; p; p = p->next)
2767 cnt++;
2768
2769 list = (char **) fs_get((cnt + 1) * sizeof(*list));
2770 memset((void *) list, 0, (cnt + 1) * sizeof(*list));
2771
2772 for(i = 0, p = pat; p; p = p->next, i++)
2773 list[i] = add_comma_escapes(p->substring);
2774
2775 return(list);
2776 }
2777
2778
2779 PATGRP_S *
nick_to_patgrp(char * nick,int rflags)2780 nick_to_patgrp(char *nick, int rflags)
2781 {
2782 PAT_S *pat;
2783 PAT_STATE pstate;
2784 PATGRP_S *patgrp = NULL;
2785
2786 if(!(nick && *nick
2787 && nonempty_patterns(rflags, &pstate) && first_pattern(&pstate)))
2788 return(patgrp);
2789
2790 for(pat = first_pattern(&pstate);
2791 !patgrp && pat;
2792 pat = next_pattern(&pstate))
2793 if(pat->patgrp && pat->patgrp->nick && !strcmp(pat->patgrp->nick, nick))
2794 patgrp = copy_patgrp(pat->patgrp);
2795
2796 return(patgrp);
2797 }
2798
2799
2800 /*
2801 * Must be called with a pstate, we don't check for it.
2802 * It respects the cur_rflag_num in pstate. That is, it doesn't start over
2803 * at i=1, it starts at cur_rflag_num.
2804 */
2805 PAT_S *
first_any_pattern(PAT_STATE * pstate)2806 first_any_pattern(PAT_STATE *pstate)
2807 {
2808 PAT_LINE_S *patline = NULL;
2809 int i;
2810 long local_rflag;
2811
2812 /*
2813 * The rest of pstate should be set before coming here.
2814 * In particular, the rflags should be set by a call to nonempty_patterns
2815 * or any_patterns, and cur_rflag_num should be set.
2816 */
2817 pstate->patlinecurrent = NULL;
2818 pstate->patcurrent = NULL;
2819
2820 /*
2821 * The order of these is important. It is the same as the order
2822 * used for next_any_pattern and opposite of the order used by
2823 * last and prev. For next_any's benefit, we allow cur_rflag_num to
2824 * start us out past the first set.
2825 */
2826 for(i = pstate->cur_rflag_num; i <= PATTERN_N; i++){
2827
2828 local_rflag = 0L;
2829
2830 switch(i){
2831 case 1:
2832 local_rflag = ROLE_DO_SRCH & CANONICAL_RFLAGS(pstate->rflags);
2833 break;
2834
2835 case 2:
2836 local_rflag = ROLE_DO_INCOLS & CANONICAL_RFLAGS(pstate->rflags);
2837 break;
2838
2839 case 3:
2840 local_rflag = ROLE_DO_ROLES & CANONICAL_RFLAGS(pstate->rflags);
2841 break;
2842
2843 case 4:
2844 local_rflag = ROLE_DO_FILTER & CANONICAL_RFLAGS(pstate->rflags);
2845 break;
2846
2847 case 5:
2848 local_rflag = ROLE_DO_SCORES & CANONICAL_RFLAGS(pstate->rflags);
2849 break;
2850
2851 case 6:
2852 local_rflag = ROLE_DO_OTHER & CANONICAL_RFLAGS(pstate->rflags);
2853 break;
2854
2855 case 7:
2856 local_rflag = ROLE_OLD_FILT & CANONICAL_RFLAGS(pstate->rflags);
2857 break;
2858
2859 case 8:
2860 local_rflag = ROLE_OLD_SCORE & CANONICAL_RFLAGS(pstate->rflags);
2861 break;
2862
2863 case PATTERN_N:
2864 local_rflag = ROLE_OLD_PAT & CANONICAL_RFLAGS(pstate->rflags);
2865 break;
2866 }
2867
2868 if(local_rflag){
2869 SET_PATTYPE(local_rflag | (pstate->rflags & PAT_USE_MASK));
2870
2871 if(*cur_pat_h){
2872 /* Find first patline with a pat */
2873 for(patline = (*cur_pat_h)->patlinehead;
2874 patline && !patline->first;
2875 patline = patline->next)
2876 ;
2877 }
2878
2879 if(patline){
2880 pstate->cur_rflag_num = i;
2881 pstate->patlinecurrent = patline;
2882 pstate->patcurrent = patline->first;
2883 }
2884 }
2885
2886 if(pstate->patcurrent)
2887 break;
2888 }
2889
2890 return(pstate->patcurrent);
2891 }
2892
2893
2894 /*
2895 * Return first pattern of the specified types. These types were set by a
2896 * previous call to any_patterns or nonempty_patterns.
2897 *
2898 * Args -- pstate pattern state. This is set here and passed back for
2899 * use by next_pattern. Must be non-null.
2900 * It must have been initialized previously by a call to
2901 * nonempty_patterns or any_patterns.
2902 */
2903 PAT_S *
first_pattern(PAT_STATE * pstate)2904 first_pattern(PAT_STATE *pstate)
2905 {
2906 PAT_S *pat;
2907 long rflags;
2908
2909 pstate->cur_rflag_num = 1;
2910
2911 rflags = pstate->rflags;
2912
2913 for(pat = first_any_pattern(pstate);
2914 pat && !((pat->action &&
2915 ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
2916 (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
2917 pat->action->is_a_incol) ||
2918 (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
2919 (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
2920 (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
2921 (rflags & ROLE_SCORE && (pat->action->scoreval
2922 || pat->action->scorevalhdrtok)) ||
2923 (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
2924 (rflags & ROLE_REPLY &&
2925 (pat->action->repl_type == ROLE_REPL_YES ||
2926 pat->action->repl_type == ROLE_REPL_NOCONF)) ||
2927 (rflags & ROLE_FORWARD &&
2928 (pat->action->forw_type == ROLE_FORW_YES ||
2929 pat->action->forw_type == ROLE_FORW_NOCONF)) ||
2930 (rflags & ROLE_COMPOSE &&
2931 (pat->action->comp_type == ROLE_COMP_YES ||
2932 pat->action->comp_type == ROLE_COMP_NOCONF)) ||
2933 (rflags & ROLE_OLD_FILT) ||
2934 (rflags & ROLE_OLD_SCORE) ||
2935 (rflags & ROLE_OLD_PAT)))
2936 ||
2937 pat->inherit);
2938 pat = next_any_pattern(pstate))
2939 ;
2940
2941 return(pat);
2942 }
2943
2944
2945 /*
2946 * Just like first_any_pattern.
2947 */
2948 PAT_S *
last_any_pattern(PAT_STATE * pstate)2949 last_any_pattern(PAT_STATE *pstate)
2950 {
2951 PAT_LINE_S *patline = NULL;
2952 int i;
2953 long local_rflag;
2954
2955 /*
2956 * The rest of pstate should be set before coming here.
2957 * In particular, the rflags should be set by a call to nonempty_patterns
2958 * or any_patterns, and cur_rflag_num should be set.
2959 */
2960 pstate->patlinecurrent = NULL;
2961 pstate->patcurrent = NULL;
2962
2963 for(i = pstate->cur_rflag_num; i >= 1; i--){
2964
2965 local_rflag = 0L;
2966
2967 switch(i){
2968 case 1:
2969 local_rflag = ROLE_DO_SRCH & CANONICAL_RFLAGS(pstate->rflags);
2970 break;
2971
2972 case 2:
2973 local_rflag = ROLE_DO_INCOLS & CANONICAL_RFLAGS(pstate->rflags);
2974 break;
2975
2976 case 3:
2977 local_rflag = ROLE_DO_ROLES & CANONICAL_RFLAGS(pstate->rflags);
2978 break;
2979
2980 case 4:
2981 local_rflag = ROLE_DO_FILTER & CANONICAL_RFLAGS(pstate->rflags);
2982 break;
2983
2984 case 5:
2985 local_rflag = ROLE_DO_SCORES & CANONICAL_RFLAGS(pstate->rflags);
2986 break;
2987
2988 case 6:
2989 local_rflag = ROLE_DO_OTHER & CANONICAL_RFLAGS(pstate->rflags);
2990 break;
2991
2992 case 7:
2993 local_rflag = ROLE_OLD_FILT & CANONICAL_RFLAGS(pstate->rflags);
2994 break;
2995
2996 case 8:
2997 local_rflag = ROLE_OLD_SCORE & CANONICAL_RFLAGS(pstate->rflags);
2998 break;
2999
3000 case PATTERN_N:
3001 local_rflag = ROLE_OLD_PAT & CANONICAL_RFLAGS(pstate->rflags);
3002 break;
3003 }
3004
3005 if(local_rflag){
3006 SET_PATTYPE(local_rflag | (pstate->rflags & PAT_USE_MASK));
3007
3008 pstate->patlinecurrent = NULL;
3009 pstate->patcurrent = NULL;
3010
3011 if(*cur_pat_h){
3012 /* Find last patline with a pat */
3013 for(patline = (*cur_pat_h)->patlinehead;
3014 patline;
3015 patline = patline->next)
3016 if(patline->last)
3017 pstate->patlinecurrent = patline;
3018
3019 if(pstate->patlinecurrent)
3020 pstate->patcurrent = pstate->patlinecurrent->last;
3021 }
3022
3023 if(pstate->patcurrent)
3024 pstate->cur_rflag_num = i;
3025
3026 if(pstate->patcurrent)
3027 break;
3028 }
3029 }
3030
3031 return(pstate->patcurrent);
3032 }
3033
3034
3035 /*
3036 * Return last pattern of the specified types. These types were set by a
3037 * previous call to any_patterns or nonempty_patterns.
3038 *
3039 * Args -- pstate pattern state. This is set here and passed back for
3040 * use by prev_pattern. Must be non-null.
3041 * It must have been initialized previously by a call to
3042 * nonempty_patterns or any_patterns.
3043 */
3044 PAT_S *
last_pattern(PAT_STATE * pstate)3045 last_pattern(PAT_STATE *pstate)
3046 {
3047 PAT_S *pat;
3048 long rflags;
3049
3050 pstate->cur_rflag_num = PATTERN_N;
3051
3052 rflags = pstate->rflags;
3053
3054 for(pat = last_any_pattern(pstate);
3055 pat && !((pat->action &&
3056 ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
3057 (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
3058 pat->action->is_a_incol) ||
3059 (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
3060 (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
3061 (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
3062 (rflags & ROLE_SCORE && (pat->action->scoreval
3063 || pat->action->scorevalhdrtok)) ||
3064 (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
3065 (rflags & ROLE_REPLY &&
3066 (pat->action->repl_type == ROLE_REPL_YES ||
3067 pat->action->repl_type == ROLE_REPL_NOCONF)) ||
3068 (rflags & ROLE_FORWARD &&
3069 (pat->action->forw_type == ROLE_FORW_YES ||
3070 pat->action->forw_type == ROLE_FORW_NOCONF)) ||
3071 (rflags & ROLE_COMPOSE &&
3072 (pat->action->comp_type == ROLE_COMP_YES ||
3073 pat->action->comp_type == ROLE_COMP_NOCONF)) ||
3074 (rflags & ROLE_OLD_FILT) ||
3075 (rflags & ROLE_OLD_SCORE) ||
3076 (rflags & ROLE_OLD_PAT)))
3077 ||
3078 pat->inherit);
3079 pat = prev_any_pattern(pstate))
3080 ;
3081
3082 return(pat);
3083 }
3084
3085
3086 /*
3087 * This assumes that pstate is valid.
3088 */
3089 PAT_S *
next_any_pattern(PAT_STATE * pstate)3090 next_any_pattern(PAT_STATE *pstate)
3091 {
3092 PAT_LINE_S *patline;
3093
3094 if(pstate->patlinecurrent){
3095 if(pstate->patcurrent && pstate->patcurrent->next)
3096 pstate->patcurrent = pstate->patcurrent->next;
3097 else{
3098 /* Find next patline with a pat */
3099 for(patline = pstate->patlinecurrent->next;
3100 patline && !patline->first;
3101 patline = patline->next)
3102 ;
3103
3104 if(patline){
3105 pstate->patlinecurrent = patline;
3106 pstate->patcurrent = patline->first;
3107 }
3108 else{
3109 pstate->patlinecurrent = NULL;
3110 pstate->patcurrent = NULL;
3111 }
3112 }
3113 }
3114
3115 /* we've reached the last, try the next rflag_num (the next pattern type) */
3116 if(!pstate->patcurrent){
3117 pstate->cur_rflag_num++;
3118 pstate->patcurrent = first_any_pattern(pstate);
3119 }
3120
3121 return(pstate->patcurrent);
3122 }
3123
3124
3125 /*
3126 * Return next pattern of the specified types. These types were set by a
3127 * previous call to any_patterns or nonempty_patterns.
3128 *
3129 * Args -- pstate pattern state. This is set by first_pattern or last_pattern.
3130 */
3131 PAT_S *
next_pattern(PAT_STATE * pstate)3132 next_pattern(PAT_STATE *pstate)
3133 {
3134 PAT_S *pat;
3135 long rflags;
3136
3137 rflags = pstate->rflags;
3138
3139 for(pat = next_any_pattern(pstate);
3140 pat && !((pat->action &&
3141 ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
3142 (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
3143 pat->action->is_a_incol) ||
3144 (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
3145 (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
3146 (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
3147 (rflags & ROLE_SCORE && (pat->action->scoreval
3148 || pat->action->scorevalhdrtok)) ||
3149 (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
3150 (rflags & ROLE_REPLY &&
3151 (pat->action->repl_type == ROLE_REPL_YES ||
3152 pat->action->repl_type == ROLE_REPL_NOCONF)) ||
3153 (rflags & ROLE_FORWARD &&
3154 (pat->action->forw_type == ROLE_FORW_YES ||
3155 pat->action->forw_type == ROLE_FORW_NOCONF)) ||
3156 (rflags & ROLE_COMPOSE &&
3157 (pat->action->comp_type == ROLE_COMP_YES ||
3158 pat->action->comp_type == ROLE_COMP_NOCONF)) ||
3159 (rflags & ROLE_OLD_FILT) ||
3160 (rflags & ROLE_OLD_SCORE) ||
3161 (rflags & ROLE_OLD_PAT)))
3162 ||
3163 pat->inherit);
3164 pat = next_any_pattern(pstate))
3165 ;
3166
3167 return(pat);
3168 }
3169
3170
3171 /*
3172 * This assumes that pstate is valid.
3173 */
3174 PAT_S *
prev_any_pattern(PAT_STATE * pstate)3175 prev_any_pattern(PAT_STATE *pstate)
3176 {
3177 PAT_LINE_S *patline;
3178
3179 if(pstate->patlinecurrent){
3180 if(pstate->patcurrent && pstate->patcurrent->prev)
3181 pstate->patcurrent = pstate->patcurrent->prev;
3182 else{
3183 /* Find prev patline with a pat */
3184 for(patline = pstate->patlinecurrent->prev;
3185 patline && !patline->last;
3186 patline = patline->prev)
3187 ;
3188
3189 if(patline){
3190 pstate->patlinecurrent = patline;
3191 pstate->patcurrent = patline->last;
3192 }
3193 else{
3194 pstate->patlinecurrent = NULL;
3195 pstate->patcurrent = NULL;
3196 }
3197 }
3198 }
3199
3200 if(!pstate->patcurrent){
3201 pstate->cur_rflag_num--;
3202 pstate->patcurrent = last_any_pattern(pstate);
3203 }
3204
3205 return(pstate->patcurrent);
3206 }
3207
3208
3209 /*
3210 * Return prev pattern of the specified types. These types were set by a
3211 * previous call to any_patterns or nonempty_patterns.
3212 *
3213 * Args -- pstate pattern state. This is set by first_pattern or last_pattern.
3214 */
3215 PAT_S *
prev_pattern(PAT_STATE * pstate)3216 prev_pattern(PAT_STATE *pstate)
3217 {
3218 PAT_S *pat;
3219 long rflags;
3220
3221 rflags = pstate->rflags;
3222
3223 for(pat = prev_any_pattern(pstate);
3224 pat && !((pat->action &&
3225 ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
3226 (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
3227 pat->action->is_a_incol) ||
3228 (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
3229 (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
3230 (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
3231 (rflags & ROLE_SCORE && (pat->action->scoreval
3232 || pat->action->scorevalhdrtok)) ||
3233 (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
3234 (rflags & ROLE_REPLY &&
3235 (pat->action->repl_type == ROLE_REPL_YES ||
3236 pat->action->repl_type == ROLE_REPL_NOCONF)) ||
3237 (rflags & ROLE_FORWARD &&
3238 (pat->action->forw_type == ROLE_FORW_YES ||
3239 pat->action->forw_type == ROLE_FORW_NOCONF)) ||
3240 (rflags & ROLE_COMPOSE &&
3241 (pat->action->comp_type == ROLE_COMP_YES ||
3242 pat->action->comp_type == ROLE_COMP_NOCONF)) ||
3243 (rflags & ROLE_OLD_FILT) ||
3244 (rflags & ROLE_OLD_SCORE) ||
3245 (rflags & ROLE_OLD_PAT)))
3246 ||
3247 pat->inherit);
3248 pat = prev_any_pattern(pstate))
3249 ;
3250
3251 return(pat);
3252 }
3253
3254
3255 /*
3256 * Rflags may be more than one pattern type OR'd together.
3257 */
3258 int
write_patterns(long int rflags)3259 write_patterns(long int rflags)
3260 {
3261 int canon_rflags;
3262 int err = 0;
3263
3264 dprint((7, "write_patterns(0x%x)\n", rflags));
3265
3266 canon_rflags = CANONICAL_RFLAGS(rflags);
3267
3268 if(canon_rflags & ROLE_DO_INCOLS)
3269 err += sub_write_patterns(ROLE_DO_INCOLS | (rflags & PAT_USE_MASK));
3270 if(!err && canon_rflags & ROLE_DO_OTHER)
3271 err += sub_write_patterns(ROLE_DO_OTHER | (rflags & PAT_USE_MASK));
3272 if(!err && canon_rflags & ROLE_DO_FILTER)
3273 err += sub_write_patterns(ROLE_DO_FILTER | (rflags & PAT_USE_MASK));
3274 if(!err && canon_rflags & ROLE_DO_SCORES)
3275 err += sub_write_patterns(ROLE_DO_SCORES | (rflags & PAT_USE_MASK));
3276 if(!err && canon_rflags & ROLE_DO_ROLES)
3277 err += sub_write_patterns(ROLE_DO_ROLES | (rflags & PAT_USE_MASK));
3278 if(!err && canon_rflags & ROLE_DO_SRCH)
3279 err += sub_write_patterns(ROLE_DO_SRCH | (rflags & PAT_USE_MASK));
3280
3281 if(!err && !(rflags & PAT_USE_CHANGED))
3282 write_pinerc(ps_global, (rflags & PAT_USE_MAIN) ? Main : Post, WRP_NONE);
3283
3284 return(err);
3285 }
3286
3287
3288 int
sub_write_patterns(long int rflags)3289 sub_write_patterns(long int rflags)
3290 {
3291 int err = 0, lineno = 0;
3292 char **lvalue = NULL;
3293 PAT_LINE_S *patline;
3294
3295 SET_PATTYPE(rflags);
3296
3297 if(!(*cur_pat_h)){
3298 q_status_message(SM_ORDER | SM_DING, 3, 4,
3299 "Unknown error saving patterns");
3300 return(-1);
3301 }
3302
3303 if((*cur_pat_h)->dirtypinerc){
3304 /* Count how many lines will be in patterns variable */
3305 for(patline = (*cur_pat_h)->patlinehead;
3306 patline;
3307 patline = patline->next)
3308 lineno++;
3309
3310 lvalue = (char **)fs_get((lineno+1)*sizeof(char *));
3311 memset(lvalue, 0, (lineno+1) * sizeof(char *));
3312 }
3313
3314 for(patline = (*cur_pat_h)->patlinehead, lineno = 0;
3315 !err && patline;
3316 patline = patline->next, lineno++){
3317 if(patline->type == File)
3318 err = write_pattern_file((*cur_pat_h)->dirtypinerc
3319 ? &lvalue[lineno] : NULL, patline);
3320 else if(patline->type == Literal && (*cur_pat_h)->dirtypinerc)
3321 err = write_pattern_lit(&lvalue[lineno], patline);
3322 else if(patline->type == Inherit)
3323 err = write_pattern_inherit((*cur_pat_h)->dirtypinerc
3324 ? &lvalue[lineno] : NULL, patline);
3325 }
3326
3327 if((*cur_pat_h)->dirtypinerc){
3328 if(err)
3329 free_list_array(&lvalue);
3330 else{
3331 char ***alval;
3332 struct variable *var = NULL;
3333
3334 if(rflags & ROLE_DO_ROLES)
3335 var = &ps_global->vars[V_PAT_ROLES];
3336 else if(rflags & ROLE_DO_OTHER)
3337 var = &ps_global->vars[V_PAT_OTHER];
3338 else if(rflags & ROLE_DO_FILTER)
3339 var = &ps_global->vars[V_PAT_FILTS];
3340 else if(rflags & ROLE_DO_SCORES)
3341 var = &ps_global->vars[V_PAT_SCORES];
3342 else if(rflags & ROLE_DO_INCOLS)
3343 var = &ps_global->vars[V_PAT_INCOLS];
3344 else if(rflags & ROLE_DO_SRCH)
3345 var = &ps_global->vars[V_PAT_SRCH];
3346
3347 alval = (rflags & PAT_USE_CHANGED) ? &(var->changed_val.l)
3348 : ALVAL(var, (rflags & PAT_USE_MAIN) ? Main : Post);
3349 if(*alval)
3350 free_list_array(alval);
3351
3352 if(rflags & PAT_USE_CHANGED) var->is_changed_val = 1;
3353
3354 *alval = lvalue;
3355
3356 if(!(rflags & PAT_USE_CHANGED))
3357 set_current_val(var, TRUE, TRUE);
3358 }
3359 }
3360
3361 if(!err)
3362 (*cur_pat_h)->dirtypinerc = 0;
3363
3364 return(err);
3365 }
3366
3367
3368 /*
3369 * Write pattern lines into a file.
3370 *
3371 * Args lvalue -- Pointer to char * to fill in variable value
3372 * patline --
3373 *
3374 * Returns 0 -- all is ok, lvalue has been filled in, file has been written
3375 * else -- error, lvalue untouched, file not written
3376 */
3377 int
write_pattern_file(char ** lvalue,PAT_LINE_S * patline)3378 write_pattern_file(char **lvalue, PAT_LINE_S *patline)
3379 {
3380 char *p, *tfile;
3381 int fd = -1, err = 0;
3382 FILE *fp_new;
3383 PAT_S *pat;
3384
3385 dprint((7, "write_pattern_file(%s)\n",
3386 (patline && patline->filepath) ? patline->filepath : "?"));
3387
3388 if(lvalue){
3389 size_t l;
3390
3391 l = strlen(patline->filename) + 5;
3392 p = (char *) fs_get((l+1) * sizeof(char));
3393 strncpy(p, "FILE:", l+1);
3394 p[l] = '\0';
3395 strncat(p, patline->filename, l+1-1-strlen(p));
3396 p[l] = '\0';
3397 *lvalue = p;
3398 }
3399
3400 if(patline->readonly || !patline->dirty) /* doesn't need writing */
3401 return(err);
3402
3403 /* Get a tempfile to write the patterns into */
3404 if(((tfile = tempfile_in_same_dir(patline->filepath, ".pt", NULL)) == NULL)
3405 || ((fd = our_open(tfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) < 0)
3406 || ((fp_new = fdopen(fd, "w")) == NULL)){
3407 q_status_message1(SM_ORDER | SM_DING, 3, 4,
3408 "Can't write in directory containing file \"%.200s\"",
3409 patline->filepath);
3410 if(tfile){
3411 our_unlink(tfile);
3412 fs_give((void **)&tfile);
3413 }
3414
3415 if(fd >= 0)
3416 close(fd);
3417
3418 return(-1);
3419 }
3420
3421 dprint((9, "write_pattern_file: writing into %s\n",
3422 tfile ? tfile : "?"));
3423
3424 if(fprintf(fp_new, "%s %s\n", PATTERN_MAGIC, PATTERN_FILE_VERS) == EOF)
3425 err--;
3426
3427 for(pat = patline->first; !err && pat; pat = pat->next){
3428 if((p = data_for_patline(pat)) != NULL){
3429 if(fprintf(fp_new, "%s\n", p) == EOF)
3430 err--;
3431
3432 fs_give((void **)&p);
3433 }
3434 }
3435
3436 if(err || fclose(fp_new) == EOF){
3437 if(err)
3438 (void)fclose(fp_new);
3439
3440 err--;
3441 q_status_message2(SM_ORDER | SM_DING, 3, 4,
3442 "I/O error: \"%.200s\": %.200s",
3443 tfile, error_description(errno));
3444 }
3445
3446 if(!err && rename_file(tfile, patline->filepath) < 0){
3447 err--;
3448 q_status_message3(SM_ORDER | SM_DING, 3, 4,
3449 _("Error renaming \"%s\" to \"%s\": %s"),
3450 tfile, patline->filepath, error_description(errno));
3451 dprint((2,
3452 "write_pattern_file: Error renaming (%s,%s): %s\n",
3453 tfile ? tfile : "?",
3454 (patline && patline->filepath) ? patline->filepath : "?",
3455 error_description(errno)));
3456 }
3457
3458 if(tfile){
3459 our_unlink(tfile);
3460 fs_give((void **)&tfile);
3461 }
3462
3463 if(!err)
3464 patline->dirty = 0;
3465
3466 return(err);
3467 }
3468
3469
3470 /*
3471 * Write literal pattern lines into lvalue (pinerc variable).
3472 *
3473 * Args lvalue -- Pointer to char * to fill in variable value
3474 * patline --
3475 *
3476 * Returns 0 -- all is ok, lvalue has been filled in, file has been written
3477 * else -- error, lvalue untouched, file not written
3478 */
3479 int
write_pattern_lit(char ** lvalue,PAT_LINE_S * patline)3480 write_pattern_lit(char **lvalue, PAT_LINE_S *patline)
3481 {
3482 char *p = NULL;
3483 int err = 0;
3484 PAT_S *pat;
3485
3486 pat = patline ? patline->first : NULL;
3487
3488 if(pat && lvalue && (p = data_for_patline(pat)) != NULL){
3489 size_t l;
3490
3491 l = strlen(p) + 4;
3492 *lvalue = (char *) fs_get((l+1) * sizeof(char));
3493 strncpy(*lvalue, "LIT:", l+1);
3494 (*lvalue)[l] = '\0';
3495 strncat(*lvalue, p, l+1-1-strlen(*lvalue));
3496 (*lvalue)[l] = '\0';
3497 }
3498 else{
3499 q_status_message(SM_ORDER | SM_DING, 3, 4,
3500 _("Unknown error saving pattern variable"));
3501 err--;
3502 }
3503
3504 if(p)
3505 fs_give((void **)&p);
3506
3507 return(err);
3508 }
3509
3510
3511 int
write_pattern_inherit(char ** lvalue,PAT_LINE_S * patline)3512 write_pattern_inherit(char **lvalue, PAT_LINE_S *patline)
3513 {
3514 int err = 0;
3515
3516 if(patline && patline->type == Inherit && lvalue)
3517 *lvalue = cpystr(INHERIT);
3518 else
3519 err--;
3520
3521 return(err);
3522 }
3523
3524
3525
3526 char *
data_for_patline(PAT_S * pat)3527 data_for_patline(PAT_S *pat)
3528 {
3529 char *p = NULL, *q, *to_pat = NULL,
3530 *news_pat = NULL, *from_pat = NULL,
3531 *sender_pat = NULL, *cc_pat = NULL, *subj_pat = NULL,
3532 *arb_pat = NULL, *fldr_type_pat = NULL, *fldr_pat = NULL,
3533 *afrom_type_pat = NULL, *abooks_pat = NULL,
3534 *alltext_pat = NULL, *scorei_pat = NULL, *recip_pat = NULL,
3535 *keyword_pat = NULL, *charset_pat = NULL,
3536 *bodytext_pat = NULL, *age_pat = NULL, *sentdate = NULL,
3537 *size_pat = NULL,
3538 *category_cmd = NULL, *category_pat = NULL,
3539 *category_lim = NULL,
3540 *partic_pat = NULL, *stat_new_val = NULL,
3541 *stat_rec_val = NULL,
3542 *stat_imp_val = NULL, *stat_del_val = NULL,
3543 *stat_ans_val = NULL, *stat_8bit_val = NULL,
3544 *stat_bom_val = NULL, *stat_boy_val = NULL,
3545 *from_act = NULL, *replyto_act = NULL, *fcc_act = NULL,
3546 *sig_act = NULL, *nick = NULL, *templ_act = NULL,
3547 *litsig_act = NULL, *cstm_act = NULL, *smtp_act = NULL,
3548 *nntp_act = NULL, *comment = NULL,
3549 *repl_val = NULL, *forw_val = NULL, *comp_val = NULL,
3550 *incol_act = NULL, *inherit_nick = NULL,
3551 *score_act = NULL, *hdrtok_act = NULL,
3552 *sort_act = NULL, *iform_act = NULL, *start_act = NULL,
3553 *folder_act = NULL, *filt_ifnotdel = NULL,
3554 *filt_nokill = NULL, *filt_del_val = NULL,
3555 *filt_imp_val = NULL, *filt_ans_val = NULL,
3556 *filt_new_val = NULL, *filt_nonterm = NULL,
3557 *keyword_set = NULL, *keyword_clr = NULL;
3558 int to_not = 0, news_not = 0, from_not = 0,
3559 sender_not = 0, cc_not = 0, subj_not = 0,
3560 partic_not = 0, recip_not = 0, alltext_not = 0, bodytext_not = 0,
3561 keyword_not = 0, charset_not = 0;
3562 size_t l;
3563 ACTION_S *action = NULL;
3564 NAMEVAL_S *f;
3565
3566 if(!pat)
3567 return(p);
3568
3569 if((pat->patgrp && pat->patgrp->bogus)
3570 || (pat->action && pat->action->bogus)){
3571 if(pat->raw)
3572 p = cpystr(pat->raw);
3573
3574 return(p);
3575 }
3576
3577 if(pat->patgrp){
3578 if(pat->patgrp->nick)
3579 if((nick = add_pat_escapes(pat->patgrp->nick)) && !*nick)
3580 fs_give((void **) &nick);
3581
3582 if(pat->patgrp->comment)
3583 if((comment = add_pat_escapes(pat->patgrp->comment)) && !*comment)
3584 fs_give((void **) &comment);
3585
3586 if(pat->patgrp->to){
3587 to_pat = pattern_to_config(pat->patgrp->to);
3588 to_not = pat->patgrp->to->not;
3589 }
3590
3591 if(pat->patgrp->from){
3592 from_pat = pattern_to_config(pat->patgrp->from);
3593 from_not = pat->patgrp->from->not;
3594 }
3595
3596 if(pat->patgrp->sender){
3597 sender_pat = pattern_to_config(pat->patgrp->sender);
3598 sender_not = pat->patgrp->sender->not;
3599 }
3600
3601 if(pat->patgrp->cc){
3602 cc_pat = pattern_to_config(pat->patgrp->cc);
3603 cc_not = pat->patgrp->cc->not;
3604 }
3605
3606 if(pat->patgrp->recip){
3607 recip_pat = pattern_to_config(pat->patgrp->recip);
3608 recip_not = pat->patgrp->recip->not;
3609 }
3610
3611 if(pat->patgrp->partic){
3612 partic_pat = pattern_to_config(pat->patgrp->partic);
3613 partic_not = pat->patgrp->partic->not;
3614 }
3615
3616 if(pat->patgrp->news){
3617 news_pat = pattern_to_config(pat->patgrp->news);
3618 news_not = pat->patgrp->news->not;
3619 }
3620
3621 if(pat->patgrp->subj){
3622 subj_pat = pattern_to_config(pat->patgrp->subj);
3623 subj_not = pat->patgrp->subj->not;
3624 }
3625
3626 if(pat->patgrp->alltext){
3627 alltext_pat = pattern_to_config(pat->patgrp->alltext);
3628 alltext_not = pat->patgrp->alltext->not;
3629 }
3630
3631 if(pat->patgrp->bodytext){
3632 bodytext_pat = pattern_to_config(pat->patgrp->bodytext);
3633 bodytext_not = pat->patgrp->bodytext->not;
3634 }
3635
3636 if(pat->patgrp->keyword){
3637 keyword_pat = pattern_to_config(pat->patgrp->keyword);
3638 keyword_not = pat->patgrp->keyword->not;
3639 }
3640
3641 if(pat->patgrp->charsets){
3642 charset_pat = pattern_to_config(pat->patgrp->charsets);
3643 charset_not = pat->patgrp->charsets->not;
3644 }
3645
3646 if(pat->patgrp->arbhdr){
3647 ARBHDR_S *a;
3648 char *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL;
3649 int len = 0;
3650
3651 /* This is brute force dumb, but who cares? */
3652 for(a = pat->patgrp->arbhdr; a; a = a->next){
3653 if(a->field && a->field[0]){
3654 p1 = pattern_to_string(a->p);
3655 p1 = p1 ? p1 : cpystr("");
3656 l = strlen(a->field)+strlen(p1)+1;
3657 p2 = (char *) fs_get((l+1) * sizeof(char));
3658 snprintf(p2, l+1, "%s=%s", a->field, p1);
3659 p3 = add_pat_escapes(p2);
3660 l = strlen(p3)+6;
3661 p4 = (char *) fs_get((l+1) * sizeof(char));
3662 snprintf(p4, l+1, "/%s%sARB%s",
3663 (a->p && a->p->not) ? "!" : "",
3664 a->isemptyval ? "E" : "", p3);
3665 len += strlen(p4);
3666
3667 if(p1)
3668 fs_give((void **)&p1);
3669 if(p2)
3670 fs_give((void **)&p2);
3671 if(p3)
3672 fs_give((void **)&p3);
3673 if(p4)
3674 fs_give((void **)&p4);
3675 }
3676 }
3677
3678 p = arb_pat = (char *)fs_get((len + 1) * sizeof(char));
3679
3680 for(a = pat->patgrp->arbhdr; a; a = a->next){
3681 if(a->field && a->field[0]){
3682 p1 = pattern_to_string(a->p);
3683 p1 = p1 ? p1 : cpystr("");
3684 l = strlen(a->field)+strlen(p1)+1;
3685 p2 = (char *) fs_get((l+1) * sizeof(char));
3686 snprintf(p2, l+1, "%s=%s", a->field, p1);
3687 p3 = add_pat_escapes(p2);
3688 l = strlen(p3)+6;
3689 p4 = (char *) fs_get((l+1) * sizeof(char));
3690 snprintf(p4, l+1, "/%s%sARB%s",
3691 (a->p && a->p->not) ? "!" : "",
3692 a->isemptyval ? "E" : "", p3);
3693 sstrncpy(&p, p4, len+1-(p-arb_pat));
3694
3695 if(p1)
3696 fs_give((void **)&p1);
3697 if(p2)
3698 fs_give((void **)&p2);
3699 if(p3)
3700 fs_give((void **)&p3);
3701 if(p4)
3702 fs_give((void **)&p4);
3703 }
3704 }
3705
3706 arb_pat[len] = '\0';
3707 }
3708
3709 if(pat->patgrp->age_uses_sentdate)
3710 sentdate = cpystr("/SENTDATE=1");
3711
3712 if(pat->patgrp->do_score){
3713 p = stringform_of_intvl(pat->patgrp->score);
3714 if(p){
3715 scorei_pat = add_pat_escapes(p);
3716 fs_give((void **)&p);
3717 }
3718 }
3719
3720 if(pat->patgrp->do_age){
3721 p = stringform_of_intvl(pat->patgrp->age);
3722 if(p){
3723 age_pat = add_pat_escapes(p);
3724 fs_give((void **)&p);
3725 }
3726 }
3727
3728 if(pat->patgrp->do_size){
3729 p = stringform_of_intvl(pat->patgrp->size);
3730 if(p){
3731 size_pat = add_pat_escapes(p);
3732 fs_give((void **)&p);
3733 }
3734 }
3735
3736 if(pat->patgrp->category_cmd && pat->patgrp->category_cmd[0]){
3737 size_t sz;
3738 char **l, *q;
3739
3740 /* concatenate into string with commas first */
3741 sz = 0;
3742 for(l = pat->patgrp->category_cmd; l[0] && l[0][0]; l++)
3743 sz += strlen(l[0]) + 1;
3744
3745 if(sz){
3746 char *p;
3747 int first_one = 1;
3748
3749 q = (char *)fs_get(sz);
3750 memset(q, 0, sz);
3751 p = q;
3752 for(l = pat->patgrp->category_cmd; l[0] && l[0][0]; l++){
3753 if(!first_one)
3754 sstrncpy(&p, ",", sz-(p-q));
3755
3756 first_one = 0;
3757 sstrncpy(&p, l[0], sz-(p-q));
3758 }
3759
3760 q[sz-1] = '\0';
3761
3762 category_cmd = add_pat_escapes(q);
3763 fs_give((void **)&q);
3764 }
3765 }
3766
3767 if(pat->patgrp->do_cat){
3768 p = stringform_of_intvl(pat->patgrp->cat);
3769 if(p){
3770 category_pat = add_pat_escapes(p);
3771 fs_give((void **)&p);
3772 }
3773 }
3774
3775 if(pat->patgrp->cat_lim != -1L){
3776 category_lim = (char *) fs_get(20 * sizeof(char));
3777 snprintf(category_lim, 20, "%ld", pat->patgrp->cat_lim);
3778 }
3779
3780 if((f = pat_fldr_types(pat->patgrp->fldr_type)) != NULL)
3781 fldr_type_pat = f->shortname;
3782
3783 if(pat->patgrp->folder)
3784 fldr_pat = pattern_to_config(pat->patgrp->folder);
3785
3786 if((f = inabook_fldr_types(pat->patgrp->inabook)) != NULL
3787 && f->value != IAB_DEFL)
3788 afrom_type_pat = f->shortname;
3789
3790 if(pat->patgrp->abooks)
3791 abooks_pat = pattern_to_config(pat->patgrp->abooks);
3792
3793 if(pat->patgrp->stat_new != PAT_STAT_EITHER &&
3794 (f = role_status_types(pat->patgrp->stat_new)) != NULL)
3795 stat_new_val = f->shortname;
3796
3797 if(pat->patgrp->stat_rec != PAT_STAT_EITHER &&
3798 (f = role_status_types(pat->patgrp->stat_rec)) != NULL)
3799 stat_rec_val = f->shortname;
3800
3801 if(pat->patgrp->stat_del != PAT_STAT_EITHER &&
3802 (f = role_status_types(pat->patgrp->stat_del)) != NULL)
3803 stat_del_val = f->shortname;
3804
3805 if(pat->patgrp->stat_ans != PAT_STAT_EITHER &&
3806 (f = role_status_types(pat->patgrp->stat_ans)) != NULL)
3807 stat_ans_val = f->shortname;
3808
3809 if(pat->patgrp->stat_imp != PAT_STAT_EITHER &&
3810 (f = role_status_types(pat->patgrp->stat_imp)) != NULL)
3811 stat_imp_val = f->shortname;
3812
3813 if(pat->patgrp->stat_8bitsubj != PAT_STAT_EITHER &&
3814 (f = role_status_types(pat->patgrp->stat_8bitsubj)) != NULL)
3815 stat_8bit_val = f->shortname;
3816
3817 if(pat->patgrp->stat_bom != PAT_STAT_EITHER &&
3818 (f = role_status_types(pat->patgrp->stat_bom)) != NULL)
3819 stat_bom_val = f->shortname;
3820
3821 if(pat->patgrp->stat_boy != PAT_STAT_EITHER &&
3822 (f = role_status_types(pat->patgrp->stat_boy)) != NULL)
3823 stat_boy_val = f->shortname;
3824 }
3825
3826 if(pat->action){
3827 action = pat->action;
3828
3829 if(action->is_a_score){
3830 if(action->scoreval != 0L &&
3831 action->scoreval >= SCORE_MIN && action->scoreval <= SCORE_MAX){
3832 score_act = (char *) fs_get(5 * sizeof(char));
3833 snprintf(score_act, 5, "%ld", pat->action->scoreval);
3834 }
3835
3836 if(action->scorevalhdrtok)
3837 hdrtok_act = hdrtok_to_config(action->scorevalhdrtok);
3838 }
3839
3840 if(action->is_a_role){
3841 if(action->inherit_nick)
3842 inherit_nick = add_pat_escapes(action->inherit_nick);
3843 if(action->fcc)
3844 fcc_act = add_pat_escapes(action->fcc);
3845 if(action->litsig)
3846 litsig_act = add_pat_escapes(action->litsig);
3847 if(action->sig)
3848 sig_act = add_pat_escapes(action->sig);
3849 if(action->template)
3850 templ_act = add_pat_escapes(action->template);
3851
3852 if(action->cstm){
3853 size_t sz;
3854 char **l, *q;
3855
3856 /* concatenate into string with commas first */
3857 sz = 0;
3858 for(l = action->cstm; l[0] && l[0][0]; l++)
3859 sz += strlen(l[0]) + 1;
3860
3861 if(sz){
3862 char *p;
3863 int first_one = 1;
3864
3865 q = (char *)fs_get(sz);
3866 memset(q, 0, sz);
3867 p = q;
3868 for(l = action->cstm; l[0] && l[0][0]; l++){
3869 if((!struncmp(l[0], "from", 4) &&
3870 (l[0][4] == ':' || l[0][4] == '\0')) ||
3871 (!struncmp(l[0], "reply-to", 8) &&
3872 (l[0][8] == ':' || l[0][8] == '\0')))
3873 continue;
3874
3875 if(!first_one)
3876 sstrncpy(&p, ",", sz-(p-q));
3877
3878 first_one = 0;
3879 sstrncpy(&p, l[0], sz-(p-q));
3880 }
3881
3882 q[sz-1] = '\0';
3883
3884 cstm_act = add_pat_escapes(q);
3885 fs_give((void **)&q);
3886 }
3887 }
3888
3889 if(action->smtp){
3890 size_t sz;
3891 char **l, *q;
3892
3893 /* concatenate into string with commas first */
3894 sz = 0;
3895 for(l = action->smtp; l[0] && l[0][0]; l++)
3896 sz += strlen(l[0]) + 1;
3897
3898 if(sz){
3899 char *p;
3900 int first_one = 1;
3901
3902 q = (char *)fs_get(sz);
3903 memset(q, 0, sz);
3904 p = q;
3905 for(l = action->smtp; l[0] && l[0][0]; l++){
3906 if(!first_one)
3907 sstrncpy(&p, ",", sz-(p-q));
3908
3909 first_one = 0;
3910 sstrncpy(&p, l[0], sz-(p-q));
3911 }
3912
3913 q[sz-1] = '\0';
3914
3915 smtp_act = add_pat_escapes(q);
3916 fs_give((void **)&q);
3917 }
3918 }
3919
3920 if(action->nntp){
3921 size_t sz;
3922 char **l, *q;
3923
3924 /* concatenate into string with commas first */
3925 sz = 0;
3926 for(l = action->nntp; l[0] && l[0][0]; l++)
3927 sz += strlen(l[0]) + 1;
3928
3929 if(sz){
3930 char *p;
3931 int first_one = 1;
3932
3933 q = (char *)fs_get(sz);
3934 memset(q, 0, sz);
3935 p = q;
3936 for(l = action->nntp; l[0] && l[0][0]; l++){
3937 if(!first_one)
3938 sstrncpy(&p, ",", sz-(p-q));
3939
3940 first_one = 0;
3941 sstrncpy(&p, l[0], sz-(p-q));
3942 }
3943
3944 q[sz-1] = '\0';
3945
3946 nntp_act = add_pat_escapes(q);
3947 fs_give((void **)&q);
3948 }
3949 }
3950
3951 if((f = role_repl_types(action->repl_type)) != NULL)
3952 repl_val = f->shortname;
3953
3954 if((f = role_forw_types(action->forw_type)) != NULL)
3955 forw_val = f->shortname;
3956
3957 if((f = role_comp_types(action->comp_type)) != NULL)
3958 comp_val = f->shortname;
3959 }
3960
3961 if(action->is_a_incol && action->incol){
3962 char *ptr, buf[256], *p1, *p2;
3963
3964 ptr = buf;
3965 memset(buf, 0, sizeof(buf));
3966 sstrncpy(&ptr, "/FG=", sizeof(buf)-(ptr-buf));
3967 sstrncpy(&ptr, (p1=add_pat_escapes(action->incol->fg)), sizeof(buf)-(ptr-buf));
3968 sstrncpy(&ptr, "/BG=", sizeof(buf)-(ptr-buf));
3969 sstrncpy(&ptr, (p2=add_pat_escapes(action->incol->bg)), sizeof(buf)-(ptr-buf));
3970 buf[sizeof(buf)-1] = '\0';
3971 /* the colors will be doubly escaped */
3972 incol_act = add_pat_escapes(buf);
3973 if(p1)
3974 fs_give((void **)&p1);
3975
3976 if(p2)
3977 fs_give((void **)&p2);
3978 }
3979
3980 if(action->is_a_other){
3981 char buf[256];
3982
3983 if(action->sort_is_set){
3984 snprintf(buf, sizeof(buf), "%.50s%.50s",
3985 sort_name(action->sortorder),
3986 action->revsort ? "/Reverse" : "");
3987 sort_act = add_pat_escapes(buf);
3988 }
3989
3990 if(action->index_format)
3991 iform_act = add_pat_escapes(action->index_format);
3992
3993 if(action->startup_rule != IS_NOTSET &&
3994 (f = startup_rules(action->startup_rule)) != NULL)
3995 start_act = S_OR_L(f);
3996 }
3997
3998 if(action->is_a_role && action->from){
3999 char *bufp;
4000 size_t len;
4001
4002 len = est_size(action->from);
4003 bufp = (char *) fs_get(len * sizeof(char));
4004 p = addr_string_mult(action->from, bufp, len);
4005 if(p){
4006 from_act = add_pat_escapes(p);
4007 fs_give((void **)&p);
4008 }
4009 }
4010
4011 if(action->is_a_role && action->replyto){
4012 char *bufp;
4013 size_t len;
4014
4015 len = est_size(action->replyto);
4016 bufp = (char *) fs_get(len * sizeof(char));
4017 p = addr_string_mult(action->replyto, bufp, len);
4018 if(p){
4019 replyto_act = add_pat_escapes(p);
4020 fs_give((void **)&p);
4021 }
4022 }
4023
4024 if(action->is_a_filter){
4025 if(action->folder){
4026 if((folder_act = pattern_to_config(action->folder)) != NULL){
4027 if(action->move_only_if_not_deleted)
4028 filt_ifnotdel = cpystr("/NOTDEL=1");
4029 }
4030 }
4031
4032 if(action->keyword_set)
4033 keyword_set = pattern_to_config(action->keyword_set);
4034
4035 if(action->keyword_clr)
4036 keyword_clr = pattern_to_config(action->keyword_clr);
4037
4038 if(!action->kill)
4039 filt_nokill = cpystr("/NOKILL=1");
4040
4041 if(action->non_terminating)
4042 filt_nonterm = cpystr("/NONTERM=1");
4043
4044 if(action->state_setting_bits){
4045 char buf[256];
4046 int dval, nval, ival, aval;
4047
4048 buf[0] = '\0';
4049 p = buf;
4050
4051 convert_statebits_to_vals(action->state_setting_bits,
4052 &dval, &aval, &ival, &nval);
4053 if(dval != ACT_STAT_LEAVE &&
4054 (f = msg_state_types(dval)) != NULL)
4055 filt_del_val = f->shortname;
4056
4057 if(aval != ACT_STAT_LEAVE &&
4058 (f = msg_state_types(aval)) != NULL)
4059 filt_ans_val = f->shortname;
4060
4061 if(ival != ACT_STAT_LEAVE &&
4062 (f = msg_state_types(ival)) != NULL)
4063 filt_imp_val = f->shortname;
4064
4065 if(nval != ACT_STAT_LEAVE &&
4066 (f = msg_state_types(nval)) != NULL)
4067 filt_new_val = f->shortname;
4068 }
4069 }
4070 }
4071
4072 l = strlen(nick ? nick : "Alternate Role") +
4073 strlen(comment ? comment : "") +
4074 strlen(to_pat ? to_pat : "") +
4075 strlen(from_pat ? from_pat : "") +
4076 strlen(sender_pat ? sender_pat : "") +
4077 strlen(cc_pat ? cc_pat : "") +
4078 strlen(recip_pat ? recip_pat : "") +
4079 strlen(partic_pat ? partic_pat : "") +
4080 strlen(news_pat ? news_pat : "") +
4081 strlen(subj_pat ? subj_pat : "") +
4082 strlen(alltext_pat ? alltext_pat : "") +
4083 strlen(bodytext_pat ? bodytext_pat : "") +
4084 strlen(arb_pat ? arb_pat : "") +
4085 strlen(scorei_pat ? scorei_pat : "") +
4086 strlen(keyword_pat ? keyword_pat : "") +
4087 strlen(charset_pat ? charset_pat : "") +
4088 strlen(age_pat ? age_pat : "") +
4089 strlen(size_pat ? size_pat : "") +
4090 strlen(category_cmd ? category_cmd : "") +
4091 strlen(category_pat ? category_pat : "") +
4092 strlen(category_lim ? category_lim : "") +
4093 strlen(fldr_pat ? fldr_pat : "") +
4094 strlen(abooks_pat ? abooks_pat : "") +
4095 strlen(sentdate ? sentdate : "") +
4096 strlen(inherit_nick ? inherit_nick : "") +
4097 strlen(score_act ? score_act : "") +
4098 strlen(hdrtok_act ? hdrtok_act : "") +
4099 strlen(from_act ? from_act : "") +
4100 strlen(replyto_act ? replyto_act : "") +
4101 strlen(fcc_act ? fcc_act : "") +
4102 strlen(litsig_act ? litsig_act : "") +
4103 strlen(cstm_act ? cstm_act : "") +
4104 strlen(smtp_act ? smtp_act : "") +
4105 strlen(nntp_act ? nntp_act : "") +
4106 strlen(sig_act ? sig_act : "") +
4107 strlen(incol_act ? incol_act : "") +
4108 strlen(sort_act ? sort_act : "") +
4109 strlen(iform_act ? iform_act : "") +
4110 strlen(start_act ? start_act : "") +
4111 strlen(filt_ifnotdel ? filt_ifnotdel : "") +
4112 strlen(filt_nokill ? filt_nokill : "") +
4113 strlen(filt_nonterm ? filt_nonterm : "") +
4114 (folder_act ? (strlen(folder_act) + 8) : 0) +
4115 strlen(keyword_set ? keyword_set : "") +
4116 strlen(keyword_clr ? keyword_clr : "") +
4117 strlen(templ_act ? templ_act : "") + 540;
4118 /*
4119 * The +540 above is larger than needed but not everything is accounted
4120 * for with the strlens.
4121 */
4122 p = (char *) fs_get(l * sizeof(char));
4123
4124 q = p;
4125 sstrncpy(&q, "pattern=\"/NICK=", l-(q-p));
4126
4127 if(nick){
4128 sstrncpy(&q, nick, l-(q-p));
4129 fs_give((void **) &nick);
4130 }
4131 else
4132 sstrncpy(&q, "Alternate Role", l-(q-p));
4133
4134 if(comment){
4135 sstrncpy(&q, "/", l-(q-p));
4136 sstrncpy(&q, "COMM=", l-(q-p));
4137 sstrncpy(&q, comment, l-(q-p));
4138 fs_give((void **) &comment);
4139 }
4140
4141 if(to_pat){
4142 sstrncpy(&q, "/", l-(q-p));
4143 if(to_not)
4144 sstrncpy(&q, "!", l-(q-p));
4145
4146 sstrncpy(&q, "TO=", l-(q-p));
4147 sstrncpy(&q, to_pat, l-(q-p));
4148 fs_give((void **) &to_pat);
4149 }
4150
4151 if(from_pat){
4152 sstrncpy(&q, "/", l-(q-p));
4153 if(from_not)
4154 sstrncpy(&q, "!", l-(q-p));
4155
4156 sstrncpy(&q, "FROM=", l-(q-p));
4157 sstrncpy(&q, from_pat, l-(q-p));
4158 fs_give((void **) &from_pat);
4159 }
4160
4161 if(sender_pat){
4162 sstrncpy(&q, "/", l-(q-p));
4163 if(sender_not)
4164 sstrncpy(&q, "!", l-(q-p));
4165
4166 sstrncpy(&q, "SENDER=", l-(q-p));
4167 sstrncpy(&q, sender_pat, l-(q-p));
4168 fs_give((void **) &sender_pat);
4169 }
4170
4171 if(cc_pat){
4172 sstrncpy(&q,"/", l-(q-p));
4173 if(cc_not)
4174 sstrncpy(&q, "!", l-(q-p));
4175
4176 sstrncpy(&q,"CC=", l-(q-p));
4177 sstrncpy(&q, cc_pat, l-(q-p));
4178 fs_give((void **) &cc_pat);
4179 }
4180
4181 if(recip_pat){
4182 sstrncpy(&q, "/", l-(q-p));
4183 if(recip_not)
4184 sstrncpy(&q, "!", l-(q-p));
4185
4186 sstrncpy(&q, "RECIP=", l-(q-p));
4187 sstrncpy(&q, recip_pat, l-(q-p));
4188 fs_give((void **) &recip_pat);
4189 }
4190
4191 if(partic_pat){
4192 sstrncpy(&q, "/", l-(q-p));
4193 if(partic_not)
4194 sstrncpy(&q, "!", l-(q-p));
4195
4196 sstrncpy(&q, "PARTIC=", l-(q-p));
4197 sstrncpy(&q, partic_pat, l-(q-p));
4198 fs_give((void **) &partic_pat);
4199 }
4200
4201 if(news_pat){
4202 sstrncpy(&q, "/", l-(q-p));
4203 if(news_not)
4204 sstrncpy(&q, "!", l-(q-p));
4205
4206 sstrncpy(&q, "NEWS=", l-(q-p));
4207 sstrncpy(&q, news_pat, l-(q-p));
4208 fs_give((void **) &news_pat);
4209 }
4210
4211 if(subj_pat){
4212 sstrncpy(&q, "/", l-(q-p));
4213 if(subj_not)
4214 sstrncpy(&q, "!", l-(q-p));
4215
4216 sstrncpy(&q, "SUBJ=", l-(q-p));
4217 sstrncpy(&q, subj_pat, l-(q-p));
4218 fs_give((void **)&subj_pat);
4219 }
4220
4221 if(alltext_pat){
4222 sstrncpy(&q, "/", l-(q-p));
4223 if(alltext_not)
4224 sstrncpy(&q, "!", l-(q-p));
4225
4226 sstrncpy(&q, "ALL=", l-(q-p));
4227 sstrncpy(&q, alltext_pat, l-(q-p));
4228 fs_give((void **) &alltext_pat);
4229 }
4230
4231 if(bodytext_pat){
4232 sstrncpy(&q, "/", l-(q-p));
4233 if(bodytext_not)
4234 sstrncpy(&q, "!", l-(q-p));
4235
4236 sstrncpy(&q, "BODY=", l-(q-p));
4237 sstrncpy(&q, bodytext_pat, l-(q-p));
4238 fs_give((void **) &bodytext_pat);
4239 }
4240
4241 if(keyword_pat){
4242 sstrncpy(&q, "/", l-(q-p));
4243 if(keyword_not)
4244 sstrncpy(&q, "!", l-(q-p));
4245
4246 sstrncpy(&q, "KEY=", l-(q-p));
4247 sstrncpy(&q, keyword_pat, l-(q-p));
4248 fs_give((void **) &keyword_pat);
4249 }
4250
4251 if(charset_pat){
4252 sstrncpy(&q, "/", l-(q-p));
4253 if(charset_not)
4254 sstrncpy(&q, "!", l-(q-p));
4255
4256 sstrncpy(&q, "CHAR=", l-(q-p));
4257 sstrncpy(&q, charset_pat, l-(q-p));
4258 fs_give((void **) &charset_pat);
4259 }
4260
4261 if(arb_pat){
4262 sstrncpy(&q, arb_pat, l-(q-p));
4263 fs_give((void **)&arb_pat);
4264 }
4265
4266 if(scorei_pat){
4267 sstrncpy(&q, "/SCOREI=", l-(q-p));
4268 sstrncpy(&q, scorei_pat, l-(q-p));
4269 fs_give((void **) &scorei_pat);
4270 }
4271
4272 if(age_pat){
4273 sstrncpy(&q, "/AGE=", l-(q-p));
4274 sstrncpy(&q, age_pat, l-(q-p));
4275 fs_give((void **) &age_pat);
4276 }
4277
4278 if(size_pat){
4279 sstrncpy(&q, "/SIZE=", l-(q-p));
4280 sstrncpy(&q, size_pat, l-(q-p));
4281 fs_give((void **) &size_pat);
4282 }
4283
4284 if(category_cmd){
4285 sstrncpy(&q, "/CATCMD=", l-(q-p));
4286 sstrncpy(&q, category_cmd, l-(q-p));
4287 fs_give((void **) &category_cmd);
4288 }
4289
4290 if(category_pat){
4291 sstrncpy(&q, "/CATVAL=", l-(q-p));
4292 sstrncpy(&q, category_pat, l-(q-p));
4293 fs_give((void **) &category_pat);
4294 }
4295
4296 if(category_lim){
4297 sstrncpy(&q, "/CATLIM=", l-(q-p));
4298 sstrncpy(&q, category_lim, l-(q-p));
4299 fs_give((void **) &category_lim);
4300 }
4301
4302 if(sentdate){
4303 sstrncpy(&q, sentdate, l-(q-p));
4304 fs_give((void **) &sentdate);
4305 }
4306
4307 if(fldr_type_pat){
4308 sstrncpy(&q, "/FLDTYPE=", l-(q-p));
4309 sstrncpy(&q, fldr_type_pat, l-(q-p));
4310 }
4311
4312 if(fldr_pat){
4313 sstrncpy(&q, "/FOLDER=", l-(q-p));
4314 sstrncpy(&q, fldr_pat, l-(q-p));
4315 fs_give((void **) &fldr_pat);
4316 }
4317
4318 if(afrom_type_pat){
4319 sstrncpy(&q, "/AFROM=", l-(q-p));
4320 sstrncpy(&q, afrom_type_pat, l-(q-p));
4321
4322 /*
4323 * Add address types. If it is From or Reply-to
4324 * leave this out so it will still work with pine.
4325 */
4326 if((pat->patgrp->inabook & IAB_FROM
4327 && pat->patgrp->inabook & IAB_REPLYTO
4328 && !(pat->patgrp->inabook & IAB_SENDER)
4329 && !(pat->patgrp->inabook & IAB_TO)
4330 && !(pat->patgrp->inabook & IAB_CC))
4331 ||
4332 (!(pat->patgrp->inabook & IAB_FROM)
4333 && !(pat->patgrp->inabook & IAB_REPLYTO)
4334 && !(pat->patgrp->inabook & IAB_SENDER)
4335 && !(pat->patgrp->inabook & IAB_TO)
4336 && !(pat->patgrp->inabook & IAB_CC))){
4337 ; /* leave it out */
4338 }
4339 else{
4340 sstrncpy(&q, "/AFROMA=", l-(q-p));
4341 if(pat->patgrp->inabook & IAB_FROM)
4342 sstrncpy(&q, "F", l-(q-p));
4343
4344 if(pat->patgrp->inabook & IAB_REPLYTO)
4345 sstrncpy(&q, "R", l-(q-p));
4346
4347 if(pat->patgrp->inabook & IAB_SENDER)
4348 sstrncpy(&q, "S", l-(q-p));
4349
4350 if(pat->patgrp->inabook & IAB_TO)
4351 sstrncpy(&q, "T", l-(q-p));
4352
4353 if(pat->patgrp->inabook & IAB_CC)
4354 sstrncpy(&q, "C", l-(q-p));
4355 }
4356 }
4357
4358 if(abooks_pat){
4359 sstrncpy(&q, "/ABOOKS=", l-(q-p));
4360 sstrncpy(&q, abooks_pat, l-(q-p));
4361 fs_give((void **) &abooks_pat);
4362 }
4363
4364 if(stat_new_val){
4365 sstrncpy(&q, "/STATN=", l-(q-p));
4366 sstrncpy(&q, stat_new_val, l-(q-p));
4367 }
4368
4369 if(stat_rec_val){
4370 sstrncpy(&q, "/STATR=", l-(q-p));
4371 sstrncpy(&q, stat_rec_val, l-(q-p));
4372 }
4373
4374 if(stat_del_val){
4375 sstrncpy(&q, "/STATD=", l-(q-p));
4376 sstrncpy(&q, stat_del_val, l-(q-p));
4377 }
4378
4379 if(stat_imp_val){
4380 sstrncpy(&q, "/STATI=", l-(q-p));
4381 sstrncpy(&q, stat_imp_val, l-(q-p));
4382 }
4383
4384 if(stat_ans_val){
4385 sstrncpy(&q, "/STATA=", l-(q-p));
4386 sstrncpy(&q, stat_ans_val, l-(q-p));
4387 }
4388
4389 if(stat_8bit_val){
4390 sstrncpy(&q, "/8BITS=", l-(q-p));
4391 sstrncpy(&q, stat_8bit_val, l-(q-p));
4392 }
4393
4394 if(stat_bom_val){
4395 sstrncpy(&q, "/BOM=", l-(q-p));
4396 sstrncpy(&q, stat_bom_val, l-(q-p));
4397 }
4398
4399 if(stat_boy_val){
4400 sstrncpy(&q, "/BOY=", l-(q-p));
4401 sstrncpy(&q, stat_boy_val, l-(q-p));
4402 }
4403
4404 sstrncpy(&q, "\" action=\"", l-(q-p));
4405
4406 if(inherit_nick && *inherit_nick){
4407 sstrncpy(&q, "/INICK=", l-(q-p));
4408 sstrncpy(&q, inherit_nick, l-(q-p));
4409 fs_give((void **)&inherit_nick);
4410 }
4411
4412 if(action){
4413 if(action->is_a_role)
4414 sstrncpy(&q, "/ROLE=1", l-(q-p));
4415
4416 if(action->is_a_incol)
4417 sstrncpy(&q, "/ISINCOL=1", l-(q-p));
4418
4419 if(action->is_a_srch)
4420 sstrncpy(&q, "/ISSRCH=1", l-(q-p));
4421
4422 if(action->is_a_score)
4423 sstrncpy(&q, "/ISSCORE=1", l-(q-p));
4424
4425 if(action->is_a_filter){
4426 /*
4427 * Older pine will interpret a filter that has no folder
4428 * as a Delete, even if we set it up here to be a Just Set
4429 * State filter. Disable the filter for older versions in that
4430 * case. If kill is set then Delete is what is supposed to
4431 * happen, so that's ok. If folder is set then Move is what is
4432 * supposed to happen, so ok.
4433 */
4434 if(!action->kill && !action->folder)
4435 sstrncpy(&q, "/FILTER=2", l-(q-p));
4436 else
4437 sstrncpy(&q, "/FILTER=1", l-(q-p));
4438 }
4439
4440 if(action->is_a_other)
4441 sstrncpy(&q, "/OTHER=1", l-(q-p));
4442 }
4443
4444 if(score_act){
4445 sstrncpy(&q, "/SCORE=", l-(q-p));
4446 sstrncpy(&q, score_act, l-(q-p));
4447 fs_give((void **)&score_act);
4448 }
4449
4450 if(hdrtok_act){
4451 sstrncpy(&q, "/SCOREHDRTOK=", l-(q-p));
4452 sstrncpy(&q, hdrtok_act, l-(q-p));
4453 fs_give((void **)&hdrtok_act);
4454 }
4455
4456 if(from_act){
4457 sstrncpy(&q, "/FROM=", l-(q-p));
4458 sstrncpy(&q, from_act, l-(q-p));
4459 fs_give((void **) &from_act);
4460 }
4461
4462 if(replyto_act){
4463 sstrncpy(&q, "/REPL=", l-(q-p));
4464 sstrncpy(&q, replyto_act, l-(q-p));
4465 fs_give((void **)&replyto_act);
4466 }
4467
4468 if(fcc_act){
4469 sstrncpy(&q, "/FCC=", l-(q-p));
4470 sstrncpy(&q, fcc_act, l-(q-p));
4471 fs_give((void **)&fcc_act);
4472 }
4473
4474 if(litsig_act){
4475 sstrncpy(&q, "/LSIG=", l-(q-p));
4476 sstrncpy(&q, litsig_act, l-(q-p));
4477 fs_give((void **)&litsig_act);
4478 }
4479
4480 if(sig_act){
4481 sstrncpy(&q, "/SIG=", l-(q-p));
4482 sstrncpy(&q, sig_act, l-(q-p));
4483 fs_give((void **)&sig_act);
4484 }
4485
4486 if(templ_act){
4487 sstrncpy(&q, "/TEMPLATE=", l-(q-p));
4488 sstrncpy(&q, templ_act, l-(q-p));
4489 fs_give((void **)&templ_act);
4490 }
4491
4492 if(cstm_act){
4493 sstrncpy(&q, "/CSTM=", l-(q-p));
4494 sstrncpy(&q, cstm_act, l-(q-p));
4495 fs_give((void **)&cstm_act);
4496 }
4497
4498 if(smtp_act){
4499 sstrncpy(&q, "/SMTP=", l-(q-p));
4500 sstrncpy(&q, smtp_act, l-(q-p));
4501 fs_give((void **)&smtp_act);
4502 }
4503
4504 if(nntp_act){
4505 sstrncpy(&q, "/NNTP=", l-(q-p));
4506 sstrncpy(&q, nntp_act, l-(q-p));
4507 fs_give((void **)&nntp_act);
4508 }
4509
4510 if(repl_val){
4511 sstrncpy(&q, "/RTYPE=", l-(q-p));
4512 sstrncpy(&q, repl_val, l-(q-p));
4513 }
4514
4515 if(forw_val){
4516 sstrncpy(&q, "/FTYPE=", l-(q-p));
4517 sstrncpy(&q, forw_val, l-(q-p));
4518 }
4519
4520 if(comp_val){
4521 sstrncpy(&q, "/CTYPE=", l-(q-p));
4522 sstrncpy(&q, comp_val, l-(q-p));
4523 }
4524
4525 if(incol_act){
4526 sstrncpy(&q, "/INCOL=", l-(q-p));
4527 sstrncpy(&q, incol_act, l-(q-p));
4528 fs_give((void **)&incol_act);
4529 }
4530
4531 if(sort_act){
4532 sstrncpy(&q, "/SORT=", l-(q-p));
4533 sstrncpy(&q, sort_act, l-(q-p));
4534 fs_give((void **)&sort_act);
4535 }
4536
4537 if(iform_act){
4538 sstrncpy(&q, "/IFORM=", l-(q-p));
4539 sstrncpy(&q, iform_act, l-(q-p));
4540 fs_give((void **)&iform_act);
4541 }
4542
4543 if(start_act){
4544 sstrncpy(&q, "/START=", l-(q-p));
4545 sstrncpy(&q, start_act, l-(q-p));
4546 }
4547
4548 if(folder_act){
4549 sstrncpy(&q, "/FOLDER=", l-(q-p));
4550 sstrncpy(&q, folder_act, l-(q-p));
4551 fs_give((void **) &folder_act);
4552 }
4553
4554 if(filt_ifnotdel){
4555 sstrncpy(&q, filt_ifnotdel, l-(q-p));
4556 fs_give((void **) &filt_ifnotdel);
4557 }
4558
4559 if(filt_nonterm){
4560 sstrncpy(&q, filt_nonterm, l-(q-p));
4561 fs_give((void **) &filt_nonterm);
4562 }
4563
4564 if(filt_nokill){
4565 sstrncpy(&q, filt_nokill, l-(q-p));
4566 fs_give((void **) &filt_nokill);
4567 }
4568
4569 if(filt_new_val){
4570 sstrncpy(&q, "/STATN=", l-(q-p));
4571 sstrncpy(&q, filt_new_val, l-(q-p));
4572 }
4573
4574 if(filt_del_val){
4575 sstrncpy(&q, "/STATD=", l-(q-p));
4576 sstrncpy(&q, filt_del_val, l-(q-p));
4577 }
4578
4579 if(filt_imp_val){
4580 sstrncpy(&q, "/STATI=", l-(q-p));
4581 sstrncpy(&q, filt_imp_val, l-(q-p));
4582 }
4583
4584 if(filt_ans_val){
4585 sstrncpy(&q, "/STATA=", l-(q-p));
4586 sstrncpy(&q, filt_ans_val, l-(q-p));
4587 }
4588
4589 if(keyword_set){
4590 sstrncpy(&q, "/KEYSET=", l-(q-p));
4591 sstrncpy(&q, keyword_set, l-(q-p));
4592 fs_give((void **) &keyword_set);
4593 }
4594
4595 if(keyword_clr){
4596 sstrncpy(&q, "/KEYCLR=", l-(q-p));
4597 sstrncpy(&q, keyword_clr, l-(q-p));
4598 fs_give((void **) &keyword_clr);
4599 }
4600
4601 if(q-p < l)
4602 *q++ = '\"';
4603
4604 if(q-p < l)
4605 *q = '\0';
4606
4607 p[l-1] = '\0';
4608
4609 return(p);
4610 }
4611
4612
4613 void
convert_statebits_to_vals(long int bits,int * dval,int * aval,int * ival,int * nval)4614 convert_statebits_to_vals(long int bits, int *dval, int *aval, int *ival, int *nval)
4615 {
4616 if(dval)
4617 *dval = ACT_STAT_LEAVE;
4618 if(aval)
4619 *aval = ACT_STAT_LEAVE;
4620 if(ival)
4621 *ival = ACT_STAT_LEAVE;
4622 if(nval)
4623 *nval = ACT_STAT_LEAVE;
4624
4625 if(ival){
4626 if(bits & F_FLAG)
4627 *ival = ACT_STAT_SET;
4628 else if(bits & F_UNFLAG)
4629 *ival = ACT_STAT_CLEAR;
4630 }
4631
4632 if(aval){
4633 if(bits & F_ANS)
4634 *aval = ACT_STAT_SET;
4635 else if(bits & F_UNANS)
4636 *aval = ACT_STAT_CLEAR;
4637 }
4638
4639 if(dval){
4640 if(bits & F_DEL)
4641 *dval = ACT_STAT_SET;
4642 else if(bits & F_UNDEL)
4643 *dval = ACT_STAT_CLEAR;
4644 }
4645
4646 if(nval){
4647 if(bits & F_UNSEEN)
4648 *nval = ACT_STAT_SET;
4649 else if(bits & F_SEEN)
4650 *nval = ACT_STAT_CLEAR;
4651 }
4652 }
4653
4654
4655 /*
4656 * The "searched" bit will be set for each message which matches.
4657 *
4658 * Args: patgrp -- Pattern to search with
4659 * stream --
4660 * searchset -- Restrict search to this set
4661 * section -- Searching a section of the message, not the whole thing
4662 * get_score -- Function to return the score for a message
4663 * flags -- Most of these are flags to mail_search_full. However, we
4664 * overload the flags namespace and pass some flags of our
4665 * own in here that we pick off before calling mail_search.
4666 * Danger, danger, don't overlap with flag values defined
4667 * for c-client (that we want to use). Flags that we will
4668 * use here are:
4669 * MP_IN_CCLIENT_CB
4670 * If this is set we are in a callback from c-client
4671 * because some imap data arrived. We don't want to
4672 * call c-client again because it isn't re-entrant safe.
4673 * This is only a problem if we need to get the text of
4674 * a message to do the search, the envelope is cached
4675 * already.
4676 * MP_NOT
4677 * We want a ! of the patgrp in the search.
4678 * We also throw in SE_FREE for free, since we create
4679 * the search program here.
4680 *
4681 * Returns: 1 if any message in the searchset matches this pattern
4682 * 0 if no matches
4683 * -1 if couldn't perform search because of no_fetch restriction
4684 */
4685 int
match_pattern(PATGRP_S * patgrp,MAILSTREAM * stream,SEARCHSET * searchset,char * section,long int (* get_score)(MAILSTREAM *,long int),long int flags)4686 match_pattern(PATGRP_S *patgrp, MAILSTREAM *stream, SEARCHSET *searchset,
4687 char *section, long int (*get_score)(MAILSTREAM *, long int),
4688 long int flags)
4689 {
4690 SEARCHPGM *pgm;
4691 SEARCHSET *s;
4692 MESSAGECACHE *mc;
4693 long i, msgno = 0L;
4694 int in_client_callback = 0, not = 0;
4695
4696 dprint((7, "match_pattern\n"));
4697
4698 /*
4699 * Is the current folder the right type and possibly the right specific
4700 * folder for a match?
4701 */
4702 if(!(patgrp && !patgrp->bogus && match_pattern_folder(patgrp, stream)))
4703 return(0);
4704
4705 /*
4706 * NULL searchset means that there is no message to compare against.
4707 * This is a match if the folder type matches above (that gets
4708 * us here), and there are no patterns to match against.
4709 *
4710 * It is not totally clear what should be done in the case of an empty
4711 * search set. If there is search criteria, and someone does something
4712 * that is not specific to any messages (composing from scratch,
4713 * forwarding an attachment), then we can't be sure what a user would
4714 * expect. The original way was to just use the role, which we'll
4715 * preserve here.
4716 */
4717 if(!searchset)
4718 return(1);
4719
4720 /*
4721 * change by sderr : match_pattern_folder will sometimes
4722 * accept NULL streams, but if we are not in a folder-type-only
4723 * match test, we don't
4724 */
4725 if(!stream)
4726 return(0);
4727
4728 if(flags & MP_IN_CCLIENT_CB){
4729 in_client_callback++;
4730 flags &= ~MP_IN_CCLIENT_CB;
4731 }
4732
4733 if(flags & MP_NOT){
4734 not++;
4735 flags &= ~MP_NOT;
4736 }
4737
4738 flags |= SE_FREE;
4739
4740 if(patgrp->stat_bom != PAT_STAT_EITHER){
4741 if(patgrp->stat_bom == PAT_STAT_YES){
4742 if(!ps_global->beginning_of_month){
4743 return(0);
4744 }
4745 }
4746 else if(patgrp->stat_bom == PAT_STAT_NO){
4747 if(ps_global->beginning_of_month){
4748 return(0);
4749 }
4750 }
4751 }
4752
4753 if(patgrp->stat_boy != PAT_STAT_EITHER){
4754 if(patgrp->stat_boy == PAT_STAT_YES){
4755 if(!ps_global->beginning_of_year){
4756 return(0);
4757 }
4758 }
4759 else if(patgrp->stat_boy == PAT_STAT_NO){
4760 if(ps_global->beginning_of_year){
4761 return(0);
4762 }
4763 }
4764 }
4765
4766 if(in_client_callback && is_imap_stream(stream)
4767 && (patgrp->alltext || patgrp->bodytext
4768 || (patgrp->inabook != IAB_EITHER
4769 && any_addressbook_in_remote_stream(stream))))
4770 return(-1);
4771
4772 pgm = match_pattern_srchpgm(patgrp, stream, searchset);
4773 if(not && !(is_imap_stream(stream) && !modern_imap_stream(stream))){
4774 SEARCHPGM *srchpgm;
4775
4776 srchpgm = pgm;
4777 pgm = mail_newsearchpgm();
4778 pgm->not = mail_newsearchpgmlist();
4779 pgm->not->pgm = srchpgm;
4780 }
4781
4782 if((patgrp->alltext || patgrp->bodytext)
4783 && (!is_imap_stream(stream) || modern_imap_stream(stream)))
4784 /*
4785 * Cache isn't going to work. Search on server.
4786 * Except that is likely to not work on an old imap server because
4787 * the OR criteria won't work and we are likely to have some ORs.
4788 * So turn off the NOSERVER flag (and search on server if remote)
4789 * unless the server is an old server. It doesn't matter if we
4790 * turn if off if it's not an imap stream, but we do it anyway.
4791 */
4792 flags &= ~SE_NOSERVER;
4793
4794 if(section){
4795 /*
4796 * Mail_search_full only searches the top-level msg. We want to
4797 * search an attached msg instead. First do the stuff
4798 * that mail_search_full would have done before calling
4799 * mail_search_msg, then call mail_search_msg with a section number.
4800 * Mail_search_msg does take a section number even though
4801 * mail_search_full doesn't.
4802 */
4803
4804 /*
4805 * We'll only ever set section if the searchset is a single message.
4806 */
4807 if(pgm->msgno->next == NULL && pgm->msgno->first == pgm->msgno->last)
4808 msgno = pgm->msgno->first;
4809
4810 for(i = 1L; i <= stream->nmsgs; i++)
4811 if((mc = mail_elt(stream, i)) != NULL)
4812 mc->searched = NIL;
4813
4814 if(mail_search_msg(stream,msgno,section,pgm)
4815 && msgno > 0L && msgno <= stream->nmsgs
4816 && (mc = mail_elt(stream, msgno)))
4817 mc->searched = T;
4818
4819 if(flags & SE_FREE)
4820 mail_free_searchpgm(&pgm);
4821 }
4822 else{
4823 /*
4824 * Here we could be checking on the return value to see if
4825 * the search was "successful" or not. It may be the case
4826 * that we'd want to stop trying filtering if we got some
4827 * sort of error, but for now we would just continue on
4828 * to the next filter.
4829 */
4830 pine_mail_search_full(stream, "UTF-8", pgm, flags);
4831 }
4832
4833 /* we searched without the not, reverse it */
4834 if(not && is_imap_stream(stream) && !modern_imap_stream(stream)){
4835 for(msgno = 1L; msgno < mn_get_total(sp_msgmap(stream)); msgno++)
4836 if(stream && msgno && msgno <= stream->nmsgs){
4837 if((mc=mail_elt(stream,msgno)) != NULL){
4838 if(mc->searched)
4839 mc->searched = NIL;
4840 else
4841 mc->searched = T;
4842 }
4843 }
4844 }
4845
4846 /* check scores */
4847 if(get_score && scores_are_used(SCOREUSE_GET) && patgrp->do_score){
4848 char *savebits;
4849 SEARCHSET *ss;
4850
4851 /*
4852 * Get_score may call build_header_line recursively (we may
4853 * be in build_header_line now) so we have to preserve and
4854 * restore the sequence bits.
4855 */
4856 savebits = (char *)fs_get((stream->nmsgs+1) * sizeof(char));
4857
4858 for(i = 1L; i <= stream->nmsgs; i++){
4859 if((mc = mail_elt(stream, i)) != NULL){
4860 savebits[i] = mc->sequence;
4861 mc->sequence = 0;
4862 }
4863 }
4864
4865 /*
4866 * Build a searchset which will get all the scores that we
4867 * need but not more.
4868 */
4869 for(s = searchset; s; s = s->next)
4870 for(msgno = s->first; msgno <= s->last; msgno++)
4871 if(msgno > 0L && msgno <= stream->nmsgs
4872 && (mc = mail_elt(stream, msgno)) && mc->searched
4873 && get_msg_score(stream, msgno) == SCORE_UNDEF)
4874 mc->sequence = 1;
4875
4876 if((ss = build_searchset(stream)) != NULL){
4877 (void)calculate_some_scores(stream, ss, in_client_callback);
4878 mail_free_searchset(&ss);
4879 }
4880
4881 /*
4882 * Now check the scores versus the score intervals to see if
4883 * any of the messages which have matched up to this point can
4884 * be tossed because they don't match the score interval.
4885 */
4886 for(s = searchset; s; s = s->next)
4887 for(msgno = s->first; msgno <= s->last; msgno++)
4888 if(msgno > 0L && msgno <= stream->nmsgs
4889 && (mc = mail_elt(stream, msgno)) && mc->searched){
4890 long score;
4891
4892 score = (*get_score)(stream, msgno);
4893
4894 /*
4895 * If the score is outside all of the intervals,
4896 * turn off the searched bit.
4897 * So that means we check each interval and if
4898 * it is inside any interval we stop and leave
4899 * the bit set. If it is outside we keep checking.
4900 */
4901 if(score != SCORE_UNDEF){
4902 INTVL_S *iv;
4903
4904 for(iv = patgrp->score; iv; iv = iv->next)
4905 if(score >= iv->imin && score <= iv->imax)
4906 break;
4907
4908 if(!iv)
4909 mc->searched = NIL;
4910 }
4911 }
4912
4913 for(i = 1L; i <= stream->nmsgs; i++)
4914 if((mc = mail_elt(stream, i)) != NULL)
4915 mc->sequence = savebits[i];
4916
4917 fs_give((void **)&savebits);
4918 }
4919
4920 /* if there are still matches, check for 8bit subject match */
4921 if(patgrp->stat_8bitsubj != PAT_STAT_EITHER)
4922 find_8bitsubj_in_messages(stream, searchset, patgrp->stat_8bitsubj, 1);
4923
4924 /* if there are still matches, check for charset matches */
4925 if(patgrp->charsets)
4926 find_charsets_in_messages(stream, searchset, patgrp, 1);
4927
4928 /* Still matches, check addrbook */
4929 if(patgrp->inabook != IAB_EITHER)
4930 address_in_abook(stream, searchset, patgrp->inabook, patgrp->abooks);
4931
4932 /* Still matches? Run the categorization command on each msg. */
4933 if(pith_opt_filter_pattern_cmd)
4934 (*pith_opt_filter_pattern_cmd)(patgrp->category_cmd, searchset, stream, patgrp->cat_lim, patgrp->cat);
4935
4936 for(s = searchset; s; s = s->next)
4937 for(msgno = s->first; msgno > 0L && msgno <= s->last; msgno++)
4938 if(msgno > 0L && msgno <= stream->nmsgs
4939 && (mc = mail_elt(stream, msgno)) && mc->searched)
4940 return(1);
4941
4942 return(0);
4943 }
4944
4945
4946 /*
4947 * Look through messages in searchset to see if they contain 8bit
4948 * characters in their subjects. All of the messages in
4949 * searchset should initially have the searched bit set. Turn off the
4950 * searched bit where appropriate.
4951 */
4952 void
find_8bitsubj_in_messages(MAILSTREAM * stream,SEARCHSET * searchset,int stat_8bitsubj,int saveseqbits)4953 find_8bitsubj_in_messages(MAILSTREAM *stream, SEARCHSET *searchset,
4954 int stat_8bitsubj, int saveseqbits)
4955 {
4956 char *savebits = NULL;
4957 SEARCHSET *s, *ss = NULL;
4958 MESSAGECACHE *mc;
4959 long count = 0L;
4960 unsigned long msgno;
4961
4962 /*
4963 * If we are being called while in build_header_line we may
4964 * call build_header_line recursively. So save and restore the
4965 * sequence bits.
4966 */
4967 if(saveseqbits)
4968 savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char));
4969
4970 for(msgno = 1L; msgno <= stream->nmsgs; msgno++){
4971 if((mc = mail_elt(stream, msgno)) != NULL){
4972 if(savebits)
4973 savebits[msgno] = mc->sequence;
4974
4975 mc->sequence = 0;
4976 }
4977 }
4978
4979 /*
4980 * Build a searchset so we can look at all the envelopes
4981 * we need to look at but only those we need to look at.
4982 * Everything with the searched bit set is still a
4983 * possibility, so restrict to that set.
4984 */
4985
4986 for(s = searchset; s; s = s->next)
4987 for(msgno = s->first; msgno <= s->last; msgno++)
4988 if(msgno > 0L && msgno <= stream->nmsgs
4989 && (mc = mail_elt(stream, msgno)) && mc->searched){
4990 mc->sequence = 1;
4991 count++;
4992 }
4993
4994 ss = build_searchset(stream);
4995
4996 if(count){
4997 SEARCHSET **sset;
4998
4999 mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
5000
5001 /*
5002 * This causes the lookahead to fetch precisely
5003 * the messages we want (in the searchset) instead
5004 * of just fetching the next 20 sequential
5005 * messages. If the searching so far has caused
5006 * a sparse searchset in a large mailbox, the
5007 * difference can be substantial.
5008 * This resets automatically after the first fetch.
5009 */
5010 sset = (SEARCHSET **) mail_parameters(stream,
5011 GET_FETCHLOOKAHEAD,
5012 (void *) stream);
5013 if(sset)
5014 *sset = ss;
5015 }
5016
5017 for(s = ss; s; s = s->next){
5018 for(msgno = s->first; msgno <= s->last; msgno++){
5019 ENVELOPE *e;
5020
5021 if(!stream || msgno <= 0L || msgno > stream->nmsgs)
5022 continue;
5023
5024 e = pine_mail_fetchenvelope(stream, msgno);
5025 if(stat_8bitsubj == PAT_STAT_YES){
5026 if(e && e->subject){
5027 char *p;
5028
5029 for(p = e->subject; *p; p++)
5030 if(*p & 0x80)
5031 break;
5032
5033 if(!*p && msgno > 0L && msgno <= stream->nmsgs
5034 && (mc = mail_elt(stream, msgno)))
5035 mc->searched = NIL;
5036 }
5037 else if(msgno > 0L && msgno <= stream->nmsgs
5038 && (mc = mail_elt(stream, msgno)))
5039 mc->searched = NIL;
5040 }
5041 else if(stat_8bitsubj == PAT_STAT_NO){
5042 if(e && e->subject){
5043 char *p;
5044
5045 for(p = e->subject; *p; p++)
5046 if(*p & 0x80)
5047 break;
5048
5049 if(*p && msgno > 0L && msgno <= stream->nmsgs
5050 && (mc = mail_elt(stream, msgno)))
5051 mc->searched = NIL;
5052 }
5053 }
5054 }
5055 }
5056
5057 if(savebits){
5058 for(msgno = 1L; msgno <= stream->nmsgs; msgno++)
5059 if((mc = mail_elt(stream, msgno)) != NULL)
5060 mc->sequence = savebits[msgno];
5061
5062 fs_give((void **) &savebits);
5063 }
5064
5065 if(ss)
5066 mail_free_searchset(&ss);
5067 }
5068
5069
5070 /*
5071 * Look through messages in searchset to see if they contain any of the
5072 * charsets or scripts listed in charsets pattern. All of the messages in
5073 * searchset should initially have the searched bit set. Turn off the
5074 * searched bit where appropriate.
5075 */
5076 void
find_charsets_in_messages(MAILSTREAM * stream,SEARCHSET * searchset,PATGRP_S * patgrp,int saveseqbits)5077 find_charsets_in_messages(MAILSTREAM *stream, SEARCHSET *searchset,
5078 PATGRP_S *patgrp, int saveseqbits)
5079 {
5080 char *savebits = NULL;
5081 unsigned long msgno;
5082 long count = 0L;
5083 MESSAGECACHE *mc;
5084 SEARCHSET *s, *ss;
5085
5086 if(!stream || !patgrp)
5087 return;
5088
5089 /*
5090 * When we actually want to use charsets, we convert it into a list
5091 * of charsets instead of the mixed list of scripts and charsets and
5092 * we eliminate duplicates. This is more efficient when we actually
5093 * do the lookups and compares.
5094 */
5095 if(!patgrp->charsets_list){
5096 PATTERN_S *cs;
5097 const CHARSET *cset;
5098 STRLIST_S *sl = NULL, *newsl;
5099 unsigned long scripts = 0L;
5100 SCRIPT *script;
5101
5102 for(cs = patgrp->charsets; cs; cs = cs->next){
5103 /*
5104 * Run through the charsets pattern looking for
5105 * scripts and set the corresponding script bits.
5106 * If it isn't a script, it is a character set.
5107 */
5108 if(cs->substring && (script = utf8_script(cs->substring)))
5109 scripts |= script->script;
5110 else{
5111 /* add it to list as a specific character set */
5112 newsl = new_strlist(cs->substring);
5113 if(compare_strlists_for_match(sl, newsl)) /* already in list */
5114 free_strlist(&newsl);
5115 else{
5116 newsl->next = sl;
5117 sl = newsl;
5118 }
5119 }
5120 }
5121
5122 /*
5123 * Now scripts has a bit set for each script the user
5124 * specified in the charsets pattern. Go through all of
5125 * the known charsets and include ones in these scripts.
5126 */
5127 if(scripts){
5128 for(cset = utf8_charset(NIL); cset && cset->name; cset++){
5129 if(cset->script & scripts){
5130
5131 /* filter this out of each script, not very useful */
5132 if(!strucmp("ISO-2022-JP-2", cset->name)
5133 || !strucmp("UTF-7", cset->name)
5134 || !strucmp("UTF-8", cset->name))
5135 continue;
5136
5137 /* add cset->name to the list */
5138 newsl = new_strlist(cset->name);
5139 if(compare_strlists_for_match(sl, newsl))
5140 free_strlist(&newsl);
5141 else{
5142 newsl->next = sl;
5143 sl = newsl;
5144 }
5145 }
5146 }
5147 }
5148
5149 patgrp->charsets_list = sl;
5150 }
5151
5152 /*
5153 * This may call build_header_line recursively because we may be in
5154 * build_header_line now. So we have to preserve and restore the
5155 * sequence bits since we want to use them here.
5156 */
5157 if(saveseqbits)
5158 savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char));
5159
5160 for(msgno = 1L; msgno <= stream->nmsgs; msgno++){
5161 if((mc = mail_elt(stream, msgno)) != NULL){
5162 if(savebits)
5163 savebits[msgno] = mc->sequence;
5164
5165 mc->sequence = 0;
5166 }
5167 }
5168
5169
5170 /*
5171 * Build a searchset so we can look at all the bodies
5172 * we need to look at but only those we need to look at.
5173 * Everything with the searched bit set is still a
5174 * possibility, so restrict to that set.
5175 */
5176
5177 for(s = searchset; s; s = s->next)
5178 for(msgno = s->first; msgno <= s->last; msgno++)
5179 if(msgno > 0L && msgno <= stream->nmsgs
5180 && (mc = mail_elt(stream, msgno)) && mc->searched){
5181 mc->sequence = 1;
5182 count++;
5183 }
5184
5185 ss = build_searchset(stream);
5186
5187 if(count){
5188 SEARCHSET **sset;
5189
5190 mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
5191
5192 /*
5193 * This causes the lookahead to fetch precisely
5194 * the messages we want (in the searchset) instead
5195 * of just fetching the next 20 sequential
5196 * messages. If the searching so far has caused
5197 * a sparse searchset in a large mailbox, the
5198 * difference can be substantial.
5199 * This resets automatically after the first fetch.
5200 */
5201 sset = (SEARCHSET **) mail_parameters(stream,
5202 GET_FETCHLOOKAHEAD,
5203 (void *) stream);
5204 if(sset)
5205 *sset = ss;
5206 }
5207
5208 for(s = ss; s; s = s->next){
5209 for(msgno = s->first; msgno <= s->last; msgno++){
5210
5211 if(msgno <= 0L || msgno > stream->nmsgs)
5212 continue;
5213
5214 if(patgrp->charsets_list
5215 && charsets_present_in_msg(stream,msgno,patgrp->charsets_list)){
5216 if(patgrp->charsets->not){
5217 if((mc = mail_elt(stream, msgno)))
5218 mc->searched = NIL;
5219 }
5220 /* else leave it */
5221 }
5222 else{ /* charset isn't in message */
5223 if(!patgrp->charsets->not){
5224 if((mc = mail_elt(stream, msgno)))
5225 mc->searched = NIL;
5226 }
5227 /* else leave it */
5228 }
5229 }
5230 }
5231
5232 if(savebits){
5233 for(msgno = 1L; msgno <= stream->nmsgs; msgno++)
5234 if((mc = mail_elt(stream, msgno)) != NULL)
5235 mc->sequence = savebits[msgno];
5236
5237 fs_give((void **) &savebits);
5238 }
5239
5240 if(ss)
5241 mail_free_searchset(&ss);
5242 }
5243
5244
5245 /*
5246 * Look for any of the charsets in this particular message.
5247 *
5248 * Returns 1 if there is a match, 0 otherwise.
5249 */
5250 int
charsets_present_in_msg(MAILSTREAM * stream,long unsigned int rawmsgno,STRLIST_S * charsets)5251 charsets_present_in_msg(MAILSTREAM *stream, long unsigned int rawmsgno, STRLIST_S *charsets)
5252 {
5253 BODY *body = NULL;
5254 ENVELOPE *env = NULL;
5255 STRLIST_S *msg_charsets = NULL;
5256 int ret = 0;
5257
5258 if(charsets && stream && rawmsgno > 0L && rawmsgno <= stream->nmsgs){
5259 env = pine_mail_fetchstructure(stream, rawmsgno, &body);
5260 collect_charsets_from_subj(env, &msg_charsets);
5261 collect_charsets_from_body(body, &msg_charsets);
5262 if(msg_charsets){
5263 ret = compare_strlists_for_match(msg_charsets, charsets);
5264 free_strlist(&msg_charsets);
5265 }
5266 }
5267
5268 return(ret);
5269 }
5270
5271
5272 void
collect_charsets_from_subj(ENVELOPE * env,STRLIST_S ** listptr)5273 collect_charsets_from_subj(ENVELOPE *env, STRLIST_S **listptr)
5274 {
5275 STRLIST_S *newsl;
5276 char *text, *e;
5277
5278 if(listptr && env && env->subject){
5279 /* find encoded word */
5280 for(text = env->subject; *text; text++){
5281 if((*text == '=') && (text[1] == '?') && isalpha(text[2]) &&
5282 (e = strchr(text+2,'?'))){
5283 *e = '\0'; /* tie off charset name */
5284
5285 newsl = new_strlist(text+2);
5286 *e = '?';
5287
5288 if(compare_strlists_for_match(*listptr, newsl))
5289 free_strlist(&newsl);
5290 else{
5291 newsl->next = *listptr;
5292 *listptr = newsl;
5293 }
5294 }
5295 }
5296 }
5297 }
5298
5299
5300 /*
5301 * Check for any of the charsets in any of the charset params in
5302 * any of the text parts of the body of a message. Put them in the list
5303 * pointed to by listptr.
5304 */
5305 void
collect_charsets_from_body(struct mail_bodystruct * body,STRLIST_S ** listptr)5306 collect_charsets_from_body(struct mail_bodystruct *body, STRLIST_S **listptr)
5307 {
5308 PART *part;
5309 char *cset;
5310
5311 if(listptr && body){
5312 switch(body->type){
5313 case TYPEMULTIPART:
5314 for(part = body->nested.part; part; part = part->next)
5315 collect_charsets_from_body(&part->body, listptr);
5316
5317 break;
5318
5319 case TYPEMESSAGE:
5320 if(!strucmp(body->subtype, "RFC822")){
5321 collect_charsets_from_subj(body->nested.msg->env, listptr);
5322 collect_charsets_from_body(body->nested.msg->body, listptr);
5323 break;
5324 }
5325 /* else fall through to text case */
5326
5327 case TYPETEXT:
5328 cset = parameter_val(body->parameter, "charset");
5329 if(cset){
5330 STRLIST_S *newsl;
5331
5332 newsl = new_strlist(cset);
5333
5334 if(compare_strlists_for_match(*listptr, newsl))
5335 free_strlist(&newsl);
5336 else{
5337 newsl->next = *listptr;
5338 *listptr = newsl;
5339 }
5340
5341 fs_give((void **) &cset);
5342 }
5343
5344 break;
5345
5346 default: /* non-text terminal mode */
5347 break;
5348 }
5349 }
5350 }
5351
5352
5353 /*
5354 * If any of the names in list1 is the same as any of the names in list2
5355 * then return 1, else return 0. Comparison is case independent.
5356 */
5357 int
compare_strlists_for_match(STRLIST_S * list1,STRLIST_S * list2)5358 compare_strlists_for_match(STRLIST_S *list1, STRLIST_S *list2)
5359 {
5360 int ret = 0;
5361 STRLIST_S *cs1, *cs2;
5362
5363 for(cs1 = list1; !ret && cs1; cs1 = cs1->next)
5364 for(cs2 = list2; !ret && cs2; cs2 = cs2->next)
5365 if(cs1->name && cs2->name && !strucmp(cs1->name, cs2->name))
5366 ret = 1;
5367
5368 return(ret);
5369 }
5370
5371
5372 int
match_pattern_folder(PATGRP_S * patgrp,MAILSTREAM * stream)5373 match_pattern_folder(PATGRP_S *patgrp, MAILSTREAM *stream)
5374 {
5375 int is_news;
5376
5377 /* change by sderr : we match FLDR_ANY even if stream is NULL */
5378 return((patgrp->fldr_type == FLDR_ANY)
5379 || (stream
5380 && (((is_news = IS_NEWS(stream))
5381 && patgrp->fldr_type == FLDR_NEWS)
5382 || (!is_news && patgrp->fldr_type == FLDR_EMAIL)
5383 || (patgrp->fldr_type == FLDR_SPECIFIC
5384 && match_pattern_folder_specific(patgrp->folder,
5385 stream, FOR_PATTERN)))));
5386 }
5387
5388
5389 /*
5390 * Returns positive if this stream is open on one of the folders in the
5391 * folders argument, 0 otherwise.
5392 *
5393 * If FOR_PATTERN is set, this interprets simple names as nicknames in
5394 * the incoming collection, otherwise it treats simple names as being in
5395 * the primary collection.
5396 * If FOR_FILT is set, the folder names are detokenized before being used.
5397 */
5398 int
match_pattern_folder_specific(PATTERN_S * folders,MAILSTREAM * stream,int flags)5399 match_pattern_folder_specific(PATTERN_S *folders, MAILSTREAM *stream, int flags)
5400 {
5401 PATTERN_S *p;
5402 int match = 0;
5403 char *patfolder, *free_this = NULL;
5404
5405 dprint((8, "match_pattern_folder_specific\n"));
5406
5407 if(!(stream && stream->mailbox && stream->mailbox[0]))
5408 return(0);
5409
5410 /*
5411 * For each of the folders in the pattern, see if we get
5412 * a match. We're just looking for any match. If none match,
5413 * we return 0, otherwise we fall through and check the rest
5414 * of the pattern. The fact that the string is called "substring"
5415 * is not meaningful. We're just using the convenient pattern
5416 * structure to store a list of folder names. They aren't
5417 * substrings of names, they are the whole name.
5418 */
5419 for(p = folders; !match && p; p = p->next){
5420 free_this = NULL;
5421 if(flags & FOR_FILTER)
5422 patfolder = free_this = detoken_src(p->substring, FOR_FILT, NULL,
5423 NULL, NULL, NULL);
5424 else
5425 patfolder = p->substring;
5426
5427 if(patfolder
5428 && (!strucmp(patfolder, ps_global->inbox_name)
5429 || !strcmp(patfolder, ps_global->VAR_INBOX_PATH))){
5430 if(sp_flagged(stream, SP_INBOX))
5431 match++;
5432 }
5433 else{
5434 char *fname;
5435 char *t, *streamfolder;
5436 char tmp1[MAILTMPLEN], tmp2[MAX(MAILTMPLEN,NETMAXMBX)];
5437 CONTEXT_S *cntxt = NULL;
5438
5439 if(flags & FOR_PATTERN){
5440 /*
5441 * See if patfolder is a nickname in the incoming collection.
5442 * If so, use its real name instead.
5443 */
5444 if(patfolder[0] &&
5445 (ps_global->context_list->use & CNTXT_INCMNG) &&
5446 (fname = (folder_is_nick(patfolder,
5447 FOLDERS(ps_global->context_list),
5448 0))))
5449 patfolder = fname;
5450 }
5451 else{
5452 char *save_ref = NULL;
5453
5454 /*
5455 * If it's an absolute pathname, we treat is as a local file
5456 * instead of interpreting it in the primary context.
5457 */
5458 if(!is_absolute_path(patfolder)
5459 && !(cntxt = default_save_context(ps_global->context_list)))
5460 cntxt = ps_global->context_list;
5461
5462 /*
5463 * Because this check is independent of where the user is
5464 * in the folder hierarchy and has nothing to do with that,
5465 * we want to ignore the reference field built into the
5466 * context. Zero it out temporarily here.
5467 */
5468 if(cntxt && cntxt->dir){
5469 save_ref = cntxt->dir->ref;
5470 cntxt->dir->ref = NULL;
5471 }
5472
5473 patfolder = context_apply(tmp1, cntxt, patfolder, sizeof(tmp1));
5474 if(save_ref)
5475 cntxt->dir->ref = save_ref;
5476 }
5477
5478 switch(patfolder[0]){
5479 case '{':
5480 if(stream->mailbox[0] == '{' &&
5481 same_stream(patfolder, stream) &&
5482 (streamfolder = strindex(&stream->mailbox[1], '}')) &&
5483 (t = strindex(&patfolder[1], '}')) &&
5484 (!strcmp(t+1, streamfolder+1) ||
5485 (*(t+1) == '\0' && !strcmp("INBOX", streamfolder+1))))
5486 match++;
5487
5488 break;
5489
5490 case '#':
5491 if(!strcmp(patfolder, stream->mailbox))
5492 match++;
5493
5494 break;
5495
5496 default:
5497 t = (strlen(patfolder) < (MAILTMPLEN/2))
5498 ? mailboxfile(tmp2, patfolder) : NULL;
5499 if(t && *t && !strcmp(t, stream->mailbox))
5500 match++;
5501
5502 break;
5503 }
5504 }
5505
5506 if(free_this)
5507 fs_give((void **) &free_this);
5508 }
5509
5510 return(match);
5511 }
5512
5513
5514 /*
5515 * generate a search program corresponding to the provided patgrp
5516 */
5517 SEARCHPGM *
match_pattern_srchpgm(PATGRP_S * patgrp,MAILSTREAM * stream,SEARCHSET * searchset)5518 match_pattern_srchpgm(PATGRP_S *patgrp, MAILSTREAM *stream, SEARCHSET *searchset)
5519 {
5520 SEARCHPGM *pgm, *tmppgm;
5521 SEARCHOR *or;
5522 SEARCHSET **sp;
5523
5524 pgm = mail_newsearchpgm();
5525
5526 sp = &pgm->msgno;
5527 /* copy the searchset */
5528 while(searchset){
5529 SEARCHSET *s;
5530
5531 s = mail_newsearchset();
5532 s->first = searchset->first;
5533 s->last = searchset->last;
5534 searchset = searchset->next;
5535 *sp = s;
5536 sp = &s->next;
5537 }
5538
5539 if(!patgrp)
5540 return(pgm);
5541
5542 if(patgrp->subj){
5543 if(patgrp->subj->not)
5544 tmppgm = next_not(pgm);
5545 else
5546 tmppgm = pgm;
5547
5548 set_up_search_pgm("subject", patgrp->subj, tmppgm);
5549 }
5550
5551 if(patgrp->cc){
5552 if(patgrp->cc->not)
5553 tmppgm = next_not(pgm);
5554 else
5555 tmppgm = pgm;
5556
5557 set_up_search_pgm("cc", patgrp->cc, tmppgm);
5558 }
5559
5560 if(patgrp->from){
5561 if(patgrp->from->not)
5562 tmppgm = next_not(pgm);
5563 else
5564 tmppgm = pgm;
5565
5566 set_up_search_pgm("from", patgrp->from, tmppgm);
5567 }
5568
5569 if(patgrp->to){
5570 if(patgrp->to->not)
5571 tmppgm = next_not(pgm);
5572 else
5573 tmppgm = pgm;
5574
5575 set_up_search_pgm("to", patgrp->to, tmppgm);
5576 }
5577
5578 if(patgrp->sender){
5579 if(patgrp->sender->not)
5580 tmppgm = next_not(pgm);
5581 else
5582 tmppgm = pgm;
5583
5584 set_up_search_pgm("sender", patgrp->sender, tmppgm);
5585 }
5586
5587 if(patgrp->news){
5588 if(patgrp->news->not)
5589 tmppgm = next_not(pgm);
5590 else
5591 tmppgm = pgm;
5592
5593 set_up_search_pgm("newsgroups", patgrp->news, tmppgm);
5594 }
5595
5596 /* To OR Cc */
5597 if(patgrp->recip){
5598 if(patgrp->recip->not)
5599 tmppgm = next_not(pgm);
5600 else
5601 tmppgm = pgm;
5602
5603 or = next_or(&tmppgm->or);
5604
5605 set_up_search_pgm("to", patgrp->recip, or->first);
5606 set_up_search_pgm("cc", patgrp->recip, or->second);
5607 }
5608
5609 /* To OR Cc OR From */
5610 if(patgrp->partic){
5611 if(patgrp->partic->not)
5612 tmppgm = next_not(pgm);
5613 else
5614 tmppgm = pgm;
5615
5616 or = next_or(&tmppgm->or);
5617
5618 set_up_search_pgm("to", patgrp->partic, or->first);
5619
5620 or->second->or = mail_newsearchor();
5621 set_up_search_pgm("cc", patgrp->partic, or->second->or->first);
5622 set_up_search_pgm("from", patgrp->partic, or->second->or->second);
5623 }
5624
5625 if(patgrp->arbhdr){
5626 ARBHDR_S *a;
5627
5628 for(a = patgrp->arbhdr; a; a = a->next)
5629 if(a->field && a->field[0] && a->p){
5630 if(a->p->not)
5631 tmppgm = next_not(pgm);
5632 else
5633 tmppgm = pgm;
5634
5635 set_up_search_pgm(a->field, a->p, tmppgm);
5636 }
5637 }
5638
5639 if(patgrp->alltext){
5640 if(patgrp->alltext->not)
5641 tmppgm = next_not(pgm);
5642 else
5643 tmppgm = pgm;
5644
5645 set_up_search_pgm("alltext", patgrp->alltext, tmppgm);
5646 }
5647
5648 if(patgrp->bodytext){
5649 if(patgrp->bodytext->not)
5650 tmppgm = next_not(pgm);
5651 else
5652 tmppgm = pgm;
5653
5654 set_up_search_pgm("bodytext", patgrp->bodytext, tmppgm);
5655 }
5656
5657 if(patgrp->keyword){
5658 PATTERN_S *p_old, *p_new, *new_pattern = NULL, **nextp;
5659 char *q;
5660
5661 if(patgrp->keyword->not)
5662 tmppgm = next_not(pgm);
5663 else
5664 tmppgm = pgm;
5665
5666 /*
5667 * The keyword entries may be nicknames instead of the actual
5668 * keywords, so those need to be converted to actual keywords.
5669 *
5670 * If we search for keywords that are not defined for a folder
5671 * we may get error messages back that we don't want instead of
5672 * just no match. We will build a replacement pattern here which
5673 * contains only the defined subset of the keywords.
5674 */
5675
5676 nextp = &new_pattern;
5677
5678 for(p_old = patgrp->keyword; p_old; p_old = p_old->next){
5679 q = nick_to_keyword(p_old->substring);
5680 if(user_flag_index(stream, q) >= 0){
5681 p_new = (PATTERN_S *) fs_get(sizeof(*p_new));
5682 memset(p_new, 0, sizeof(*p_new));
5683 p_new->substring = cpystr(q);
5684 *nextp = p_new;
5685 nextp = &p_new->next;
5686 }
5687 }
5688
5689 /*
5690 * If there are some matching keywords that are defined in
5691 * the folder, then we are ok because we will match only if
5692 * we match one of those. However, if the list is empty, then
5693 * we can't just leave this part of the search program empty.
5694 * That would result in a match instead of not a match.
5695 * We can fake our way around the problem with NOT. If the
5696 * list is empty we want the opposite, so we insert a NOT in
5697 * front of an empty program. We may end up with NOT NOT if
5698 * this was already NOT'd, but that's ok, too. Alternatively,
5699 * we could undo the first NOT instead.
5700 */
5701
5702 if(new_pattern){
5703 set_up_search_pgm("keyword", new_pattern, tmppgm);
5704 free_pattern(&new_pattern);
5705 }
5706 else
5707 (void) next_not(tmppgm); /* add NOT of something that matches,
5708 so the NOT thing doesn't match */
5709 }
5710
5711 if(patgrp->do_age && patgrp->age){
5712 INTVL_S *iv;
5713 SEARCHOR *or;
5714
5715 tmppgm = pgm;
5716
5717 for(iv = patgrp->age; iv; iv = iv->next){
5718 if(iv->next){
5719 or = next_or(&tmppgm->or);
5720 set_search_by_age(iv, or->first, patgrp->age_uses_sentdate);
5721 tmppgm = or->second;
5722 }
5723 else
5724 set_search_by_age(iv, tmppgm, patgrp->age_uses_sentdate);
5725 }
5726 }
5727
5728 if(patgrp->do_size && patgrp->size){
5729 INTVL_S *iv;
5730 SEARCHOR *or;
5731
5732 tmppgm = pgm;
5733
5734 for(iv = patgrp->size; iv; iv = iv->next){
5735 if(iv->next){
5736 or = next_or(&tmppgm->or);
5737 set_search_by_size(iv, or->first);
5738 tmppgm = or->second;
5739 }
5740 else
5741 set_search_by_size(iv, tmppgm);
5742 }
5743 }
5744
5745 SETPGMSTATUS(patgrp->stat_new,pgm->unseen,pgm->seen);
5746 SETPGMSTATUS(patgrp->stat_rec,pgm->recent,pgm->old);
5747 SETPGMSTATUS(patgrp->stat_del,pgm->deleted,pgm->undeleted);
5748 SETPGMSTATUS(patgrp->stat_imp,pgm->flagged,pgm->unflagged);
5749 SETPGMSTATUS(patgrp->stat_ans,pgm->answered,pgm->unanswered);
5750
5751 return(pgm);
5752 }
5753
5754
5755 SEARCHPGM *
next_not(SEARCHPGM * pgm)5756 next_not(SEARCHPGM *pgm)
5757 {
5758 SEARCHPGMLIST *not, **not_ptr;
5759
5760 if(!pgm)
5761 return(NULL);
5762
5763 /* find next unused not slot */
5764 for(not = pgm->not; not && not->next; not = not->next)
5765 ;
5766
5767 if(not)
5768 not_ptr = ¬->next;
5769 else
5770 not_ptr = &pgm->not;
5771
5772 /* allocate */
5773 *not_ptr = mail_newsearchpgmlist();
5774
5775 return((*not_ptr)->pgm);
5776 }
5777
5778
5779 SEARCHOR *
next_or(struct search_or ** startingor)5780 next_or(struct search_or **startingor)
5781 {
5782 SEARCHOR *or, **or_ptr;
5783
5784 /* find next unused or slot */
5785 for(or = (*startingor); or && or->next; or = or->next)
5786 ;
5787
5788 if(or)
5789 or_ptr = &or->next;
5790 else
5791 or_ptr = startingor;
5792
5793 /* allocate */
5794 *or_ptr = mail_newsearchor();
5795
5796 return(*or_ptr);
5797 }
5798
5799
5800 void
set_up_search_pgm(char * field,PATTERN_S * pattern,SEARCHPGM * pgm)5801 set_up_search_pgm(char *field, PATTERN_S *pattern, SEARCHPGM *pgm)
5802 {
5803 SEARCHOR *or;
5804
5805 if(field && pattern && pgm){
5806
5807 /*
5808 * To is special because we want to use the ReSent-To header instead
5809 * of the To header if it exists. We set up something like:
5810 *
5811 * if((resent-to matches pat1 or pat2...)
5812 * OR
5813 * (<resent-to doesn't exist> AND (to matches pat1 or pat2...)))
5814 *
5815 * Some servers (Exchange, apparently) seem to have trouble with
5816 * the search for the empty string to decide if the header exists
5817 * or not. So, we will search for either the empty string OR the
5818 * header with a SPACE in it. Some still have trouble with this
5819 * so we are changing it to be off by default.
5820 */
5821 if(!strucmp(field, "to") && F_ON(F_USE_RESENTTO, ps_global)){
5822 or = next_or(&pgm->or);
5823
5824 add_type_to_pgm("resent-to", pattern, or->first);
5825
5826 /* check for resent-to doesn't exist */
5827 or->second->not = mail_newsearchpgmlist();
5828
5829 or->second->not->pgm->or = mail_newsearchor();
5830 set_srch("resent-to", " ", or->second->not->pgm->or->first);
5831 set_srch("resent-to", "", or->second->not->pgm->or->second);
5832
5833 /* now add the real To search to second */
5834 add_type_to_pgm(field, pattern, or->second);
5835 }
5836 else
5837 add_type_to_pgm(field, pattern, pgm);
5838 }
5839 }
5840
5841
5842 void
add_type_to_pgm(char * field,PATTERN_S * pattern,SEARCHPGM * pgm)5843 add_type_to_pgm(char *field, PATTERN_S *pattern, SEARCHPGM *pgm)
5844 {
5845 PATTERN_S *p;
5846 SEARCHOR *or;
5847 SEARCHPGM *notpgm, *tpgm;
5848 int cnt = 0;
5849
5850 if(field && pattern && pgm){
5851 /*
5852 * Here is a weird bit of logic. What we want here is simply
5853 * A or B or C or D
5854 * for all of the elements of pattern. Ors are a bit complicated.
5855 * The list of ORs in the SEARCHPGM structure are ANDed together,
5856 * not ORd together. It's for things like
5857 * Subject A or B AND From C or D
5858 * The Subject part would be one member of the OR list and the From
5859 * part would be another member of the OR list. Instead we want
5860 * a big OR which may have more than two members (first and second)
5861 * but the structure just has two members. So we have to build an
5862 * OR tree and we build it by going down one branch of the tree
5863 * instead of by balancing the branches.
5864 *
5865 * or
5866 * / \
5867 * first==A second
5868 * / \
5869 * first==B second
5870 * / \
5871 * first==C second==D
5872 *
5873 * There is an additional problem. Some servers don't like deeply
5874 * nested logic in the SEARCH command. The tree above produces a
5875 * fairly deeply nested command if the user wants to match on
5876 * several different From addresses or Subjects...
5877 * We use the tried and true equation
5878 *
5879 * (A or B) == !(!A and !B)
5880 *
5881 * to change the deeply nested OR tree into ANDs which aren't nested.
5882 * Right now we're only doing that if the nesting is fairly deep.
5883 * We can think of some reasons to do that. First, we know that the
5884 * OR thing works, that's what we've been using for a while and the
5885 * only problem is the deep nesting. 2nd, it is easier to understand.
5886 * 3rd, it looks dumb to use NOT NOT A instead of A.
5887 * It is probably dumb to mix the two, but what the heck.
5888 * Hubert 2003-04-02
5889 */
5890 for(p = pattern; p; p = p->next)
5891 cnt++;
5892
5893 if(cnt < 10){ /* use ORs if count is low */
5894 for(p = pattern; p; p = p->next){
5895 if(p->next){
5896 or = next_or(&pgm->or);
5897
5898 set_srch(field, p->substring ? p->substring : "", or->first);
5899 pgm = or->second;
5900 }
5901 else
5902 set_srch(field, p->substring ? p->substring : "", pgm);
5903 }
5904 }
5905 else{ /* else use ANDs */
5906 /* ( A or B or C ) <=> ! ( !A and !B and !C ) */
5907
5908 /* first, NOT of the whole thing */
5909 notpgm = next_not(pgm);
5910
5911 /* then the not list is ANDed together */
5912 for(p = pattern; p; p = p->next){
5913 tpgm = next_not(notpgm);
5914 set_srch(field, p->substring ? p->substring : "", tpgm);
5915 }
5916 }
5917 }
5918 }
5919
5920
5921 void
set_srch(char * field,char * value,SEARCHPGM * pgm)5922 set_srch(char *field, char *value, SEARCHPGM *pgm)
5923 {
5924 char *decoded;
5925 STRINGLIST **list;
5926
5927 if(!(field && value && pgm))
5928 return;
5929
5930 if(!strucmp(field, "subject"))
5931 list = &pgm->subject;
5932 else if(!strucmp(field, "from"))
5933 list = &pgm->from;
5934 else if(!strucmp(field, "to"))
5935 list = &pgm->to;
5936 else if(!strucmp(field, "cc"))
5937 list = &pgm->cc;
5938 else if(!strucmp(field, "sender"))
5939 list = &pgm->sender;
5940 else if(!strucmp(field, "reply-to"))
5941 list = &pgm->reply_to;
5942 else if(!strucmp(field, "in-reply-to"))
5943 list = &pgm->in_reply_to;
5944 else if(!strucmp(field, "message-id"))
5945 list = &pgm->message_id;
5946 else if(!strucmp(field, "newsgroups"))
5947 list = &pgm->newsgroups;
5948 else if(!strucmp(field, "followup-to"))
5949 list = &pgm->followup_to;
5950 else if(!strucmp(field, "alltext"))
5951 list = &pgm->text;
5952 else if(!strucmp(field, "bodytext"))
5953 list = &pgm->body;
5954 else if(!strucmp(field, "keyword"))
5955 list = &pgm->keyword;
5956 else{
5957 set_srch_hdr(field, value, pgm);
5958 return;
5959 }
5960
5961 if(!list)
5962 return;
5963
5964 *list = mail_newstringlist();
5965 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, value);
5966
5967 (*list)->text.data = (unsigned char *)cpystr(decoded);
5968 (*list)->text.size = strlen(decoded);
5969 }
5970
5971
5972 void
set_srch_hdr(char * field,char * value,SEARCHPGM * pgm)5973 set_srch_hdr(char *field, char *value, SEARCHPGM *pgm)
5974 {
5975 char *decoded;
5976 SEARCHHEADER **hdr;
5977
5978 if(!(field && value && pgm))
5979 return;
5980
5981 hdr = &pgm->header;
5982 if(!hdr)
5983 return;
5984
5985 decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
5986 SIZEOF_20KBUF, value);
5987 while(*hdr && (*hdr)->next)
5988 *hdr = (*hdr)->next;
5989
5990 if(*hdr)
5991 (*hdr)->next = mail_newsearchheader(field, decoded);
5992 else
5993 *hdr = mail_newsearchheader(field, decoded);
5994 }
5995
5996
5997 void
set_search_by_age(INTVL_S * age,SEARCHPGM * pgm,int age_uses_sentdate)5998 set_search_by_age(INTVL_S *age, SEARCHPGM *pgm, int age_uses_sentdate)
5999 {
6000 time_t now, comparetime;
6001 struct tm *tm;
6002 unsigned short i;
6003
6004 if(!(age && pgm))
6005 return;
6006
6007 now = time(0);
6008
6009 if(age->imin >= 0L && age->imin == age->imax){
6010 comparetime = now;
6011 comparetime -= (age->imin * 86400L);
6012 tm = localtime(&comparetime);
6013 if(tm && tm->tm_year >= 70){
6014 i = mail_shortdate(tm->tm_year - 70, tm->tm_mon + 1,
6015 tm->tm_mday);
6016 if(age_uses_sentdate)
6017 pgm->senton = i;
6018 else
6019 pgm->on = i;
6020 }
6021 }
6022 else{
6023 /*
6024 * The 20000's are just protecting against overflows.
6025 * That's back past the start of email time, anyway.
6026 */
6027 if(age->imin > 0L && age->imin < 20000L){
6028 comparetime = now;
6029 comparetime -= ((age->imin - 1L) * 86400L);
6030 tm = localtime(&comparetime);
6031 if(tm && tm->tm_year >= 70){
6032 i = mail_shortdate(tm->tm_year - 70, tm->tm_mon + 1,
6033 tm->tm_mday);
6034 if(age_uses_sentdate)
6035 pgm->sentbefore = i;
6036 else
6037 pgm->before = i;
6038 }
6039 }
6040
6041 if(age->imax >= 0L && age->imax < 20000L){
6042 comparetime = now;
6043 comparetime -= (age->imax * 86400L);
6044 tm = localtime(&comparetime);
6045 if(tm && tm->tm_year >= 70){
6046 i = mail_shortdate(tm->tm_year - 70, tm->tm_mon + 1,
6047 tm->tm_mday);
6048 if(age_uses_sentdate)
6049 pgm->sentsince = i;
6050 else
6051 pgm->since = i;
6052 }
6053 }
6054 }
6055 }
6056
6057
6058 void
set_search_by_size(INTVL_S * size,SEARCHPGM * pgm)6059 set_search_by_size(INTVL_S *size, SEARCHPGM *pgm)
6060 {
6061 if(!(size && pgm))
6062 return;
6063
6064 /*
6065 * INTVL_S intervals include the endpoints, pgm larger and smaller
6066 * do not include the endpoints.
6067 */
6068 if(size->imin != INTVL_UNDEF && size->imin > 0L)
6069 pgm->larger = size->imin - 1L;
6070
6071 if(size->imax != INTVL_UNDEF && size->imax >= 0L && size->imax != INTVL_INF)
6072 pgm->smaller = size->imax + 1L;
6073 }
6074
6075
6076 static char *extra_hdrs;
6077
6078 /*
6079 * Run through the patterns and note which headers we'll need to ask for
6080 * which aren't normally asked for and so won't be cached.
6081 */
6082 void
calc_extra_hdrs(void)6083 calc_extra_hdrs(void)
6084 {
6085 PAT_S *pat = NULL;
6086 int alloced_size;
6087 long type = (ROLE_INCOL | ROLE_SCORE);
6088 ARBHDR_S *a;
6089 PAT_STATE pstate;
6090 char *q, *p = NULL, *hdrs[MLCMD_COUNT + 1], **pp;
6091 INDEX_COL_S *cdesc;
6092 #define INITIALSIZE 1000
6093
6094 q = (char *)fs_get((INITIALSIZE+1) * sizeof(char));
6095 q[0] = '\0';
6096 alloced_size = INITIALSIZE;
6097 p = q;
6098
6099 /*
6100 * *ALWAYS* make sure Resent-To is in the set of
6101 * extra headers getting fetched.
6102 *
6103 * This is because we *will* reference it when we're
6104 * building header lines and thus want it fetched with
6105 * the standard envelope data. Worse, in the IMAP case
6106 * we're called back from c-client with the envelope data
6107 * so we can format and display the index lines as they
6108 * arrive, so we have to ensure the resent-to field
6109 * is in the cache so we don't reenter c-client
6110 * to look for it from the callback. Yeouch.
6111 */
6112 add_eh(&q, &p, "resent-to", &alloced_size);
6113 add_eh(&q, &p, "resent-date", &alloced_size);
6114 add_eh(&q, &p, "resent-from", &alloced_size);
6115 add_eh(&q, &p, "resent-cc", &alloced_size);
6116 add_eh(&q, &p, "resent-subject", &alloced_size);
6117
6118 /*
6119 * Sniff at viewer-hdrs too so we can include them
6120 * if there are any...
6121 */
6122 for(pp = ps_global->VAR_VIEW_HEADERS; pp && *pp; pp++)
6123 if(non_eh(*pp))
6124 add_eh(&q, &p, *pp, &alloced_size);
6125
6126 /*
6127 * Be sure to ask for List management headers too
6128 * since we'll offer their use in the message view
6129 */
6130 for(pp = rfc2369_hdrs(hdrs); *pp; pp++)
6131 add_eh(&q, &p, *pp, &alloced_size);
6132
6133 if(nonempty_patterns(type, &pstate))
6134 for(pat = first_pattern(&pstate);
6135 pat;
6136 pat = next_pattern(&pstate)){
6137 /*
6138 * This section wouldn't be necessary if sender was retrieved
6139 * from the envelope. But if not, we do need to add it.
6140 */
6141 if(pat->patgrp && pat->patgrp->sender)
6142 add_eh(&q, &p, "sender", &alloced_size);
6143
6144 if(pat->patgrp && pat->patgrp->arbhdr)
6145 for(a = pat->patgrp->arbhdr; a; a = a->next)
6146 if(a->field && a->field[0] && a->p && non_eh(a->field))
6147 add_eh(&q, &p, a->field, &alloced_size);
6148 }
6149
6150 /*
6151 * Check for use of HEADER or X-Priority in index-format.
6152 */
6153 for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++){
6154 if(cdesc->ctype == iHeader && cdesc->hdrtok && cdesc->hdrtok->hdrname
6155 && cdesc->hdrtok->hdrname[0] && non_eh(cdesc->hdrtok->hdrname))
6156 add_eh(&q, &p, cdesc->hdrtok->hdrname, &alloced_size);
6157 else if(cdesc->ctype == iPrio
6158 || cdesc->ctype == iPrioAlpha
6159 || cdesc->ctype == iPrioBang)
6160 add_eh(&q, &p, PRIORITYNAME, &alloced_size);
6161 }
6162
6163 /*
6164 * Check for use of scorevalhdrtok in scoring patterns.
6165 */
6166 type = ROLE_SCORE;
6167 if(nonempty_patterns(type, &pstate))
6168 for(pat = first_pattern(&pstate);
6169 pat;
6170 pat = next_pattern(&pstate)){
6171 /*
6172 * This section wouldn't be necessary if sender was retrieved
6173 * from the envelope. But if not, we do need to add it.
6174 */
6175 if(pat->action && pat->action->scorevalhdrtok
6176 && pat->action->scorevalhdrtok->hdrname
6177 && pat->action->scorevalhdrtok->hdrname[0]
6178 && non_eh(pat->action->scorevalhdrtok->hdrname))
6179 add_eh(&q, &p, pat->action->scorevalhdrtok->hdrname, &alloced_size);
6180 }
6181
6182 set_extra_hdrs(q);
6183 if(q)
6184 fs_give((void **)&q);
6185 }
6186
6187
6188 int
non_eh(char * field)6189 non_eh(char *field)
6190 {
6191 char **t;
6192 static char *existing[] = {"subject", "from", "to", "cc", "sender",
6193 "reply-to", "in-reply-to", "message-id",
6194 "path", "newsgroups", "followup-to",
6195 "references", NULL};
6196
6197 /*
6198 * If it is one of these, we should already have it
6199 * from the envelope or from the extra headers c-client
6200 * already adds to the list (hdrheader and hdrtrailer
6201 * in imap4r1.c, Aug 99, slh).
6202 */
6203 for(t = existing; *t; t++)
6204 if(!strucmp(field, *t))
6205 return(FALSE);
6206
6207 return(TRUE);
6208 }
6209
6210
6211 /*
6212 * Add field to extra headers string if not already there.
6213 */
6214 void
add_eh(char ** start,char ** ptr,char * field,int * asize)6215 add_eh(char **start, char **ptr, char *field, int *asize)
6216 {
6217 char *s;
6218
6219 /* already there? */
6220 for(s = *start; (s = srchstr(s, field)) != NULL; s++)
6221 if(s[strlen(field)] == SPACE || s[strlen(field)] == '\0')
6222 return;
6223
6224 /* enough space for it? */
6225 while(strlen(field) + (*ptr - *start) + 1 > *asize){
6226 (*asize) *= 2;
6227 fs_resize((void **)start, (*asize)+1);
6228 *ptr = *start + strlen(*start);
6229 }
6230
6231 if(*ptr > *start)
6232 sstrncpy(ptr, " ", *asize-(*ptr - *start));
6233
6234 sstrncpy(ptr, field, *asize-(*ptr - *start));
6235
6236 (*start)[*asize] = '\0';
6237 }
6238
6239
6240 void
set_extra_hdrs(char * hdrs)6241 set_extra_hdrs(char *hdrs)
6242 {
6243 free_extra_hdrs();
6244 if(hdrs && *hdrs)
6245 extra_hdrs = cpystr(hdrs);
6246 }
6247
6248
6249 char *
get_extra_hdrs(void)6250 get_extra_hdrs(void)
6251 {
6252 return(extra_hdrs);
6253 }
6254
6255
6256 void
free_extra_hdrs(void)6257 free_extra_hdrs(void)
6258 {
6259 if(extra_hdrs)
6260 fs_give((void **)&extra_hdrs);
6261 }
6262
6263
6264 int
is_ascii_string(char * str)6265 is_ascii_string(char *str)
6266 {
6267 if(!str)
6268 return(0);
6269
6270 while(*str && isascii(*str))
6271 str++;
6272
6273 return(*str == '\0');
6274 }
6275
6276
6277 void
free_patline(PAT_LINE_S ** patline)6278 free_patline(PAT_LINE_S **patline)
6279 {
6280 if(patline && *patline){
6281 free_patline(&(*patline)->next);
6282 if((*patline)->filename)
6283 fs_give((void **)&(*patline)->filename);
6284 if((*patline)->filepath)
6285 fs_give((void **)&(*patline)->filepath);
6286 free_pat(&(*patline)->first);
6287 fs_give((void **)patline);
6288 }
6289 }
6290
6291
6292 void
free_pat(PAT_S ** pat)6293 free_pat(PAT_S **pat)
6294 {
6295 if(pat && *pat){
6296 free_pat(&(*pat)->next);
6297 free_patgrp(&(*pat)->patgrp);
6298 free_action(&(*pat)->action);
6299 if((*pat)->raw)
6300 fs_give((void **)&(*pat)->raw);
6301
6302 fs_give((void **)pat);
6303 }
6304 }
6305
6306
6307 void
free_patgrp(PATGRP_S ** patgrp)6308 free_patgrp(PATGRP_S **patgrp)
6309 {
6310 if(patgrp && *patgrp){
6311 if((*patgrp)->nick)
6312 fs_give((void **) &(*patgrp)->nick);
6313
6314 if((*patgrp)->comment)
6315 fs_give((void **) &(*patgrp)->comment);
6316
6317 if((*patgrp)->category_cmd)
6318 free_list_array(&(*patgrp)->category_cmd);
6319
6320 if((*patgrp)->charsets_list)
6321 free_strlist(&(*patgrp)->charsets_list);
6322
6323 free_pattern(&(*patgrp)->to);
6324 free_pattern(&(*patgrp)->cc);
6325 free_pattern(&(*patgrp)->recip);
6326 free_pattern(&(*patgrp)->partic);
6327 free_pattern(&(*patgrp)->from);
6328 free_pattern(&(*patgrp)->sender);
6329 free_pattern(&(*patgrp)->news);
6330 free_pattern(&(*patgrp)->subj);
6331 free_pattern(&(*patgrp)->alltext);
6332 free_pattern(&(*patgrp)->bodytext);
6333 free_pattern(&(*patgrp)->keyword);
6334 free_pattern(&(*patgrp)->charsets);
6335 free_pattern(&(*patgrp)->folder);
6336 free_arbhdr(&(*patgrp)->arbhdr);
6337 free_intvl(&(*patgrp)->score);
6338 free_intvl(&(*patgrp)->age);
6339 fs_give((void **) patgrp);
6340 }
6341 }
6342
6343
6344 void
free_pattern(PATTERN_S ** pattern)6345 free_pattern(PATTERN_S **pattern)
6346 {
6347 if(pattern && *pattern){
6348 free_pattern(&(*pattern)->next);
6349 if((*pattern)->substring)
6350 fs_give((void **)&(*pattern)->substring);
6351 fs_give((void **)pattern);
6352 }
6353 }
6354
6355
6356 void
free_arbhdr(ARBHDR_S ** arbhdr)6357 free_arbhdr(ARBHDR_S **arbhdr)
6358 {
6359 if(arbhdr && *arbhdr){
6360 free_arbhdr(&(*arbhdr)->next);
6361 if((*arbhdr)->field)
6362 fs_give((void **)&(*arbhdr)->field);
6363 free_pattern(&(*arbhdr)->p);
6364 fs_give((void **)arbhdr);
6365 }
6366 }
6367
6368
6369 void
free_intvl(INTVL_S ** intvl)6370 free_intvl(INTVL_S **intvl)
6371 {
6372 if(intvl && *intvl){
6373 free_intvl(&(*intvl)->next);
6374 fs_give((void **) intvl);
6375 }
6376 }
6377
6378
6379 void
free_action(ACTION_S ** action)6380 free_action(ACTION_S **action)
6381 {
6382 if(action && *action){
6383 if((*action)->from)
6384 mail_free_address(&(*action)->from);
6385 if((*action)->replyto)
6386 mail_free_address(&(*action)->replyto);
6387 if((*action)->fcc)
6388 fs_give((void **)&(*action)->fcc);
6389 if((*action)->litsig)
6390 fs_give((void **)&(*action)->litsig);
6391 if((*action)->sig)
6392 fs_give((void **)&(*action)->sig);
6393 if((*action)->template)
6394 fs_give((void **)&(*action)->template);
6395 if((*action)->scorevalhdrtok)
6396 free_hdrtok(&(*action)->scorevalhdrtok);
6397 if((*action)->cstm)
6398 free_list_array(&(*action)->cstm);
6399 if((*action)->smtp)
6400 free_list_array(&(*action)->smtp);
6401 if((*action)->nntp)
6402 free_list_array(&(*action)->nntp);
6403 if((*action)->nick)
6404 fs_give((void **)&(*action)->nick);
6405 if((*action)->inherit_nick)
6406 fs_give((void **)&(*action)->inherit_nick);
6407 if((*action)->incol)
6408 free_color_pair(&(*action)->incol);
6409 if((*action)->folder)
6410 free_pattern(&(*action)->folder);
6411 if((*action)->index_format)
6412 fs_give((void **)&(*action)->index_format);
6413 if((*action)->keyword_set)
6414 free_pattern(&(*action)->keyword_set);
6415 if((*action)->keyword_clr)
6416 free_pattern(&(*action)->keyword_clr);
6417
6418 fs_give((void **)action);
6419 }
6420 }
6421
6422
6423 /*
6424 * Returns an allocated copy of the pat.
6425 *
6426 * Args pat -- the source pat
6427 *
6428 * Returns a copy of pat.
6429 */
6430 PAT_S *
copy_pat(PAT_S * pat)6431 copy_pat(PAT_S *pat)
6432 {
6433 PAT_S *new_pat = NULL;
6434
6435 if(pat){
6436 new_pat = (PAT_S *)fs_get(sizeof(*new_pat));
6437 memset((void *)new_pat, 0, sizeof(*new_pat));
6438
6439 new_pat->patgrp = copy_patgrp(pat->patgrp);
6440 new_pat->action = copy_action(pat->action);
6441 }
6442
6443 return(new_pat);
6444 }
6445
6446
6447 /*
6448 * Returns an allocated copy of the patgrp.
6449 *
6450 * Args patgrp -- the source patgrp
6451 *
6452 * Returns a copy of patgrp.
6453 */
6454 PATGRP_S *
copy_patgrp(PATGRP_S * patgrp)6455 copy_patgrp(PATGRP_S *patgrp)
6456 {
6457 char *p;
6458 PATGRP_S *new_patgrp = NULL;
6459
6460 if(patgrp){
6461 new_patgrp = (PATGRP_S *)fs_get(sizeof(*new_patgrp));
6462 memset((void *)new_patgrp, 0, sizeof(*new_patgrp));
6463
6464 if(patgrp->nick)
6465 new_patgrp->nick = cpystr(patgrp->nick);
6466
6467 if(patgrp->comment)
6468 new_patgrp->comment = cpystr(patgrp->comment);
6469
6470 if(patgrp->to){
6471 p = pattern_to_string(patgrp->to);
6472 new_patgrp->to = string_to_pattern(p);
6473 fs_give((void **)&p);
6474 new_patgrp->to->not = patgrp->to->not;
6475 }
6476
6477 if(patgrp->from){
6478 p = pattern_to_string(patgrp->from);
6479 new_patgrp->from = string_to_pattern(p);
6480 fs_give((void **)&p);
6481 new_patgrp->from->not = patgrp->from->not;
6482 }
6483
6484 if(patgrp->sender){
6485 p = pattern_to_string(patgrp->sender);
6486 new_patgrp->sender = string_to_pattern(p);
6487 fs_give((void **)&p);
6488 new_patgrp->sender->not = patgrp->sender->not;
6489 }
6490
6491 if(patgrp->cc){
6492 p = pattern_to_string(patgrp->cc);
6493 new_patgrp->cc = string_to_pattern(p);
6494 fs_give((void **)&p);
6495 new_patgrp->cc->not = patgrp->cc->not;
6496 }
6497
6498 if(patgrp->recip){
6499 p = pattern_to_string(patgrp->recip);
6500 new_patgrp->recip = string_to_pattern(p);
6501 fs_give((void **)&p);
6502 new_patgrp->recip->not = patgrp->recip->not;
6503 }
6504
6505 if(patgrp->partic){
6506 p = pattern_to_string(patgrp->partic);
6507 new_patgrp->partic = string_to_pattern(p);
6508 fs_give((void **)&p);
6509 new_patgrp->partic->not = patgrp->partic->not;
6510 }
6511
6512 if(patgrp->news){
6513 p = pattern_to_string(patgrp->news);
6514 new_patgrp->news = string_to_pattern(p);
6515 fs_give((void **)&p);
6516 new_patgrp->news->not = patgrp->news->not;
6517 }
6518
6519 if(patgrp->subj){
6520 p = pattern_to_string(patgrp->subj);
6521 new_patgrp->subj = string_to_pattern(p);
6522 fs_give((void **)&p);
6523 new_patgrp->subj->not = patgrp->subj->not;
6524 }
6525
6526 if(patgrp->alltext){
6527 p = pattern_to_string(patgrp->alltext);
6528 new_patgrp->alltext = string_to_pattern(p);
6529 fs_give((void **)&p);
6530 new_patgrp->alltext->not = patgrp->alltext->not;
6531 }
6532
6533 if(patgrp->bodytext){
6534 p = pattern_to_string(patgrp->bodytext);
6535 new_patgrp->bodytext = string_to_pattern(p);
6536 fs_give((void **)&p);
6537 new_patgrp->bodytext->not = patgrp->bodytext->not;
6538 }
6539
6540 if(patgrp->keyword){
6541 p = pattern_to_string(patgrp->keyword);
6542 new_patgrp->keyword = string_to_pattern(p);
6543 fs_give((void **)&p);
6544 new_patgrp->keyword->not = patgrp->keyword->not;
6545 }
6546
6547 if(patgrp->charsets){
6548 p = pattern_to_string(patgrp->charsets);
6549 new_patgrp->charsets = string_to_pattern(p);
6550 fs_give((void **)&p);
6551 new_patgrp->charsets->not = patgrp->charsets->not;
6552 }
6553
6554 if(patgrp->charsets_list)
6555 new_patgrp->charsets_list = copy_strlist(patgrp->charsets_list);
6556
6557 if(patgrp->arbhdr){
6558 ARBHDR_S *aa, *a, *new_a;
6559
6560 aa = NULL;
6561 for(a = patgrp->arbhdr; a; a = a->next){
6562 new_a = (ARBHDR_S *)fs_get(sizeof(*new_a));
6563 memset((void *)new_a, 0, sizeof(*new_a));
6564
6565 if(a->field)
6566 new_a->field = cpystr(a->field);
6567
6568 if(a->p){
6569 p = pattern_to_string(a->p);
6570 new_a->p = string_to_pattern(p);
6571 fs_give((void **)&p);
6572 new_a->p->not = a->p->not;
6573 }
6574
6575 new_a->isemptyval = a->isemptyval;
6576
6577 if(aa){
6578 aa->next = new_a;
6579 aa = aa->next;
6580 }
6581 else{
6582 new_patgrp->arbhdr = new_a;
6583 aa = new_patgrp->arbhdr;
6584 }
6585 }
6586 }
6587
6588 new_patgrp->fldr_type = patgrp->fldr_type;
6589
6590 if(patgrp->folder){
6591 p = pattern_to_string(patgrp->folder);
6592 new_patgrp->folder = string_to_pattern(p);
6593 fs_give((void **)&p);
6594 }
6595
6596 new_patgrp->inabook = patgrp->inabook;
6597
6598 if(patgrp->abooks){
6599 p = pattern_to_string(patgrp->abooks);
6600 new_patgrp->abooks = string_to_pattern(p);
6601 fs_give((void **)&p);
6602 }
6603
6604 new_patgrp->do_score = patgrp->do_score;
6605 if(patgrp->score){
6606 INTVL_S *intvl, *iv, *new_iv;
6607
6608 intvl = NULL;
6609 for(iv = patgrp->score; iv; iv = iv->next){
6610 new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
6611 memset((void *) new_iv, 0, sizeof(*new_iv));
6612
6613 new_iv->imin = iv->imin;
6614 new_iv->imax = iv->imax;
6615
6616 if(intvl){
6617 intvl->next = new_iv;
6618 intvl = intvl->next;
6619 }
6620 else{
6621 new_patgrp->score = new_iv;
6622 intvl = new_patgrp->score;
6623 }
6624 }
6625 }
6626
6627 new_patgrp->do_age = patgrp->do_age;
6628 if(patgrp->age){
6629 INTVL_S *intvl, *iv, *new_iv;
6630
6631 intvl = NULL;
6632 for(iv = patgrp->age; iv; iv = iv->next){
6633 new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
6634 memset((void *) new_iv, 0, sizeof(*new_iv));
6635
6636 new_iv->imin = iv->imin;
6637 new_iv->imax = iv->imax;
6638
6639 if(intvl){
6640 intvl->next = new_iv;
6641 intvl = intvl->next;
6642 }
6643 else{
6644 new_patgrp->age = new_iv;
6645 intvl = new_patgrp->age;
6646 }
6647 }
6648 }
6649
6650 new_patgrp->age_uses_sentdate = patgrp->age_uses_sentdate;
6651
6652 new_patgrp->do_size = patgrp->do_size;
6653 if(patgrp->size){
6654 INTVL_S *intvl, *iv, *new_iv;
6655
6656 intvl = NULL;
6657 for(iv = patgrp->size; iv; iv = iv->next){
6658 new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
6659 memset((void *) new_iv, 0, sizeof(*new_iv));
6660
6661 new_iv->imin = iv->imin;
6662 new_iv->imax = iv->imax;
6663
6664 if(intvl){
6665 intvl->next = new_iv;
6666 intvl = intvl->next;
6667 }
6668 else{
6669 new_patgrp->size = new_iv;
6670 intvl = new_patgrp->size;
6671 }
6672 }
6673 }
6674
6675 new_patgrp->stat_new = patgrp->stat_new;
6676 new_patgrp->stat_rec = patgrp->stat_rec;
6677 new_patgrp->stat_del = patgrp->stat_del;
6678 new_patgrp->stat_imp = patgrp->stat_imp;
6679 new_patgrp->stat_ans = patgrp->stat_ans;
6680
6681 new_patgrp->stat_8bitsubj = patgrp->stat_8bitsubj;
6682 new_patgrp->stat_bom = patgrp->stat_bom;
6683 new_patgrp->stat_boy = patgrp->stat_boy;
6684
6685 new_patgrp->do_cat = patgrp->do_cat;
6686 if(patgrp->cat){
6687 INTVL_S *intvl, *iv, *new_iv;
6688
6689 intvl = NULL;
6690 for(iv = patgrp->cat; iv; iv = iv->next){
6691 new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
6692 memset((void *) new_iv, 0, sizeof(*new_iv));
6693
6694 new_iv->imin = iv->imin;
6695 new_iv->imax = iv->imax;
6696
6697 if(intvl){
6698 intvl->next = new_iv;
6699 intvl = intvl->next;
6700 }
6701 else{
6702 new_patgrp->cat = new_iv;
6703 intvl = new_patgrp->cat;
6704 }
6705 }
6706 }
6707
6708 if(patgrp->category_cmd)
6709 new_patgrp->category_cmd = copy_list_array(patgrp->category_cmd);
6710 }
6711
6712 return(new_patgrp);
6713 }
6714
6715
6716 /*
6717 * Returns an allocated copy of the action.
6718 *
6719 * Args action -- the source action
6720 *
6721 * Returns a copy of action.
6722 */
6723 ACTION_S *
copy_action(ACTION_S * action)6724 copy_action(ACTION_S *action)
6725 {
6726 ACTION_S *newaction = NULL;
6727 char *p;
6728
6729 if(action){
6730 newaction = (ACTION_S *)fs_get(sizeof(*newaction));
6731 memset((void *)newaction, 0, sizeof(*newaction));
6732
6733 newaction->is_a_role = action->is_a_role;
6734 newaction->is_a_incol = action->is_a_incol;
6735 newaction->is_a_score = action->is_a_score;
6736 newaction->is_a_filter = action->is_a_filter;
6737 newaction->is_a_other = action->is_a_other;
6738 newaction->is_a_srch = action->is_a_srch;
6739 newaction->repl_type = action->repl_type;
6740 newaction->forw_type = action->forw_type;
6741 newaction->comp_type = action->comp_type;
6742 newaction->scoreval = action->scoreval;
6743 newaction->kill = action->kill;
6744 newaction->state_setting_bits = action->state_setting_bits;
6745 newaction->move_only_if_not_deleted = action->move_only_if_not_deleted;
6746 newaction->non_terminating = action->non_terminating;
6747 newaction->sort_is_set = action->sort_is_set;
6748 newaction->sortorder = action->sortorder;
6749 newaction->revsort = action->revsort;
6750 newaction->startup_rule = action->startup_rule;
6751
6752 if(action->from)
6753 newaction->from = copyaddrlist(action->from);
6754 if(action->replyto)
6755 newaction->replyto = copyaddrlist(action->replyto);
6756 if(action->cstm)
6757 newaction->cstm = copy_list_array(action->cstm);
6758 if(action->smtp)
6759 newaction->smtp = copy_list_array(action->smtp);
6760 if(action->nntp)
6761 newaction->nntp = copy_list_array(action->nntp);
6762 if(action->fcc)
6763 newaction->fcc = cpystr(action->fcc);
6764 if(action->litsig)
6765 newaction->litsig = cpystr(action->litsig);
6766 if(action->sig)
6767 newaction->sig = cpystr(action->sig);
6768 if(action->template)
6769 newaction->template = cpystr(action->template);
6770 if(action->nick)
6771 newaction->nick = cpystr(action->nick);
6772 if(action->inherit_nick)
6773 newaction->inherit_nick = cpystr(action->inherit_nick);
6774 if(action->incol)
6775 newaction->incol = new_color_pair(action->incol->fg,
6776 action->incol->bg);
6777 if(action->scorevalhdrtok){
6778 newaction->scorevalhdrtok = new_hdrtok(action->scorevalhdrtok->hdrname);
6779 if(action->scorevalhdrtok && action->scorevalhdrtok->fieldseps){
6780 if(newaction->scorevalhdrtok->fieldseps)
6781 fs_give((void **) &newaction->scorevalhdrtok->fieldseps);
6782
6783 newaction->scorevalhdrtok->fieldseps = cpystr(action->scorevalhdrtok->fieldseps);
6784 }
6785 }
6786
6787 if(action->folder){
6788 p = pattern_to_string(action->folder);
6789 newaction->folder = string_to_pattern(p);
6790 fs_give((void **) &p);
6791 }
6792
6793 if(action->keyword_set){
6794 p = pattern_to_string(action->keyword_set);
6795 newaction->keyword_set = string_to_pattern(p);
6796 fs_give((void **) &p);
6797 }
6798
6799 if(action->keyword_clr){
6800 p = pattern_to_string(action->keyword_clr);
6801 newaction->keyword_clr = string_to_pattern(p);
6802 fs_give((void **) &p);
6803 }
6804
6805 if(action->index_format)
6806 newaction->index_format = cpystr(action->index_format);
6807 }
6808
6809 return(newaction);
6810 }
6811
6812
6813 /*
6814 * Given a role, return an allocated role. If this role inherits from
6815 * another role, then do the correct inheriting so that the result is
6816 * the role we want to use. The inheriting that is done is just the set
6817 * of set- actions. This is for role stuff, no inheriting happens for scores
6818 * or for colors.
6819 *
6820 * Args role -- The source role
6821 *
6822 * Returns a role.
6823 */
6824 ACTION_S *
combine_inherited_role(ACTION_S * role)6825 combine_inherited_role(ACTION_S *role)
6826 {
6827 PAT_STATE pstate;
6828 PAT_S *pat;
6829
6830 /*
6831 * Protect against loops in the role inheritance.
6832 */
6833 if(role && role->is_a_role && nonempty_patterns(ROLE_DO_ROLES, &pstate))
6834 for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate))
6835 if(pat->action){
6836 if(pat->action == role)
6837 pat->action->been_here_before = 1;
6838 else
6839 pat->action->been_here_before = 0;
6840 }
6841
6842 return(combine_inherited_role_guts(role));
6843 }
6844
6845
6846 ACTION_S *
combine_inherited_role_guts(ACTION_S * role)6847 combine_inherited_role_guts(ACTION_S *role)
6848 {
6849 ACTION_S *newrole = NULL, *inherit_role = NULL;
6850 PAT_STATE pstate;
6851
6852 if(role && role->is_a_role){
6853 newrole = (ACTION_S *)fs_get(sizeof(*newrole));
6854 memset((void *)newrole, 0, sizeof(*newrole));
6855
6856 newrole->repl_type = role->repl_type;
6857 newrole->forw_type = role->forw_type;
6858 newrole->comp_type = role->comp_type;
6859 newrole->is_a_role = role->is_a_role;
6860
6861 if(role->inherit_nick && role->inherit_nick[0] &&
6862 nonempty_patterns(ROLE_DO_ROLES, &pstate)){
6863 PAT_S *pat;
6864
6865 /* find the inherit_nick pattern */
6866 for(pat = first_pattern(&pstate);
6867 pat;
6868 pat = next_pattern(&pstate)){
6869 if(pat->patgrp &&
6870 pat->patgrp->nick &&
6871 !strucmp(role->inherit_nick, pat->patgrp->nick)){
6872 /* found it, if it has a role, use it */
6873 if(!pat->action->been_here_before){
6874 pat->action->been_here_before = 1;
6875 inherit_role = pat->action;
6876 }
6877
6878 break;
6879 }
6880 }
6881
6882 /*
6883 * inherit_role might inherit further from other roles.
6884 * In any case, we copy it so that we'll consistently have
6885 * an allocated copy.
6886 */
6887 if(inherit_role){
6888 if(inherit_role->inherit_nick && inherit_role->inherit_nick[0])
6889 inherit_role = combine_inherited_role_guts(inherit_role);
6890 else
6891 inherit_role = copy_action(inherit_role);
6892 }
6893 }
6894
6895 if(role->from)
6896 newrole->from = copyaddrlist(role->from);
6897 else if(inherit_role && inherit_role->from)
6898 newrole->from = copyaddrlist(inherit_role->from);
6899
6900 if(role->replyto)
6901 newrole->replyto = copyaddrlist(role->replyto);
6902 else if(inherit_role && inherit_role->replyto)
6903 newrole->replyto = copyaddrlist(inherit_role->replyto);
6904
6905 if(role->fcc)
6906 newrole->fcc = cpystr(role->fcc);
6907 else if(inherit_role && inherit_role->fcc)
6908 newrole->fcc = cpystr(inherit_role->fcc);
6909
6910 if(role->litsig)
6911 newrole->litsig = cpystr(role->litsig);
6912 else if(inherit_role && inherit_role->litsig)
6913 newrole->litsig = cpystr(inherit_role->litsig);
6914
6915 if(role->sig)
6916 newrole->sig = cpystr(role->sig);
6917 else if(inherit_role && inherit_role->sig)
6918 newrole->sig = cpystr(inherit_role->sig);
6919
6920 if(role->template)
6921 newrole->template = cpystr(role->template);
6922 else if(inherit_role && inherit_role->template)
6923 newrole->template = cpystr(inherit_role->template);
6924
6925 if(role->cstm)
6926 newrole->cstm = copy_list_array(role->cstm);
6927 else if(inherit_role && inherit_role->cstm)
6928 newrole->cstm = copy_list_array(inherit_role->cstm);
6929
6930 if(role->smtp)
6931 newrole->smtp = copy_list_array(role->smtp);
6932 else if(inherit_role && inherit_role->smtp)
6933 newrole->smtp = copy_list_array(inherit_role->smtp);
6934
6935 if(role->nntp)
6936 newrole->nntp = copy_list_array(role->nntp);
6937 else if(inherit_role && inherit_role->nntp)
6938 newrole->nntp = copy_list_array(inherit_role->nntp);
6939
6940 if(role->nick)
6941 newrole->nick = cpystr(role->nick);
6942
6943 if(inherit_role)
6944 free_action(&inherit_role);
6945 }
6946
6947 return(newrole);
6948 }
6949
6950
6951 void
mail_expunge_prefilter(MAILSTREAM * stream,int flags)6952 mail_expunge_prefilter(MAILSTREAM *stream, int flags)
6953 {
6954 int sfdo_state = 0, /* Some Filter Depends On or Sets State */
6955 sfdo_scores = 0, /* Some Filter Depends On Scores */
6956 ssdo_state = 0; /* Some Score Depends On State */
6957
6958 if(!stream || !sp_flagged(stream, SP_LOCKED))
6959 return;
6960
6961 /*
6962 * An Expunge causes a re-examination of the filters to
6963 * see if any state changes have caused new matches.
6964 */
6965
6966 sfdo_scores = (scores_are_used(SCOREUSE_GET) & SCOREUSE_FILTERS);
6967 if(sfdo_scores)
6968 ssdo_state = (scores_are_used(SCOREUSE_GET) & SCOREUSE_STATEDEP);
6969
6970 if(!(sfdo_scores && ssdo_state))
6971 sfdo_state = some_filter_depends_on_active_state();
6972
6973
6974 if(sfdo_state || (sfdo_scores && ssdo_state)){
6975 if(sfdo_scores && ssdo_state)
6976 clear_folder_scores(stream);
6977
6978 reprocess_filter_patterns(stream, sp_msgmap(stream),
6979 (flags & MI_CLOSING) |
6980 MI_REFILTERING | MI_STATECHGONLY);
6981 }
6982 }
6983
6984
6985 /*----------------------------------------------------------------------
6986 Dispatch messages matching FILTER patterns.
6987
6988 Args:
6989 stream -- mail stream serving messages
6990 msgmap -- sequence to msgno mapping table
6991 recent -- number of recent messages to check (but really only its
6992 nonzeroness is used)
6993
6994 When we're done, any filtered messages are filtered and the message
6995 mapping table has any filtered messages removed.
6996 ---*/
6997 void
process_filter_patterns(MAILSTREAM * stream,MSGNO_S * msgmap,long int recent)6998 process_filter_patterns(MAILSTREAM *stream, MSGNO_S *msgmap, long int recent)
6999 {
7000 long i, n, raw;
7001 imapuid_t uid;
7002 int we_cancel = 0, any_msgs = 0, any_to_filter = 0;
7003 int exbits, nt = 0, pending_actions = 0, for_debugging = 0;
7004 int cleared_index_cache = 0;
7005 long rflags = ROLE_DO_FILTER;
7006 char *nick = NULL;
7007 char busymsg[80];
7008 MSGNO_S *tmpmap = NULL;
7009 MESSAGECACHE *mc;
7010 PAT_S *pat, *nextpat = NULL;
7011 SEARCHPGM *pgm = NULL;
7012 SEARCHSET *srchset = NULL;
7013 long flags = (SE_NOPREFETCH|SE_FREE);
7014 PAT_STATE pstate;
7015
7016 dprint((5, "process_filter_patterns(stream=%s, recent=%ld)\n",
7017 !stream ? "<null>" :
7018 sp_flagged(stream, SP_INBOX) ? "inbox" :
7019 stream->original_mailbox ? stream->original_mailbox :
7020 stream->mailbox ? stream->mailbox :
7021 "?",
7022 recent));
7023
7024 if(!msgmap || !stream)
7025 return;
7026
7027 if(!recent)
7028 sp_set_flags(stream, sp_flags(stream) | SP_FILTERED);
7029
7030 while(stream && stream->nmsgs && nonempty_patterns(rflags, &pstate)){
7031
7032 for_debugging++;
7033 pending_actions = 0;
7034 nextpat = NULL;
7035
7036 uid = mail_uid(stream, stream->nmsgs);
7037
7038 /*
7039 * Some of the search stuff won't work on old servers so we
7040 * get the data and search locally. Big performance hit.
7041 */
7042 if(is_imap_stream(stream) && !modern_imap_stream(stream))
7043 flags |= SE_NOSERVER;
7044
7045 /*
7046 * ignore all previously filtered messages
7047 * and, if requested, anything not a recent
7048 * arrival...
7049 *
7050 * Here we're using spare6 (MN_STMP), meaning we'll only
7051 * search the ones with spare6 marked, new messages coming
7052 * in will not be considered. There used to be orig_nmsgs,
7053 * which kept track of this, but if a message gets expunged,
7054 * then a new message could be lower than orig_nmsgs.
7055 */
7056 for(i = 1; i <= stream->nmsgs; i++)
7057 if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
7058 if(exbits & MSG_EX_FILTERED){
7059 if((mc = mail_elt(stream, i)) != NULL)
7060 mc->spare6 = 0;
7061 }
7062 else if(!recent || !(exbits & MSG_EX_TESTED)){
7063 if((mc = mail_elt(stream, i)) != NULL)
7064 mc->spare6 = 1;
7065
7066 any_to_filter++;
7067 }
7068 else if((mc = mail_elt(stream, i)) != NULL)
7069 mc->spare6 = 0;
7070 }
7071 else{
7072 if((mc = mail_elt(stream, i)) != NULL)
7073 mc->spare6 = !recent;
7074
7075 any_to_filter += !recent;
7076 }
7077
7078 if(!any_to_filter){
7079 dprint((5, "No messages need filtering\n"));
7080 }
7081
7082 /* Then start searching */
7083 for(pat = first_pattern(&pstate); any_to_filter && pat; pat = nextpat){
7084 nextpat = next_pattern(&pstate);
7085 dprint((5,
7086 "Trying filter \"%s\"\n",
7087 (pat->patgrp && pat->patgrp->nick)
7088 ? pat->patgrp->nick : "?"));
7089 if(pat->patgrp && !pat->patgrp->bogus
7090 && pat->action && !pat->action->bogus
7091 && !trivial_patgrp(pat->patgrp)
7092 && match_pattern_folder(pat->patgrp, stream)
7093 && !match_pattern_folder_specific(pat->action->folder,
7094 stream, FOR_FILTER)){
7095
7096 /*
7097 * We could just keep track of spare6 accurately when
7098 * we change the msgno_exceptions flags, but...
7099 */
7100 for(i = 1; i <= stream->nmsgs; i++){
7101 if((mc=mail_elt(stream, i)) && mc->spare6){
7102 if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
7103 if(exbits & MSG_EX_FILTERED)
7104 mc->sequence = 0;
7105 else if(!recent || !(exbits & MSG_EX_TESTED))
7106 mc->sequence = 1;
7107 else
7108 mc->sequence = 0;
7109 }
7110 else
7111 mc->sequence = !recent;
7112 }
7113 else
7114 mc->sequence = 0;
7115 }
7116
7117 if(!(srchset = build_searchset(stream))){
7118 dprint((5, "Empty searchset\n"));
7119 continue; /* nothing to search, move on */
7120 }
7121
7122 #ifdef DEBUG
7123 {SEARCHSET *s;
7124 dprint((5, "searchset="));
7125 for(s = srchset; s; s = s->next){
7126 if(s->first == s->last || s->last == 0L){
7127 dprint((5, " %ld", s->first));
7128 }
7129 else{
7130 dprint((5, " %ld-%ld", s->first, s->last));
7131 }
7132 }
7133 dprint((5, "\n"));
7134 }
7135 #endif
7136 nick = (pat && pat->patgrp && pat->patgrp->nick
7137 && pat->patgrp->nick[0]) ? pat->patgrp->nick : NULL;
7138 snprintf(busymsg, sizeof(busymsg), _("Processing filter \"%s\""),
7139 nick ? nick : "?");
7140
7141 /*
7142 * The strange last argument is so that the busy message
7143 * won't come out until after a second if the user sets
7144 * the feature to quell "filtering done". That's because
7145 * they are presumably interested in the filtering actions
7146 * themselves more than what is happening, so they'd
7147 * rather see the action messages instead of the processing
7148 * message. That's my theory anyway.
7149 */
7150 if(F_OFF(F_QUELL_FILTER_MSGS, ps_global))
7151 any_msgs = we_cancel = busy_cue(busymsg, NULL,
7152 F_ON(F_QUELL_FILTER_DONE_MSG, ps_global)
7153 ? 1 : 0);
7154
7155 if(pat->patgrp->stat_bom != PAT_STAT_EITHER){
7156 if(pat->patgrp->stat_bom == PAT_STAT_YES){
7157 if(!ps_global->beginning_of_month){
7158 dprint((5,
7159 "Filter %s wants beginning of month and it isn't bom\n",
7160 nick ? nick : "?"));
7161 continue;
7162 }
7163 }
7164 else if(pat->patgrp->stat_bom == PAT_STAT_NO){
7165 if(ps_global->beginning_of_month){
7166 dprint((5,
7167 "Filter %s does not want beginning of month and it is bom\n",
7168 nick ? nick : "?"));
7169 continue;
7170 }
7171 }
7172 }
7173
7174 if(pat->patgrp->stat_boy != PAT_STAT_EITHER){
7175 if(pat->patgrp->stat_boy == PAT_STAT_YES){
7176 if(!ps_global->beginning_of_year){
7177 dprint((5,
7178 "Filter %s wants beginning of year and it isn't boy\n",
7179 nick ? nick : "?"));
7180 continue;
7181 }
7182 }
7183 else if(pat->patgrp->stat_boy == PAT_STAT_NO){
7184 if(ps_global->beginning_of_year){
7185 dprint((5,
7186 "Filter %s does not want beginning of year and it is boy\n",
7187 nick ? nick : "?"));
7188 continue;
7189 }
7190 }
7191 }
7192
7193 pgm = match_pattern_srchpgm(pat->patgrp, stream, srchset);
7194
7195 pine_mail_search_full(stream, "UTF-8", pgm, flags);
7196
7197 /* check scores */
7198 if(scores_are_used(SCOREUSE_GET) & SCOREUSE_FILTERS &&
7199 pat->patgrp->do_score){
7200 SEARCHSET *s, *ss;
7201
7202 /*
7203 * Build a searchset so we can get all the scores we
7204 * need and only the scores we need efficiently.
7205 */
7206
7207 for(i = 1; i <= stream->nmsgs; i++)
7208 if((mc = mail_elt(stream, i)) != NULL)
7209 mc->sequence = 0;
7210
7211 for(s = srchset; s; s = s->next)
7212 for(i = s->first; i <= s->last; i++)
7213 if(i > 0L && stream && i <= stream->nmsgs
7214 && (mc=mail_elt(stream, i)) && mc->searched &&
7215 get_msg_score(stream, i) == SCORE_UNDEF)
7216 mc->sequence = 1;
7217
7218 if((ss = build_searchset(stream)) != NULL){
7219 (void)calculate_some_scores(stream, ss, 0);
7220 mail_free_searchset(&ss);
7221 }
7222
7223 /*
7224 * Now check the patterns which have matched so far
7225 * to see if their score is in the score interval.
7226 */
7227 for(s = srchset; s; s = s->next)
7228 for(i = s->first; i <= s->last; i++)
7229 if(i > 0L && stream && i <= stream->nmsgs
7230 && (mc=mail_elt(stream, i)) && mc->searched){
7231 long score;
7232
7233 score = get_msg_score(stream, i);
7234
7235 /*
7236 * If the score is outside all of the intervals,
7237 * turn off the searched bit.
7238 * So that means we check each interval and if
7239 * it is inside any interval we stop and leave
7240 * the bit set. If it is outside we keep checking.
7241 */
7242 if(score != SCORE_UNDEF){
7243 INTVL_S *iv;
7244
7245 for(iv = pat->patgrp->score; iv; iv = iv->next)
7246 if(score >= iv->imin && score <= iv->imax)
7247 break;
7248
7249 if(!iv)
7250 mc->searched = NIL;
7251 }
7252 }
7253 }
7254
7255 /* check for 8bit subject match or not */
7256 if(pat->patgrp->stat_8bitsubj != PAT_STAT_EITHER)
7257 find_8bitsubj_in_messages(stream, srchset,
7258 pat->patgrp->stat_8bitsubj, 0);
7259
7260 /* if there are still matches, check for charset matches */
7261 if(pat->patgrp->charsets)
7262 find_charsets_in_messages(stream, srchset, pat->patgrp, 0);
7263
7264 if(pat->patgrp->inabook != IAB_EITHER)
7265 address_in_abook(stream, srchset, pat->patgrp->inabook, pat->patgrp->abooks);
7266
7267 /* Still matches? Run the categorization command on each msg. */
7268 if(pith_opt_filter_pattern_cmd)
7269 (*pith_opt_filter_pattern_cmd)(pat->patgrp->category_cmd, srchset, stream, pat->patgrp->cat_lim, pat->patgrp->cat);
7270
7271 if(we_cancel){
7272 cancel_busy_cue(-1);
7273 we_cancel = 0;
7274 }
7275
7276 nt = pat->action->non_terminating;
7277 pending_actions = MAX(nt, pending_actions);
7278
7279 /*
7280 * Change some state bits.
7281 * This used to only happen if kill was not set, but
7282 * it can be useful to Delete a message even if killing.
7283 * That way, it will show up in another pine that isn't
7284 * running the same filter as Deleted, so the user won't
7285 * bother looking at it. Hubert 2004-11-16
7286 */
7287 if(pat->action->state_setting_bits
7288 || pat->action->keyword_set
7289 || pat->action->keyword_clr){
7290 tmpmap = NULL;
7291 mn_init(&tmpmap, stream->nmsgs);
7292
7293 for(i = 1L, n = 0L; i <= stream->nmsgs; i++)
7294 if((mc = mail_elt(stream, i)) && mc->searched
7295 && !(msgno_exceptions(stream, i, "0", &exbits, FALSE)
7296 && (exbits & MSG_EX_FILTERED))){
7297 if(!n++){
7298 mn_set_cur(tmpmap, i);
7299 }
7300 else{
7301 mn_add_cur(tmpmap, i);
7302 }
7303 }
7304
7305 if(n){
7306 long flagbits;
7307 char **keywords_to_set = NULL,
7308 **keywords_to_clr = NULL;
7309 PATTERN_S *pp;
7310 int cnt;
7311
7312 flagbits = pat->action->state_setting_bits;
7313
7314 if(pat->action->keyword_set){
7315 for(cnt = 0, pp = pat->action->keyword_set;
7316 pp; pp = pp->next)
7317 cnt++;
7318
7319 keywords_to_set = (char **) fs_get((cnt+1) *
7320 sizeof(*keywords_to_set));
7321 memset(keywords_to_set, 0,
7322 (cnt+1) * sizeof(*keywords_to_set));
7323 for(cnt = 0, pp = pat->action->keyword_set;
7324 pp; pp = pp->next){
7325 char *q;
7326
7327 q = nick_to_keyword(pp->substring);
7328 if(q && q[0])
7329 keywords_to_set[cnt++] = cpystr(q);
7330 }
7331
7332 flagbits |= F_KEYWORD;
7333 }
7334
7335 if(pat->action->keyword_clr){
7336 for(cnt = 0, pp = pat->action->keyword_clr;
7337 pp; pp = pp->next)
7338 cnt++;
7339
7340 keywords_to_clr = (char **) fs_get((cnt+1) *
7341 sizeof(*keywords_to_clr));
7342 memset(keywords_to_clr, 0,
7343 (cnt+1) * sizeof(*keywords_to_clr));
7344 for(cnt = 0, pp = pat->action->keyword_clr;
7345 pp; pp = pp->next){
7346 char *q;
7347
7348 q = nick_to_keyword(pp->substring);
7349 if(q && q[0])
7350 keywords_to_clr[cnt++] = cpystr(q);
7351 }
7352
7353 flagbits |= F_UNKEYWORD;
7354 }
7355
7356 set_some_flags(stream, tmpmap, flagbits,
7357 keywords_to_set, keywords_to_clr, 1,
7358 nick);
7359 }
7360
7361 mn_give(&tmpmap);
7362 }
7363
7364 /*
7365 * The two halves of the if-else are almost the same and
7366 * could probably be combined cleverly. The if clause
7367 * is simply setting the MSG_EX_FILTERED bit, and leaving
7368 * n set to zero. The msgno_exclude is not done in this case.
7369 * The else clause excludes each message (because it is
7370 * either filtered into nothing or moved to folder). The
7371 * exclude messes with the msgmap and that changes max_msgno,
7372 * so the loop control is a little tricky.
7373 */
7374 if(!(pat->action->kill || pat->action->folder)){
7375 n = 0L;
7376 for(i = 1L; i <= mn_get_total(msgmap); i++)
7377 if((raw = mn_m2raw(msgmap, i)) > 0L
7378 && stream && raw <= stream->nmsgs
7379 && (mc = mail_elt(stream, raw)) && mc->searched){
7380 dprint((5,
7381 "FILTER matching \"%s\": msg %ld%s\n",
7382 nick ? nick : "unnamed",
7383 raw, nt ? " (dont stop)" : ""));
7384 if(msgno_exceptions(stream, raw, "0", &exbits, FALSE))
7385 exbits |= (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
7386 else
7387 exbits = (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
7388
7389 /*
7390 * If this matched an earlier non-terminating rule
7391 * we've been keeping track of that so that we can
7392 * turn it into a permanent match at the end.
7393 * However, now we've matched another rule that is
7394 * terminating so we don't have to worry about it
7395 * anymore. Turn off the flag.
7396 */
7397 if(!nt && exbits & MSG_EX_FILTONCE)
7398 exbits ^= MSG_EX_FILTONCE;
7399
7400 exbits &= ~MSG_EX_STATECHG;
7401
7402 msgno_exceptions(stream, raw, "0", &exbits, TRUE);
7403 }
7404 }
7405 else{
7406 for(i = 1L, n = 0L; i <= mn_get_total(msgmap); )
7407 if((raw = mn_m2raw(msgmap, i))
7408 && raw > 0L && stream && raw <= stream->nmsgs
7409 && (mc = mail_elt(stream, raw)) && mc->searched){
7410 dprint((5,
7411 "FILTER matching \"%s\": msg %ld %s%s\n",
7412 nick ? nick : "unnamed",
7413 raw, pat->action->folder ? "filed" : "killed",
7414 nt ? " (dont stop)" : ""));
7415 if(nt)
7416 i++;
7417 else{
7418 if(!cleared_index_cache
7419 && stream == ps_global->mail_stream){
7420 cleared_index_cache = 1;
7421 clear_index_cache(stream, 0);
7422 }
7423
7424 msgno_exclude(stream, msgmap, i, 1);
7425 /*
7426 * If this message is new, decrement
7427 * new_mail_count. Previously, the caller would
7428 * do this by counting MN_EXCLUDE before and after,
7429 * but the results weren't accurate in the case
7430 * where new messages arrived while filtering,
7431 * or the filtered message could have gotten
7432 * expunged.
7433 */
7434 if(msgno_exceptions(stream, raw, "0", &exbits,
7435 FALSE)
7436 && (exbits & MSG_EX_RECENT)){
7437 long l, ll;
7438
7439 l = sp_new_mail_count(stream);
7440 ll = sp_recent_since_visited(stream);
7441 dprint((5, "New message being filtered, decrement new_mail_count: %ld -> %ld\n", l, l-1L));
7442 if(l > 0L)
7443 sp_set_new_mail_count(stream, l-1L);
7444 if(ll > 0L)
7445 sp_set_recent_since_visited(stream, ll-1L);
7446 }
7447 }
7448
7449 if(msgno_exceptions(stream, raw, "0", &exbits, FALSE))
7450 exbits |= (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
7451 else
7452 exbits = (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
7453
7454 /* set pending exclusion for later */
7455 if(nt)
7456 exbits |= MSG_EX_PEND_EXLD;
7457
7458 /*
7459 * If this matched an earlier non-terminating rule
7460 * we've been keeping track of that so that we can
7461 * turn it into a permanent match at the end.
7462 * However, now we've matched another rule that is
7463 * terminating so we don't have to worry about it
7464 * anymore. Turn off the flags.
7465 */
7466 if(!nt && exbits & MSG_EX_FILTONCE){
7467 exbits ^= MSG_EX_FILTONCE;
7468
7469 /* we've already excluded it, too */
7470 if(exbits & MSG_EX_PEND_EXLD)
7471 exbits ^= MSG_EX_PEND_EXLD;
7472 }
7473
7474 exbits &= ~MSG_EX_STATECHG;
7475
7476 msgno_exceptions(stream, raw, "0", &exbits, TRUE);
7477 n++;
7478 }
7479 else
7480 i++;
7481 }
7482
7483 if(n && pat->action->folder){
7484 PATTERN_S *p;
7485 int err = 0;
7486
7487 tmpmap = NULL;
7488 mn_init(&tmpmap, stream->nmsgs);
7489
7490 /*
7491 * For everything matching msg that hasn't
7492 * already been saved somewhere, do it...
7493 */
7494 for(i = 1L, n = 0L; i <= stream->nmsgs; i++)
7495 if((mc = mail_elt(stream, i)) && mc->searched
7496 && !(msgno_exceptions(stream, i, "0", &exbits, FALSE)
7497 && (exbits & MSG_EX_FILED))){
7498 if(!n++){
7499 mn_set_cur(tmpmap, i);
7500 }
7501 else{
7502 mn_add_cur(tmpmap, i);
7503 }
7504 }
7505
7506 /*
7507 * Remove already deleted messages from the tmp
7508 * message map.
7509 * There is a bug with this. If a filter moves a
7510 * message to another folder _and_ sets the deleted
7511 * status, then the setting of the deleted status
7512 * will already have happened above in set_some_flags.
7513 * So if the move_only_if_not_deleted bit is set that
7514 * message will never be moved. A workaround for the
7515 * user is to not set the move-only-if-not-deleted
7516 * option.
7517 */
7518 if(n && pat->action->move_only_if_not_deleted){
7519 char *seq;
7520 MSGNO_S *tmpmap2 = NULL;
7521 long nn = 0L;
7522 MESSAGECACHE *mc;
7523
7524 mn_init(&tmpmap2, stream->nmsgs);
7525
7526 /*
7527 * First, make sure elts are valid for all the
7528 * interesting messages.
7529 */
7530 if((seq = invalid_elt_sequence(stream, tmpmap)) != NULL){
7531 pine_mail_fetch_flags(stream, seq, NIL);
7532 fs_give((void **) &seq);
7533 }
7534
7535 for(i = mn_first_cur(tmpmap); i > 0L;
7536 i = mn_next_cur(tmpmap)){
7537 mc = ((raw = mn_m2raw(tmpmap, i)) > 0L
7538 && stream && raw <= stream->nmsgs)
7539 ? mail_elt(stream, raw) : NULL;
7540 if(mc && !mc->deleted){
7541 if(!nn++){
7542 mn_set_cur(tmpmap2, i);
7543 }
7544 else{
7545 mn_add_cur(tmpmap2, i);
7546 }
7547 }
7548 }
7549
7550 mn_give(&tmpmap);
7551 tmpmap = tmpmap2;
7552 n = nn;
7553 }
7554
7555 if(n){
7556 for(p = pat->action->folder; p; p = p->next){
7557 int dval;
7558 int flags_for_save;
7559 char *processed_name;
7560
7561 /* does this filter set delete bit? ... */
7562 convert_statebits_to_vals(pat->action->state_setting_bits, &dval, NULL, NULL, NULL);
7563 /* ... if so, tell save not to fix it before copy */
7564 flags_for_save = SV_FOR_FILT | SV_INBOXWOCNTXT |
7565 (nt ? 0 : SV_DELETE) |
7566 ((dval != ACT_STAT_SET) ? SV_FIX_DELS : 0);
7567 processed_name = detoken_src(p->substring,
7568 FOR_FILT, NULL,
7569 NULL, NULL, NULL);
7570 if(move_filtered_msgs(stream, tmpmap,
7571 (processed_name && *processed_name)
7572 ? processed_name : p->substring,
7573 flags_for_save, nick)){
7574 /*
7575 * If we filtered into the current
7576 * folder, chuck a ping down the
7577 * stream so the user can notice it
7578 * before the next new mail check...
7579 */
7580 if(ps_global->mail_stream
7581 && ps_global->mail_stream != stream
7582 && match_pattern_folder_specific(
7583 pat->action->folder,
7584 ps_global->mail_stream,
7585 FOR_FILTER)){
7586 (void) pine_mail_ping(ps_global->mail_stream);
7587 }
7588 }
7589 else{
7590 err = 1;
7591 break;
7592 }
7593
7594 if(processed_name)
7595 fs_give((void **) &processed_name);
7596 }
7597
7598 if(!err)
7599 for(n = mn_first_cur(tmpmap);
7600 n > 0L;
7601 n = mn_next_cur(tmpmap)){
7602
7603 if(msgno_exceptions(stream, mn_m2raw(tmpmap, n),
7604 "0", &exbits, FALSE))
7605 exbits |= (nt ? MSG_EX_FILEONCE : MSG_EX_FILED);
7606 else
7607 exbits = (nt ? MSG_EX_FILEONCE : MSG_EX_FILED);
7608
7609 exbits &= ~MSG_EX_STATECHG;
7610
7611 msgno_exceptions(stream, mn_m2raw(tmpmap, n),
7612 "0", &exbits, TRUE);
7613 }
7614 }
7615
7616 mn_give(&tmpmap);
7617 }
7618
7619 mail_free_searchset(&srchset);
7620 }
7621
7622 /*
7623 * If this is the last rule,
7624 * we make sure we delete messages that we delayed deleting
7625 * in the save. We delayed so that the deletion wouldn't have
7626 * an effect on later rules. We convert any temporary
7627 * FILED (FILEONCE) and FILTERED (FILTONCE) flags
7628 * (which were set by an earlier non-terminating rule)
7629 * to permanent. We also exclude some messages from the view.
7630 */
7631 if(pending_actions && !nextpat){
7632
7633 pending_actions = 0;
7634 tmpmap = NULL;
7635 mn_init(&tmpmap, stream->nmsgs);
7636
7637 for(i = 1L, n = 0L; i <= mn_get_total(msgmap); i++){
7638
7639 raw = mn_m2raw(msgmap, i);
7640 if(msgno_exceptions(stream, raw, "0", &exbits, FALSE)){
7641 if(exbits & MSG_EX_FILEONCE){
7642 if(!n++){
7643 mn_set_cur(tmpmap, raw);
7644 }
7645 else{
7646 mn_add_cur(tmpmap, raw);
7647 }
7648 }
7649 }
7650 }
7651
7652 if(n)
7653 set_some_flags(stream, tmpmap, F_DEL, NULL, NULL, 0, NULL);
7654
7655 mn_give(&tmpmap);
7656
7657 for(i = 1L; i <= mn_get_total(msgmap); i++){
7658 raw = mn_m2raw(msgmap, i);
7659 if(msgno_exceptions(stream, raw, "0", &exbits, FALSE)){
7660 if(exbits & MSG_EX_PEND_EXLD){
7661 if(!cleared_index_cache
7662 && stream == ps_global->mail_stream){
7663 cleared_index_cache = 1;
7664 clear_index_cache(stream, 0);
7665 }
7666
7667 msgno_exclude(stream, msgmap, i, 1);
7668 if(msgno_exceptions(stream, raw, "0",
7669 &exbits, FALSE)
7670 && (exbits & MSG_EX_RECENT)){
7671 long l, ll;
7672
7673 /*
7674 * If this message is new, decrement
7675 * new_mail_count. See the above
7676 * call to msgno_exclude.
7677 */
7678 l = sp_new_mail_count(stream);
7679 ll = sp_recent_since_visited(stream);
7680 dprint((5, "New message being filtered. Decrement new_mail_count: %ld -> %ld\n", l, l-1L));
7681 if(l > 0L)
7682 sp_set_new_mail_count(stream, l - 1L);
7683 if(ll > 0L)
7684 sp_set_recent_since_visited(stream, ll - 1L);
7685 }
7686
7687 i--; /* to compensate for loop's i++ */
7688 }
7689
7690 /* get rid of temporary flags */
7691 if(exbits & (MSG_EX_FILTONCE | MSG_EX_FILEONCE |
7692 MSG_EX_PEND_EXLD)){
7693 if(exbits & MSG_EX_FILTONCE){
7694 /* convert to permanent */
7695 exbits ^= MSG_EX_FILTONCE;
7696 exbits |= MSG_EX_FILTERED;
7697 }
7698
7699 /* convert to permanent */
7700 if(exbits & MSG_EX_FILEONCE){
7701 exbits ^= MSG_EX_FILEONCE;
7702 exbits |= MSG_EX_FILED;
7703 }
7704
7705 if(exbits & MSG_EX_PEND_EXLD)
7706 exbits ^= MSG_EX_PEND_EXLD;
7707
7708 exbits &= ~MSG_EX_STATECHG;
7709
7710 msgno_exceptions(stream, raw, "0", &exbits,TRUE);
7711 }
7712 }
7713 }
7714 }
7715 }
7716
7717 /* New mail arrival means start over */
7718 if(mail_uid(stream, stream->nmsgs) == uid)
7719 break;
7720 /* else, go again */
7721
7722 recent = 1; /* only check recent ones now */
7723 }
7724
7725 if(!recent){
7726 /* clear status change flags */
7727 for(i = 1; i <= stream->nmsgs; i++){
7728 if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
7729 if(exbits & MSG_EX_STATECHG){
7730 exbits &= ~MSG_EX_STATECHG;
7731 msgno_exceptions(stream, i, "0", &exbits, TRUE);
7732 }
7733 }
7734 }
7735 }
7736
7737 /* clear any private "recent" flags and add TESTED flag */
7738 for(i = 1; i <= stream->nmsgs; i++){
7739 if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
7740 if(exbits & MSG_EX_RECENT
7741 || !(exbits & MSG_EX_TESTED)
7742 || (!recent && exbits & MSG_EX_STATECHG)){
7743 exbits &= ~MSG_EX_RECENT;
7744 exbits |= MSG_EX_TESTED;
7745 if(!recent)
7746 exbits &= ~MSG_EX_STATECHG;
7747
7748 msgno_exceptions(stream, i, "0", &exbits, TRUE);
7749 }
7750 }
7751 else{
7752 exbits = MSG_EX_TESTED;
7753 msgno_exceptions(stream, i, "0", &exbits, TRUE);
7754 }
7755
7756 /* clear any stmp flags just in case */
7757 if((mc = mail_elt(stream, i)) != NULL)
7758 mc->spare6 = 0;
7759 }
7760
7761 msgmap->flagged_stmp = 0L;
7762
7763 if(any_msgs && F_OFF(F_QUELL_FILTER_MSGS, ps_global)
7764 && F_OFF(F_QUELL_FILTER_DONE_MSG, ps_global)){
7765 q_status_message(SM_ORDER, 0, 1, _("filtering done"));
7766 display_message('x');
7767 }
7768 }
7769
7770
7771 /*
7772 * Re-check the filters for matches because a change of message state may
7773 * have changed the results.
7774 */
7775 void
reprocess_filter_patterns(MAILSTREAM * stream,MSGNO_S * msgmap,int flags)7776 reprocess_filter_patterns(MAILSTREAM *stream, MSGNO_S *msgmap, int flags)
7777 {
7778 if(stream){
7779 long i;
7780 int exbits;
7781
7782 if(msgno_include(stream, msgmap, flags)
7783 && stream == ps_global->mail_stream
7784 && !(flags & MI_CLOSING)){
7785 clear_index_cache(stream, 0);
7786 refresh_sort(stream, msgmap, SRT_NON);
7787 ps_global->mangled_header = 1;
7788 }
7789
7790 /*
7791 * Passing 1 in the last argument causes it to only look at the
7792 * messages we included above, which should be only the ones we
7793 * need to look at.
7794 */
7795 process_filter_patterns(stream, msgmap,
7796 (flags & MI_STATECHGONLY) ? 1L : 0);
7797
7798 /* clear status change flags */
7799 for(i = 1; i <= stream->nmsgs; i++){
7800 if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
7801 if(exbits & MSG_EX_STATECHG){
7802 exbits &= ~MSG_EX_STATECHG;
7803 msgno_exceptions(stream, i, "0", &exbits, TRUE);
7804 }
7805 }
7806 }
7807 }
7808 }
7809
7810
7811 /*
7812 * When killing or filtering we don't want to match by mistake. So if
7813 * a pattern has nothing set except the Current Folder Type (which is always
7814 * set to something) we'll consider it to be trivial and not a match.
7815 * match_pattern uses this to determine if there is a match, where it is
7816 * just triggered on the Current Folder Type.
7817 */
7818 int
trivial_patgrp(PATGRP_S * patgrp)7819 trivial_patgrp(PATGRP_S *patgrp)
7820 {
7821 int ret = 1;
7822
7823 if(patgrp){
7824 if(patgrp->subj || patgrp->cc || patgrp->from || patgrp->to ||
7825 patgrp->sender || patgrp->news || patgrp->recip || patgrp->partic ||
7826 patgrp->alltext || patgrp->bodytext)
7827 ret = 0;
7828
7829 if(ret && patgrp->do_age)
7830 ret = 0;
7831
7832 if(ret && patgrp->do_size)
7833 ret = 0;
7834
7835 if(ret && patgrp->do_score)
7836 ret = 0;
7837
7838 if(ret && patgrp->category_cmd && patgrp->category_cmd[0])
7839 ret = 0;
7840
7841 if(ret && patgrp_depends_on_state(patgrp))
7842 ret = 0;
7843
7844 if(ret && patgrp->stat_8bitsubj != PAT_STAT_EITHER)
7845 ret = 0;
7846
7847 if(ret && patgrp->charsets)
7848 ret = 0;
7849
7850 if(ret && patgrp->stat_bom != PAT_STAT_EITHER)
7851 ret = 0;
7852
7853 if(ret && patgrp->stat_boy != PAT_STAT_EITHER)
7854 ret = 0;
7855
7856 if(ret && patgrp->inabook != IAB_EITHER)
7857 ret = 0;
7858
7859 if(ret && patgrp->arbhdr){
7860 ARBHDR_S *a;
7861
7862 for(a = patgrp->arbhdr; a && ret; a = a->next)
7863 if(a->field && a->field[0] && a->p)
7864 ret = 0;
7865 }
7866 }
7867
7868 return(ret);
7869 }
7870
7871
7872 int
some_filter_depends_on_active_state(void)7873 some_filter_depends_on_active_state(void)
7874 {
7875 long rflags = ROLE_DO_FILTER;
7876 PAT_S *pat;
7877 PAT_STATE pstate;
7878 int ret = 0;
7879
7880 if(nonempty_patterns(rflags, &pstate)){
7881
7882 for(pat = first_pattern(&pstate);
7883 pat && !ret;
7884 pat = next_pattern(&pstate))
7885 if(patgrp_depends_on_active_state(pat->patgrp))
7886 ret++;
7887 }
7888
7889 return(ret);
7890 }
7891
7892
7893 /*----------------------------------------------------------------------
7894 Move all messages with sequence bit lit to dstfldr
7895
7896 Args: stream -- stream to use
7897 msgmap -- map of messages to be moved
7898 dstfldr -- folder to receive moved messages
7899 flags_for_save
7900
7901 Returns: nonzero on success or on readonly stream
7902 ----*/
7903 int
move_filtered_msgs(MAILSTREAM * stream,MSGNO_S * msgmap,char * dstfldr,int flags_for_save,char * nick)7904 move_filtered_msgs(MAILSTREAM *stream, MSGNO_S *msgmap, char *dstfldr,
7905 int flags_for_save, char *nick)
7906 {
7907 long n;
7908 int we_cancel = 0, width;
7909 CONTEXT_S *save_context = NULL;
7910 char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1];
7911 char *save_ref = NULL;
7912 #define FILTMSG_MAX 30
7913
7914 if(!stream)
7915 return 0;
7916
7917 if(READONLY_FOLDER(stream)){
7918 dprint((1,
7919 "Can't delete messages in readonly folder \"%s\"\n",
7920 STREAMNAME(stream)));
7921 q_status_message1(SM_ORDER, 1, 3,
7922 _("Can't delete messages in readonly folder \"%s\""),
7923 STREAMNAME(stream));
7924 return 1;
7925 }
7926
7927 buf[0] = '\0';
7928
7929 width = MAX(10, ps_global->ttyo ? ps_global->ttyo->screen_cols : 80);
7930 snprintf(buf, sizeof(buf), "%.30s%.2sMoving %.10s filtered message%.2s to \"\"",
7931 nick ? nick : "", nick ? ": " : "",
7932 comatose(mn_total_cur(msgmap)), plural(mn_total_cur(msgmap)));
7933 /* 2 is for brackets, 5 is for " DONE" in busy alarm */
7934 width -= (strlen(buf) + 2 + 5);
7935 snprintf(buf, sizeof(buf), "%.30s%.2sMoving %.10s filtered message%.2s to \"%s\"",
7936 nick ? nick : "", nick ? ": " : "",
7937 comatose(mn_total_cur(msgmap)), plural(mn_total_cur(msgmap)),
7938 short_str(dstfldr, sbuf, sizeof(sbuf), width, FrontDots));
7939
7940 dprint((5, "%s\n", buf));
7941
7942 if(F_OFF(F_QUELL_FILTER_MSGS, ps_global))
7943 we_cancel = busy_cue(buf, NULL, 0);
7944
7945 if(!is_absolute_path(dstfldr)
7946 && !(save_context = default_save_context(ps_global->context_list)))
7947 save_context = ps_global->context_list;
7948
7949 /*
7950 * Because this save is happening independent of where the user is
7951 * in the folder hierarchy and has nothing to do with that, we want
7952 * to ignore the reference field built into the context. Zero it out
7953 * temporarily here so it won't affect the results of context_apply
7954 * in save.
7955 *
7956 * This might be a problem elsewhere, as well. The same thing as this
7957 * is also done in match_pattern_folder_specific, which is also only
7958 * called from within process_filter_patterns. But there could be
7959 * others. We could have a separate function, something like
7960 * copy_default_save_context(), that automatically zeroes out the
7961 * reference field in the copy. However, some of the uses of
7962 * default_save_context() require that a pointer into the actual
7963 * context list is returned, so this would have to be done carefully.
7964 * Besides, we don't know of any other problems so we'll just change
7965 * these known cases for now.
7966 */
7967 if(save_context && save_context->dir){
7968 save_ref = save_context->dir->ref;
7969 save_context->dir->ref = NULL;
7970 }
7971
7972 n = save(ps_global, stream, save_context, dstfldr, msgmap, flags_for_save);
7973
7974 if(save_ref)
7975 save_context->dir->ref = save_ref;
7976
7977 if(n != mn_total_cur(msgmap)){
7978 int exbits;
7979 long x;
7980
7981 buf[0] = '\0';
7982
7983 /* Clear "filtered" flags for failed messages */
7984 for(x = mn_first_cur(msgmap); x > 0L; x = mn_next_cur(msgmap))
7985 if(n-- <= 0 && msgno_exceptions(stream, mn_m2raw(msgmap, x),
7986 "0", &exbits, FALSE)){
7987 exbits &= ~(MSG_EX_FILTONCE | MSG_EX_FILEONCE |
7988 MSG_EX_FILTERED | MSG_EX_FILED);
7989 msgno_exceptions(stream, mn_m2raw(msgmap, x),
7990 "0", &exbits, TRUE);
7991 }
7992
7993 /* then re-incorporate them into folder they belong */
7994 (void) msgno_include(stream, sp_msgmap(stream), MI_NONE);
7995 clear_index_cache(stream, 0);
7996 refresh_sort(stream, sp_msgmap(stream), SRT_NON);
7997 ps_global->mangled_header = 1;
7998 }
7999 else{
8000 snprintf(buf, sizeof(buf), _("Filtered all %s message to \"%s\""),
8001 comatose(n), dstfldr);
8002 dprint((5, "%s\n", buf));
8003 }
8004
8005 if(we_cancel)
8006 cancel_busy_cue(buf[0] ? 0 : -1);
8007
8008 return(buf[0] != '\0');
8009 }
8010
8011
8012 /*----------------------------------------------------------------------
8013 Move all messages with sequence bit lit to dstfldr
8014
8015 Args: stream -- stream to use
8016 msgmap -- which messages to set
8017 flagbits -- which flags to set or clear
8018 kw_on -- keywords to set
8019 kw_off -- keywords to clear
8020 verbose -- 1 => busy alarm after 1 second
8021 2 => forced busy alarm
8022 ----*/
8023 void
set_some_flags(MAILSTREAM * stream,MSGNO_S * msgmap,long int flagbits,char ** kw_on,char ** kw_off,int verbose,char * nick)8024 set_some_flags(MAILSTREAM *stream, MSGNO_S *msgmap, long int flagbits,
8025 char **kw_on, char **kw_off, int verbose, char *nick)
8026 {
8027 long count = 0L, flipped_flags;
8028 int we_cancel = 0;
8029 char buf[150], *seq;
8030
8031 if(!stream)
8032 return;
8033
8034 if(READONLY_FOLDER(stream)){
8035 dprint((1, "Can't set flags in readonly folder \"%s\"\n",
8036 STREAMNAME(stream)));
8037 q_status_message1(SM_ORDER, 1, 3,
8038 _("Can't set flags in readonly folder \"%s\""),
8039 STREAMNAME(stream));
8040 return;
8041 }
8042
8043 /* use this to determine if anything needs to be done */
8044 flipped_flags = ((flagbits & F_ANS) ? F_UNANS : 0) |
8045 ((flagbits & F_UNANS) ? F_ANS : 0) |
8046 ((flagbits & F_FLAG) ? F_UNFLAG : 0) |
8047 ((flagbits & F_UNFLAG) ? F_FLAG : 0) |
8048 ((flagbits & F_DEL) ? F_UNDEL : 0) |
8049 ((flagbits & F_UNDEL) ? F_DEL : 0) |
8050 ((flagbits & F_SEEN) ? F_UNSEEN : 0) |
8051 ((flagbits & F_UNSEEN) ? F_SEEN : 0) |
8052 ((flagbits & F_KEYWORD) ? F_UNKEYWORD : 0) |
8053 ((flagbits & F_UNKEYWORD) ? F_KEYWORD : 0);
8054 if((seq = currentf_sequence(stream, msgmap, flipped_flags, &count, 0,
8055 kw_off, kw_on)) != NULL){
8056 char *sets = NULL, *clears = NULL;
8057 char *ps, *pc, **t;
8058 size_t clen, slen;
8059
8060 /* allocate enough space for mail_flags arguments */
8061 for(slen=100, t = kw_on; t && *t; t++)
8062 slen += (strlen(*t) + 1);
8063
8064 sets = (char *) fs_get(slen * sizeof(*sets));
8065
8066 for(clen=100, t = kw_off; t && *t; t++)
8067 clen += (strlen(*t) + 1);
8068
8069 clears = (char *) fs_get(clen * sizeof(*clears));
8070
8071 sets[0] = clears[0] = '\0';
8072 ps = sets;
8073 pc = clears;
8074
8075 snprintf(buf, sizeof(buf), "%.30s%.2sSetting flags in %.10s message%.10s",
8076 nick ? nick : "", nick ? ": " : "",
8077 comatose(count), plural(count));
8078
8079 if(F_OFF(F_QUELL_FILTER_MSGS, ps_global))
8080 we_cancel = busy_cue(buf, NULL, verbose ? 0 : 1);
8081
8082 /*
8083 * What's going on here? If we want to set more than one flag
8084 * we can do it with a single roundtrip by combining the arguments
8085 * into a single call and separating them with spaces.
8086 */
8087 if(flagbits & F_ANS)
8088 sstrncpy(&ps, "\\ANSWERED", slen-(ps-sets));
8089 if(flagbits & F_FLAG){
8090 if(ps > sets)
8091 sstrncpy(&ps, " ", slen-(ps-sets));
8092
8093 sstrncpy(&ps, "\\FLAGGED", slen-(ps-sets));
8094 }
8095 if(flagbits & F_DEL){
8096 if(ps > sets)
8097 sstrncpy(&ps, " ", slen-(ps-sets));
8098
8099 sstrncpy(&ps, "\\DELETED", slen-(ps-sets));
8100 }
8101 if(flagbits & F_SEEN){
8102 if(ps > sets)
8103 sstrncpy(&ps, " ", slen-(ps-sets));
8104
8105 sstrncpy(&ps, "\\SEEN", slen-(ps-sets));
8106 }
8107 if(flagbits & F_KEYWORD){
8108 for(t = kw_on; t && *t; t++){
8109 int i;
8110
8111 /*
8112 * We may be able to tell that this will fail before
8113 * we actually try it.
8114 */
8115 if(stream->kwd_create ||
8116 (((i=user_flag_index(stream, *t)) >= 0) && i < NUSERFLAGS)){
8117 if(ps > sets)
8118 sstrncpy(&ps, " ", slen-(ps-sets));
8119
8120 sstrncpy(&ps, *t, slen-(ps-sets));
8121 }
8122 else{
8123 int some_defined = 0;
8124 static int msg_delivered = 0;
8125
8126 some_defined = some_user_flags_defined(stream);
8127
8128 if(msg_delivered++ < 2){
8129 char b[200], c[200], *p;
8130 int w;
8131
8132 if(some_defined){
8133 snprintf(b, sizeof(b), "Can't set \"%.30s\". No more keywords in ", keyword_to_nick(*t));
8134 w = MIN((ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - strlen(b) - 1 - 2, sizeof(c)-1);
8135 p = short_str(STREAMNAME(stream), c, sizeof(c), w, FrontDots);
8136 q_status_message2(SM_ORDER, 3, 3, "%s%s!", b, p);
8137 }
8138 else{
8139 snprintf(b, sizeof(b), "Can't set \"%.30s\". Can't add keywords in ", keyword_to_nick(*t));
8140 w = MIN((ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - strlen(b) - 1 - 2, sizeof(c)-1);
8141 p = short_str(STREAMNAME(stream), c, sizeof(c), w, FrontDots);
8142 q_status_message2(SM_ORDER, 3, 3, "%s%s!", b, p);
8143 }
8144 }
8145
8146 if(some_defined){
8147 dprint((1, "Can't set keyword \"%s\". No more keywords allowed in %s\n", *t, stream->mailbox ? stream->mailbox : "target folder"));
8148 }
8149 else{
8150 dprint((1, "Can't set keyword \"%s\". Can't add keywords in %s\n", *t, stream->mailbox ? stream->mailbox : "target folder"));
8151 }
8152 }
8153 }
8154 }
8155
8156 /* need a separate call for the clears */
8157 if(flagbits & F_UNANS)
8158 sstrncpy(&pc, "\\ANSWERED", clen-(pc-clears));
8159 if(flagbits & F_UNFLAG){
8160 if(pc > clears)
8161 sstrncpy(&pc, " ", clen-(pc-clears));
8162
8163 sstrncpy(&pc, "\\FLAGGED", clen-(pc-clears));
8164 }
8165 if(flagbits & F_UNDEL){
8166 if(pc > clears)
8167 sstrncpy(&pc, " ", clen-(pc-clears));
8168
8169 sstrncpy(&pc, "\\DELETED", clen-(pc-clears));
8170 }
8171 if(flagbits & F_UNSEEN){
8172 if(pc > clears)
8173 sstrncpy(&pc, " ", clen-(pc-clears));
8174
8175 sstrncpy(&pc, "\\SEEN", clen-(pc-clears));
8176 }
8177 if(flagbits & F_UNKEYWORD){
8178 for(t = kw_off; t && *t; t++){
8179 if(pc > clears)
8180 sstrncpy(&pc, " ", clen-(pc-clears));
8181
8182 sstrncpy(&pc, *t, clen-(pc-clears));
8183 }
8184 }
8185
8186
8187 if(sets[0])
8188 mail_flag(stream, seq, sets, ST_SET);
8189
8190 if(clears[0])
8191 mail_flag(stream, seq, clears, 0L);
8192
8193 fs_give((void **) &sets);
8194 fs_give((void **) &clears);
8195 fs_give((void **) &seq);
8196
8197 if(we_cancel)
8198 cancel_busy_cue(buf[0] ? 0 : -1);
8199 }
8200 }
8201
8202
8203 /*
8204 * Delete messages which are marked FILTERED and excluded.
8205 * Messages which are FILTERED but not excluded are those that have had
8206 * their state set by a filter pattern, but are to remain in the same
8207 * folder.
8208 */
8209 void
delete_filtered_msgs(MAILSTREAM * stream)8210 delete_filtered_msgs(MAILSTREAM *stream)
8211 {
8212 int exbits;
8213 long i;
8214 char *seq;
8215 MESSAGECACHE *mc;
8216
8217 for(i = 1L; i <= stream->nmsgs; i++)
8218 if(msgno_exceptions(stream, i, "0", &exbits, FALSE)
8219 && (exbits & MSG_EX_FILTERED)
8220 && get_lflag(stream, NULL, i, MN_EXLD)){
8221 if((mc = mail_elt(stream, i)) != NULL)
8222 mc->sequence = 1;
8223 }
8224 else if((mc = mail_elt(stream, i)) != NULL)
8225 mc->sequence = 0;
8226
8227 if((seq = build_sequence(stream, NULL, NULL)) != NULL){
8228 mail_flag(stream, seq, "\\DELETED", ST_SET | ST_SILENT);
8229 fs_give((void **) &seq);
8230 }
8231 }
8232