1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * Kill file handling
6 */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include "config.h"
13 #include "global.h"
14 #include "db.h"
15 #include "kill.h"
16 #include "match.h"
17 #include "menu.h"
18 #include "regexp.h"
19 #include "nn_term.h"
20
21 /* prototypes further down */
22
23 int killed_articles;
24 int dflt_kill_select = 30;
25 int kill_file_loaded = 0;
26 int kill_debug = 0;
27 int kill_ref_count = 0;
28
29 extern char *temp_file;
30 extern char delayed_msg[];
31
32 static char KILL_FILE[] = "kill";
33 static char COMPILED_KILL[] = "KILL.COMP";
34
35 #define COMP_KILL_MAGIC 0x4b694c6f /* KiLo */
36
37 /*
38 * kill flags
39 */
40
41 #define COMP_KILL_ENTRY 0x80000000
42
43 #define GROUP_REGEXP 0x01000000
44 #define GROUP_REGEXP_HDR 0x02000000
45
46 #define AND_MATCH 0x00020000
47 #define OR_MATCH 0x00010000
48
49 #define KILL_CASE_MATCH 0x00000100
50 #define KILL_ON_REGEXP 0x00000200
51 #define KILL_UNLESS_MATCH 0x00000400
52
53 #define AUTO_KILL 0x00000001
54 #define AUTO_SELECT 0x00000002
55 #define ON_SUBJECT 0x00000004
56 #define ON_SENDER 0x00000008
57 #define ON_FOLLOW_UP 0x00000010
58 #define ON_ANY_REFERENCES 0x00000020
59 #define ON_NOT_FOLLOW_UP 0x00000040
60
61 /*
62 * external flag representation
63 */
64
65 #define EXT_AUTO_KILL '!'
66 #define EXT_AUTO_SELECT '+'
67 #define EXT_KILL_UNLESS_MATCH '~'
68 #define EXT_ON_FOLLOW_UP '>'
69 #define EXT_ON_NOT_FOLLOW_UP '<'
70 #define EXT_ON_ANY_REFERENCES 'a'
71 #define EXT_ON_SUBJECT 's'
72 #define EXT_ON_SENDER 'n'
73 #define EXT_KILL_CASE_MATCH '='
74 #define EXT_KILL_ON_REGEXP '/'
75 #define EXT_AND_MATCH '&'
76 #define EXT_OR_MATCH '|'
77
78 /*
79 * period = nnn DAYS
80 */
81
82 #define DAYS * 24 * 60 * 60
83
84
85 /*
86 * kill_article
87 *
88 * return 1 to kill article, 0 to include it
89 */
90
91 typedef struct kill_list_entry {
92 flag_type kill_flag;
93 char *kill_pattern;
94 regexp *kill_regexp;
95 struct kill_list_entry *next_kill;
96 } kill_list_entry;
97
98 static kill_list_entry *kill_tab;
99 static char *kill_patterns;
100
101 static kill_list_entry *global_kill_list = NULL;
102 static kill_list_entry latest_kl_entry;
103
104 typedef struct {
105 regexp *group_regexp;
106 kill_list_entry *kill_entry;
107 } kill_group_regexp;
108
109 static kill_group_regexp *group_regexp_table = NULL;
110 static int regexp_table_size = 0;
111 static kill_list_entry *regexp_kill_list = NULL;
112 static group_header *current_kill_group = NULL;
113
114 /* kill.c */
115
116 static void build_regexp_kill(void);
117 static kill_list_entry *exec_kill(register kill_list_entry * kl, register article_header * ah, int *unlessp, int do_kill, int do_select);
118 static void fput_pattern(register char *p, register FILE * f);
119 static char *get_pattern(register char *p, int *lenp, int more);
120 static int compile_kill_file(void);
121 static void free_kill_list(register kill_list_entry * kl);
122 static int print_kill(register kill_list_entry * kl);
123
124 /*
125 * Build regexp_kill_list for current_group
126 */
127
128 static void
build_regexp_kill(void)129 build_regexp_kill(void)
130 {
131 register kill_group_regexp *tb;
132 register int n, used_last;
133 register char *name;
134
135 regexp_kill_list = NULL;
136 current_kill_group = current_group;
137 name = current_group->group_name;
138 used_last = 0; /* get AND_MATCH/OR_MATCH for free */
139
140 for (n = regexp_table_size, tb = group_regexp_table; --n >= 0; tb++) {
141 if (tb->group_regexp != NULL) {
142 used_last = 0;
143 if (!regexec(tb->group_regexp, name))
144 continue;
145 } else if (!used_last)
146 continue;
147
148 tb->kill_entry->next_kill = regexp_kill_list;
149 regexp_kill_list = tb->kill_entry;
150 used_last = 1;
151 }
152 }
153
154 /*
155 * execute kill patterns on article
156 */
157
158 static kill_list_entry *
exec_kill(register kill_list_entry * kl,register article_header * ah,int * unlessp,int do_kill,int do_select)159 exec_kill(register kill_list_entry * kl, register article_header * ah, int *unlessp, int do_kill, int do_select)
160 {
161 register flag_type flag;
162 register char *string;
163
164 for (; kl != NULL; kl = kl->next_kill) {
165 flag = kl->kill_flag;
166
167 if (do_select && (flag & AUTO_SELECT) == 0)
168 goto failed;
169 if (do_kill && (flag & AUTO_KILL) == 0)
170 goto failed;
171
172 if (kill_debug && print_kill(kl) < 0)
173 kill_debug = 0;
174
175 if (flag & KILL_UNLESS_MATCH)
176 *unlessp = 1;
177
178 if (flag & ON_ANY_REFERENCES) {
179 if (ah->replies & 0x7f)
180 goto match;
181 goto failed;
182 }
183 if (flag & ON_SUBJECT) {
184 if (flag & ON_FOLLOW_UP) {
185 if ((ah->replies & 0x80) == 0)
186 goto failed;
187 } else if (flag & ON_NOT_FOLLOW_UP) {
188 if (ah->replies & 0x80)
189 goto failed;
190 }
191 string = ah->subject;
192 } else
193 string = ah->sender;
194
195 if (flag & KILL_CASE_MATCH) {
196 if (flag & KILL_ON_REGEXP) {
197 if (regexec(kl->kill_regexp, string))
198 goto match;
199 } else if (strcmp(kl->kill_pattern, string) == 0)
200 goto match;
201 } else if (flag & KILL_ON_REGEXP) {
202 if (regexec_fold(kl->kill_regexp, string))
203 goto match;
204 } else if (strmatch_fold(kl->kill_pattern, string))
205 goto match;
206
207 failed:
208 if ((flag & AND_MATCH) == 0)
209 continue;
210
211 do /* skip next */
212 kl = kl->next_kill;
213 while (kl && (kl->kill_flag & AND_MATCH));
214 if (kl)
215 continue;
216 break;
217
218 match:
219 if (kill_debug) {
220 pg_next();
221 tprintf("%sMATCH\n", flag & AND_MATCH ? "PARTIAL " : "");
222 }
223 if (flag & AND_MATCH)
224 continue;
225 break;
226 }
227 return kl;
228 }
229
230
231 int
kill_article(article_header * ah)232 kill_article(article_header * ah)
233 {
234 register kill_list_entry *kl;
235 int unless_match = 0;
236
237 if (kill_debug) {
238 clrdisp();
239 pg_init(0, 1);
240 pg_next();
241 so_printf("\1KILL: %s: %s%-.40s (%d)\1",
242 ah->sender, ah->replies & 0x80 ? "Re: " : "",
243 ah->subject, ah->replies & 0x7f);
244 }
245 kl = exec_kill((kill_list_entry *) (current_group->kill_list), ah,
246 &unless_match, 0, 0);
247 if (kl == NULL && group_regexp_table != NULL) {
248 if (current_kill_group != current_group)
249 build_regexp_kill();
250 kl = exec_kill(regexp_kill_list, ah, &unless_match, 0, 0);
251 }
252 if (kl == NULL)
253 kl = exec_kill(global_kill_list, ah, &unless_match, 0, 0);
254
255 if (kl != NULL) {
256 if (kl->kill_flag & AUTO_KILL)
257 goto did_kill;
258
259 if (kl->kill_flag & AUTO_SELECT) {
260 ah->attr = A_AUTO_SELECT;
261 goto did_select;
262 }
263 goto no_kill;
264 }
265 if (unless_match)
266 goto did_kill;
267
268 no_kill:
269 if (kill_ref_count && (int) (ah->replies & 0x7f) >= kill_ref_count) {
270 if (kill_debug) {
271 pg_next();
272 tprintf("REFERENCE COUNT (%d) >= %d\n",
273 ah->replies & 0x7f, kill_ref_count);
274 }
275 goto did_kill;
276 }
277 did_select:
278 if (kill_debug && pg_end() < 0)
279 kill_debug = 0;
280 return 0;
281
282 did_kill:
283 if (kill_debug && pg_end() < 0)
284 kill_debug = 0;
285 killed_articles++;
286 return 1;
287 }
288
289
290 int
auto_select_article(article_header * ah,int do_select)291 auto_select_article(article_header * ah, int do_select)
292 {
293 register kill_list_entry *kl;
294 int dummy;
295
296 if (do_select == 1) {
297 kl = ah->a_group ? (kill_list_entry *) (ah->a_group->kill_list) :
298 (kill_list_entry *) (current_group->kill_list);
299 kl = exec_kill(kl, ah, &dummy, !do_select, do_select);
300 if (kl == NULL && group_regexp_table != NULL) {
301 if (current_kill_group != current_group)
302 build_regexp_kill();
303 kl = exec_kill(regexp_kill_list, ah, &dummy, !do_select, do_select);
304 }
305 if (kl == NULL)
306 kl = exec_kill(global_kill_list, ah, &dummy, !do_select, do_select);
307 } else {
308 kl = exec_kill(&latest_kl_entry, ah, &dummy, !do_select, do_select);
309 }
310
311 if (kl == NULL)
312 return 0;
313
314 if (!do_select)
315 killed_articles++;
316 return 1;
317 }
318
319
320 static void
fput_pattern(register char * p,register FILE * f)321 fput_pattern(register char *p, register FILE * f)
322 {
323 register char c;
324
325 while ((c = *p++)) {
326 if (c == ':' || c == '\\')
327 putc('\\', f);
328 putc(c, f);
329 }
330 }
331
332 static char *
get_pattern(register char * p,int * lenp,int more)333 get_pattern(register char *p, int *lenp, int more)
334 {
335 register char c, *q, *start;
336
337 start = q = p;
338 while ((c = *p++)) {
339 if (c == '\\') {
340 c = *p++;
341 if (c != ':' && c != '\\')
342 *q++ = '\\';
343 *q++ = c;
344 continue;
345 }
346 if (more) {
347 if (c == ':')
348 break;
349 if (c == NL)
350 return NULL;
351 } else if (c == NL)
352 break;
353
354 *q++ = c;
355 }
356
357 if (c == NUL)
358 return NULL;
359
360 *q++ = NUL;
361 *lenp = q - start;
362 return p;
363 }
364
365 void
enter_kill_file(group_header * gh,char * pattern,register flag_type flag,int days)366 enter_kill_file(group_header * gh, char *pattern, register flag_type flag, int days)
367 {
368 FILE *killf;
369 register kill_list_entry *kl;
370 regexp *re;
371 char *str;
372
373 str = copy_str(pattern);
374
375 if ((flag & KILL_CASE_MATCH) == 0)
376 fold_string(str);
377
378 if (flag & KILL_ON_REGEXP) {
379 re = regcomp(pattern);
380 if (re == NULL)
381 return;
382 } else
383 re = NULL;
384
385 killf = open_file(relative(nn_directory, KILL_FILE), OPEN_APPEND);
386 if (killf == NULL) {
387 msg("cannot create kill file");
388 return;
389 }
390 if (days >= 0) {
391 if (days == 0)
392 days = 30;
393 fprintf(killf, "%ld:", (long) (cur_time() + days DAYS));
394 }
395 if (gh)
396 fputs(gh->group_name, killf);
397 fputc(':', killf);
398
399 if (flag & KILL_UNLESS_MATCH)
400 fputc(EXT_KILL_UNLESS_MATCH, killf);
401 if (flag & AUTO_KILL)
402 fputc(EXT_AUTO_KILL, killf);
403 if (flag & AUTO_SELECT)
404 fputc(EXT_AUTO_SELECT, killf);
405 if (flag & ON_FOLLOW_UP)
406 fputc(EXT_ON_FOLLOW_UP, killf);
407 if (flag & ON_NOT_FOLLOW_UP)
408 fputc(EXT_ON_NOT_FOLLOW_UP, killf);
409 if (flag & ON_ANY_REFERENCES)
410 fputc(EXT_ON_ANY_REFERENCES, killf);
411 if (flag & ON_SENDER)
412 fputc(EXT_ON_SENDER, killf);
413 if (flag & ON_SUBJECT)
414 fputc(EXT_ON_SUBJECT, killf);
415 if (flag & KILL_CASE_MATCH)
416 fputc(EXT_KILL_CASE_MATCH, killf);
417 if (flag & KILL_ON_REGEXP)
418 fputc(EXT_KILL_ON_REGEXP, killf);
419 fputc(':', killf);
420
421 fput_pattern(pattern, killf);
422 fputc(NL, killf);
423
424 fclose(killf);
425 rm_kill_file();
426
427 kl = newobj(kill_list_entry, 1);
428
429 latest_kl_entry.kill_pattern = kl->kill_pattern = str;
430 latest_kl_entry.kill_regexp = kl->kill_regexp = re;
431 latest_kl_entry.kill_flag = kl->kill_flag = flag;
432 latest_kl_entry.next_kill = NULL;
433
434 if (gh) {
435 kl->next_kill = (kill_list_entry *) (gh->kill_list);
436 gh->kill_list = (char *) kl;
437 } else {
438 kl->next_kill = global_kill_list;
439 global_kill_list = kl;
440 }
441 }
442
443
444 typedef struct {
445 group_number ck_group;
446 flag_type ck_flag;
447 long ck_pattern_index;
448 } comp_kill_entry;
449
450 typedef struct {
451 long ckh_magic;
452 time_t ckh_db_check;
453 off_t ckh_pattern_offset;
454 long ckh_pattern_size;
455 long ckh_entries;
456 long ckh_regexp_size;
457 } comp_kill_header;
458
459
460 int
kill_menu(article_header * ah)461 kill_menu(article_header * ah)
462 {
463 int days;
464 register flag_type flag;
465 char *mode1, *mode2;
466 char *pattern, *dflt, *days_str, buffer[512];
467 group_header *gh;
468
469 days = dflt_kill_select % 100;
470 flag = (dflt_kill_select / 100) ? AUTO_SELECT : AUTO_KILL;
471 prompt("\1AUTO\1 (k)ill or (s)elect (CR => %s subject %d days) ",
472 flag == AUTO_KILL ? "Kill" : "Select", days);
473
474 switch (get_c()) {
475 case CR:
476 case NL:
477 if (ah == NULL) {
478 ah = get_menu_article();
479 if (ah == NULL)
480 return -1;
481 }
482 strcpy(buffer, ah->subject);
483 enter_kill_file(current_group, buffer,
484 flag | ON_SUBJECT | KILL_CASE_MATCH, days);
485 msg("DONE");
486 return 1;
487
488 case 'k':
489 case 'K':
490 case '!':
491 flag = AUTO_KILL;
492 mode1 = "KILL";
493 break;
494 case 's':
495 case 'S':
496 case '+':
497 flag = AUTO_SELECT;
498 mode1 = "SELECT";
499 break;
500 default:
501 return -1;
502 }
503
504 prompt("\1AUTO %s\1 on (s)ubject or (n)ame (s)", mode1);
505
506 dflt = NULL;
507 switch (get_c()) {
508 case 'n':
509 case 'N':
510 flag |= ON_SENDER;
511 if (ah)
512 dflt = ah->sender;
513 mode2 = "Name";
514 break;
515 case 's':
516 case 'S':
517 case SP:
518 case CR:
519 case NL:
520 flag |= ON_SUBJECT;
521 if (ah)
522 dflt = ah->subject;
523 mode2 = "Subject";
524 break;
525 default:
526 return -1;
527 }
528
529 prompt("\1%s %s:\1 (%=/) ", mode1, mode2);
530
531 pattern = get_s(dflt, NONE, "%=/", NULL_FCT);
532 if (pattern == NULL)
533 return -1;
534 if (*pattern == NUL || *pattern == '%' || *pattern == '=') {
535 if (dflt && *dflt)
536 pattern = dflt;
537 else {
538 if ((ah = get_menu_article()) == NULL)
539 return -1;
540 pattern = (flag & ON_SUBJECT) ? ah->subject : ah->sender;
541 }
542 flag |= KILL_CASE_MATCH;
543 } else if (*pattern == '/') {
544 prompt("\1%s %s\1 (regexp): ", mode1, mode2);
545
546 pattern = get_s(NONE, NONE, NONE, NULL_FCT);
547 if (pattern == NULL || *pattern == NUL)
548 return -1;
549 flag |= KILL_ON_REGEXP;
550 }
551 strcpy(buffer, pattern);
552 pattern = buffer;
553
554 prompt("\1%s\1 in (g)roup '%s' or in (a)ll groups (g)",
555 mode1, current_group->group_name);
556
557 switch (get_c()) {
558 case 'g':
559 case 'G':
560 case SP:
561 case CR:
562 case NL:
563 gh = current_group;
564 break;
565 case 'A':
566 case 'a':
567 gh = NULL;
568 break;
569 default:
570 return -1;
571 }
572
573 prompt("\1Lifetime of entry in days\1 (p)ermanent (30) ");
574 days_str = get_s(" 30 days", NONE, "pP", NULL_FCT);
575 if (days_str == NULL)
576 return -1;
577
578 if (*days_str == NUL) {
579 days_str = "30 days";
580 days = 30;
581 } else if (*days_str == 'p' || *days_str == 'P') {
582 days_str = "perm";
583 days = -1;
584 } else if (isdigit(*days_str)) {
585 days = atoi(days_str);
586 sprintf(days_str, "%d days", days);
587 } else {
588 ding();
589 return -1;
590 }
591
592 prompt("\1CONFIRM\1 %s %s %s%s: %-.35s%s ",
593 mode1, mode2, days_str,
594 (flag & KILL_CASE_MATCH) ? " exact" :
595 (flag & KILL_ON_REGEXP) ? " regexp" : "",
596 pattern, (int) strlen(pattern) > 35 ? "..." : "");
597 if (yes(0) <= 0)
598 return -1;
599
600 enter_kill_file(gh, pattern, flag, days);
601
602 return (flag & AUTO_KILL) ? 1 : 0;
603 }
604
605 static int
compile_kill_file(void)606 compile_kill_file(void)
607 {
608 FILE *killf, *compf, *patternf, *dropf;
609 comp_kill_header header;
610 comp_kill_entry entry;
611 time_t now, age;
612 long cur_line_start;
613 char line[512];
614 register char *cp, *np;
615 register int c;
616 group_header *gh;
617 flag_type flag, fields[10];
618 int any_errors, nfield, nf, len;
619
620 any_errors = 0;
621 header.ckh_entries = header.ckh_regexp_size = 0;
622
623 killf = open_file(relative(nn_directory, KILL_FILE),
624 OPEN_READ | DONT_CREATE);
625 if (killf == NULL)
626 return 0;
627
628 dropf = NULL;
629
630 compf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_CREATE);
631 if (compf == NULL)
632 goto err1;
633
634 new_temp_file();
635 if ((patternf = open_file(temp_file, OPEN_CREATE)) == NULL)
636 goto err2;
637
638 msg("Compiling kill file");
639
640 fseek(compf, sizeof(header), 0);
641
642 now = cur_time();
643
644 next_entry:
645
646 for (;;) {
647 cur_line_start = ftell(killf);
648
649 if (fgets(line, 512, killf) == NULL)
650 break;
651
652 cp = line;
653 while (*cp && isascii(*cp) && isspace(*cp))
654 cp++;
655 if (*cp == NUL || *cp == '#' || !isascii(*cp))
656 continue;
657
658 if ((np = strchr(cp, ':')) == NULL)
659 goto bad_entry;
660
661 /* optional "age:" */
662
663 if (np != cp && isdigit(*cp)) {
664 *np++ = NUL;
665 age = (time_t) atol(cp);
666 if (age < now)
667 goto drop_entry;
668 cp = np;
669 if ((np = strchr(cp, ':')) == NULL)
670 goto bad_entry;
671 }
672 /* "group-name:" or "/regexp:" or ":" for all groups */
673
674 flag = COMP_KILL_ENTRY;
675
676 if (np == cp) {
677 entry.ck_group = -1;
678 np++;
679 } else {
680 *np++ = NUL;
681 if (*cp == '/') {
682 entry.ck_group = (long) ftell(patternf);
683 cp++;
684 len = strlen(cp) + 1;
685 if (fwrite(cp, sizeof(char), len, patternf) != len)
686 goto err3;
687 flag |= GROUP_REGEXP | GROUP_REGEXP_HDR;
688 } else {
689 if ((gh = lookup(cp)) == NULL || (gh->master_flag & M_IGNORE_GROUP)) {
690 tprintf("Unknown/ignored group in kill file: %s\n", cp);
691 any_errors++;
692 goto drop_entry;
693 }
694 entry.ck_group = gh->group_num;
695 }
696 }
697
698 /* flags */
699
700 cp = np;
701 nfield = 0;
702
703 for (;;) {
704 switch (*cp++) {
705 case EXT_AND_MATCH:
706 case EXT_OR_MATCH:
707 fields[nfield++] = flag;
708 flag &= ~(AND_MATCH | ON_SUBJECT | ON_SENDER |
709 KILL_CASE_MATCH | KILL_ON_REGEXP |
710 GROUP_REGEXP_HDR);
711 flag |= (cp[-1] == EXT_AND_MATCH) ? AND_MATCH : OR_MATCH;
712 continue;
713 case EXT_AUTO_KILL:
714 flag |= AUTO_KILL;
715 continue;
716 case EXT_AUTO_SELECT:
717 flag |= AUTO_SELECT;
718 continue;
719 case EXT_ON_FOLLOW_UP:
720 flag |= ON_FOLLOW_UP;
721 continue;
722 case EXT_ON_NOT_FOLLOW_UP:
723 flag |= ON_NOT_FOLLOW_UP;
724 continue;
725 case EXT_ON_ANY_REFERENCES:
726 flag |= ON_ANY_REFERENCES;
727 continue;
728 case EXT_ON_SUBJECT:
729 flag |= ON_SUBJECT;
730 continue;
731 case EXT_ON_SENDER:
732 flag |= ON_SENDER;
733 continue;
734 case EXT_KILL_CASE_MATCH:
735 flag |= KILL_CASE_MATCH;
736 continue;
737 case EXT_KILL_UNLESS_MATCH:
738 flag |= KILL_UNLESS_MATCH;
739 continue;
740 case EXT_KILL_ON_REGEXP:
741 flag |= KILL_ON_REGEXP;
742 continue;
743 case ':':
744 break;
745 case NL:
746 goto bad_entry;
747 default:
748 tprintf("Ignored flag '%c' in kill file\n", cp[-1]);
749 any_errors++;
750 continue;
751 }
752 break;
753 }
754
755 fields[nfield++] = flag;
756
757 for (nf = 0; --nfield >= 0; nf++) {
758 entry.ck_flag = flag = fields[nf];
759 np = cp;
760 if ((cp = get_pattern(np, &len, nfield)) == NULL)
761 goto bad_entry;
762
763 if ((flag & KILL_CASE_MATCH) == 0)
764 fold_string(np);
765
766 entry.ck_pattern_index = ftell(patternf);
767
768 if (fwrite((char *) &entry, sizeof(entry), 1, compf) != 1)
769 goto err3;
770
771 if (fwrite(np, sizeof(char), len, patternf) != len)
772 goto err3;
773
774 header.ckh_entries++;
775 if (flag & GROUP_REGEXP)
776 header.ckh_regexp_size++;
777 }
778 }
779
780 header.ckh_pattern_size = ftell(patternf);
781
782 fclose(patternf);
783 patternf = open_file(temp_file, OPEN_READ | OPEN_UNLINK);
784 if (patternf == NULL)
785 goto err2;
786
787 header.ckh_pattern_offset = ftell(compf);
788
789 while ((c = getc(patternf)) != EOF)
790 putc(c, compf);
791
792 fclose(patternf);
793
794 rewind(compf);
795
796 header.ckh_magic = COMP_KILL_MAGIC;
797 header.ckh_db_check = master.db_created;
798
799 if (fwrite((char *) &header, sizeof(header), 1, compf) != 1)
800 goto err2;
801
802 fclose(compf);
803 fclose(killf);
804 if (dropf != NULL)
805 fclose(dropf);
806
807 if (any_errors) {
808 tputc(NL);
809 any_key(0);
810 }
811 return 1;
812
813 bad_entry:
814 tprintf("Incomplete kill file entry:\n%s", line);
815 fl;
816 any_errors++;
817
818 drop_entry:
819 if (dropf == NULL) {
820 dropf = open_file(relative(nn_directory, KILL_FILE),
821 OPEN_UPDATE | DONT_CREATE);
822 if (dropf == NULL)
823 goto next_entry;
824 }
825 fseek(dropf, cur_line_start, 0);
826 fwrite("# ", sizeof(char), 2, dropf);
827 goto next_entry;
828
829 err3:
830 fclose(patternf);
831 unlink(temp_file);
832 err2:
833 fclose(compf);
834 rm_kill_file();
835 err1:
836 fclose(killf);
837 if (dropf != NULL)
838 fclose(dropf);
839
840 msg("cannot compile kill file");
841 return 0;
842 }
843
844 int
init_kill(void)845 init_kill(void)
846 {
847 FILE *killf;
848 comp_kill_header header;
849 comp_kill_entry entry;
850 register kill_list_entry *kl;
851 register kill_group_regexp *tb;
852 register group_header *gh;
853 time_t kill_age, comp_age;
854 register long n;
855 int first_try = 1;
856
857 Loop_Groups_Header(gh)
858 gh->kill_list = NULL;
859
860 kill_age = file_exist(relative(nn_directory, KILL_FILE), "frw");
861 if (kill_age == 0)
862 return 0;
863
864 comp_age = file_exist(relative(nn_directory, COMPILED_KILL), "fr");
865 again:
866 if (comp_age < kill_age && !compile_kill_file())
867 return 0;
868
869 kill_tab = NULL;
870 kill_patterns = NULL;
871 group_regexp_table = NULL;
872 regexp_table_size = 0;
873
874 killf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_READ);
875 if (killf == NULL)
876 return 0;
877
878 if (fread((char *) &header, sizeof(header), 1, killf) != 1)
879 goto err;
880 /* MAGIC check: format changed or using different hardware */
881 if (header.ckh_magic != COMP_KILL_MAGIC)
882 goto err;
883
884 #ifndef NOV
885 /* DB check: if database is rebuilt, group numbers may change */
886 if (header.ckh_db_check != master.db_created)
887 goto err;
888 #else
889 /* ugly hack for NOV as there isn't a master to check */
890 if (first_try)
891 goto err;
892 #endif
893
894 if (header.ckh_entries == 0) {
895 fclose(killf);
896 kill_file_loaded = 1;
897 return 0;
898 }
899 if (header.ckh_pattern_size > 0) {
900 kill_patterns = newstr(header.ckh_pattern_size);
901 fseek(killf, header.ckh_entries * sizeof(entry), 1);
902 if (fread(kill_patterns, sizeof(char), (int) header.ckh_pattern_size, killf)
903 != header.ckh_pattern_size)
904 goto err;
905 } else
906 kill_patterns = newstr(1);
907
908 kill_tab = newobj(kill_list_entry, header.ckh_entries);
909 if ((regexp_table_size = header.ckh_regexp_size))
910 group_regexp_table = newobj(kill_group_regexp, header.ckh_regexp_size);
911
912 tb = group_regexp_table;
913
914 fseek(killf, sizeof(header), 0);
915 for (n = header.ckh_entries, kl = kill_tab; --n >= 0; kl++) {
916 if (fread((char *) &entry, sizeof(entry), 1, killf) != 1)
917 goto err;
918 if (header.ckh_pattern_size <= entry.ck_pattern_index ||
919 entry.ck_pattern_index < 0)
920 goto err;
921
922 kl->kill_pattern = kill_patterns + entry.ck_pattern_index;
923 kl->kill_flag = entry.ck_flag;
924
925 if (kl->kill_flag & KILL_ON_REGEXP)
926 kl->kill_regexp = regcomp(kl->kill_pattern);
927 else
928 kl->kill_regexp = NULL;
929
930 if (kl->kill_flag & GROUP_REGEXP) {
931 if (kl->kill_flag & GROUP_REGEXP_HDR) {
932 if (header.ckh_pattern_size <= entry.ck_group ||
933 entry.ck_group < 0)
934 goto err;
935 tb->group_regexp = regcomp(kill_patterns + entry.ck_group);
936 } else
937 tb->group_regexp = NULL;
938 tb->kill_entry = kl;
939 tb++;
940 } else if (entry.ck_group >= 0) {
941 gh = ACTIVE_GROUP(entry.ck_group);
942 kl->next_kill = (kill_list_entry *) (gh->kill_list);
943 gh->kill_list = (char *) kl;
944 } else {
945 kl->next_kill = global_kill_list;
946 global_kill_list = kl;
947 }
948 }
949
950 fclose(killf);
951
952 kill_file_loaded = 1;
953 return 1;
954
955 err:
956 if (group_regexp_table != NULL)
957 freeobj(group_regexp_table);
958 if (kill_patterns != NULL)
959 freeobj(kill_patterns);
960 if (kill_tab != NULL)
961 freeobj(kill_tab);
962
963 fclose(killf);
964 rm_kill_file();
965 if (first_try) {
966 first_try = 0;
967 comp_age = 0;
968 goto again;
969 }
970 strcpy(delayed_msg, "Error in compiled kill file (ignored)");
971
972 Loop_Groups_Header(gh)
973 gh->kill_list = NULL;
974
975 global_kill_list = NULL;
976 group_regexp_table = NULL;
977
978 return 0;
979 }
980
981
982
983 void
rm_kill_file(void)984 rm_kill_file(void)
985 {
986 unlink(relative(nn_directory, COMPILED_KILL));
987 }
988
989
990 static void
free_kill_list(register kill_list_entry * kl)991 free_kill_list(register kill_list_entry * kl)
992 {
993 register kill_list_entry *nxt;
994 while (kl) {
995 nxt = kl->next_kill;
996 if (kl->kill_regexp != NULL)
997 freeobj(kl->kill_regexp);
998 if ((kl->kill_flag & COMP_KILL_ENTRY) == 0) {
999 if (kl->kill_pattern != NULL)
1000 freeobj(kl->kill_pattern);
1001 freeobj(kl);
1002 }
1003 kl = nxt;
1004 }
1005 }
1006
1007 void
free_kill_entries(void)1008 free_kill_entries(void)
1009 {
1010 register group_header *gh;
1011 register kill_group_regexp *tb;
1012 register int n;
1013
1014 Loop_Groups_Header(gh)
1015 if (gh->kill_list) {
1016 free_kill_list((kill_list_entry *) (gh->kill_list));
1017 gh->kill_list = NULL;
1018 }
1019 free_kill_list(global_kill_list);
1020 global_kill_list = NULL;
1021
1022 if ((tb = group_regexp_table)) {
1023 for (n = regexp_table_size; --n >= 0; tb++)
1024 if (tb->group_regexp != NULL)
1025 freeobj(tb->group_regexp);
1026
1027 freeobj(group_regexp_table);
1028 group_regexp_table = NULL;
1029 }
1030 if (kill_patterns != NULL)
1031 freeobj(kill_patterns);
1032 if (kill_tab != NULL)
1033 freeobj(kill_tab);
1034 kill_file_loaded = 0;
1035 }
1036
1037
1038 static flag_type pk_prev_and;
1039
1040 static int
print_kill(register kill_list_entry * kl)1041 print_kill(register kill_list_entry * kl)
1042 {
1043 register flag_type flag = kl->kill_flag;
1044
1045 if (pg_next() < 0)
1046 return -1;
1047
1048 if (pk_prev_and)
1049 tprintf("\r AND ");
1050 else
1051 tprintf("\r%s%s ON ",
1052 flag & AUTO_KILL ? "AUTO KILL" :
1053 flag & AUTO_SELECT ? "AUTO SELECT" : "",
1054
1055 (flag & KILL_UNLESS_MATCH) == 0 ? "" :
1056 flag & AUTO_KILL ? " UNLESS" :
1057 flag & AUTO_SELECT ? "" : "KEEP");
1058
1059 tprintf("%s '%s%.35s'%s\n",
1060 flag & ON_SUBJECT ? "SUBJECT" :
1061 flag & ON_SENDER ? "NAME" :
1062 flag & ON_ANY_REFERENCES ? "ANY REF" : "????",
1063
1064 flag & ON_NOT_FOLLOW_UP ? "!Re: " :
1065 flag & ON_FOLLOW_UP ? "Re: " : "",
1066 kl->kill_pattern,
1067
1068 flag & KILL_CASE_MATCH ?
1069 (flag & KILL_ON_REGEXP ? " (re exact)" : " (exact)") :
1070 (flag & KILL_ON_REGEXP ? " (re fold)" : ""));
1071
1072 pk_prev_and = flag & AND_MATCH;
1073
1074 return 0;
1075 }
1076
1077 void
dump_kill_list(void)1078 dump_kill_list(void)
1079 {
1080 register kill_list_entry *kl;
1081
1082 pg_init(0, 1);
1083 pg_next();
1084
1085 kl = (kill_list_entry *) (current_group->kill_list);
1086 if (current_kill_group != current_group)
1087 build_regexp_kill();
1088
1089 if (kl == NULL && regexp_kill_list == NULL) {
1090 tprintf("No kill entries for %s", current_group->group_name);
1091 } else {
1092 so_printf("\1GROUP %s kill list entries\1", current_group->group_name);
1093
1094 pk_prev_and = 0;
1095 for (; kl; kl = kl->next_kill)
1096 if (print_kill(kl) < 0)
1097 goto out;
1098
1099 pk_prev_and = 0;
1100 for (kl = regexp_kill_list; kl; kl = kl->next_kill)
1101 if (print_kill(kl) < 0)
1102 goto out;
1103
1104 if (pg_next() < 0)
1105 goto out;
1106 }
1107
1108 if (pg_next() < 0)
1109 goto out;
1110 so_printf("\1GLOBAL kill list entries:\1");
1111
1112 pk_prev_and = 0;
1113 for (kl = global_kill_list; kl != NULL; kl = kl->next_kill)
1114 if (print_kill(kl) < 0)
1115 goto out;
1116
1117 out:
1118 pg_end();
1119 }
1120