1 /* register.c -- read input with collect and register to persistent db */
2 
3 #include "common.h"
4 
5 #include <stdlib.h>
6 
7 #include "bogofilter.h"
8 #include "datastore.h"
9 #include "collect.h"
10 #include "format.h"
11 #include "msgcounts.h"
12 #include "rand_sleep.h"
13 #include "register.h"
14 #include "wordhash.h"
15 #include "wordlists.h"
16 
17 #define PLURAL(count) ((count == 1) ? "" : "s")
18 
19 /*
20  * tokenize text on stdin and register it to a specified list
21  * and possibly out of another list
22  */
register_words(run_t _run_type,wordhash_t * h,u_int32_t msgcount)23 void register_words(run_t _run_type, wordhash_t *h, u_int32_t msgcount)
24 {
25     const char *r="",*u="";
26     dsv_t val;
27     hashnode_t *node;
28     wordprop_t *wordprop;
29     run_t save_run_type = run_type;
30     int retrycount = 60;		/* we'll retry an aborted
31 					   registration five dozen times
32 					   before giving up. */
33     bool first;
34 
35     u_int32_t wordcount = h->count;	/* use number of unique tokens */
36 
37     /* registrations always go to the default wordlist */
38     wordlist_t *list = get_default_wordlist(word_lists);
39 
40     sh_t incr = IX_UNDF, decr = IX_UNDF;
41 
42     /* If update directory explicity supplied, setup the wordlists. */
43     if (update_dir) {
44 	if (set_wordlist_dir(update_dir, PR_CFG_UPDATE) != 0) {
45 	    fprintf(stderr, "Can't find HOME or BOGOFILTER_DIR in environment.\n");
46 	    exit(EX_ERROR);
47 	}
48     }
49 
50     if (_run_type & REG_SPAM)	{ r = "s"; incr = IX_SPAM; }
51     if (_run_type & REG_GOOD)	{ r = "n"; incr = IX_GOOD; }
52     if (_run_type & UNREG_SPAM)	{ u = "S"; decr = IX_SPAM; }
53     if (_run_type & UNREG_GOOD)	{ u = "N"; decr = IX_GOOD; }
54 
55     if (wordcount == 0)
56 	msgcount = 0;
57 
58     format_set_counts(wordcount, msgcount);
59     format_log_update(msg_register, msg_register_size, u, r);
60 
61     if (verbose)
62 	(void)fprintf(dbgout, "# %u word%s, %u message%s\n",
63 		      wordcount, PLURAL(wordcount), msgcount, PLURAL(msgcount));
64 
65     /* When using auto-update with separate wordlists ,
66        datastore.c needs to know which to update */
67 
68     run_type = (run_t)(run_type | _run_type);
69 
70     first = true;
71 
72 retry:
73     if (first)
74 	first = false;
75     else {
76 	if (verbose)
77 	    fprintf(stderr, "retrying registration after avoided deadlock...\n");
78 	begin_wordlist(list);
79     }
80 
81     if (retrycount-- == 0) {
82 	fprintf(stderr, "retry count exceeded, giving up.\n");
83 	exit(EX_ERROR);
84     }
85 
86     for (node = (hashnode_t *)wordhash_first(h); node != NULL; node = (hashnode_t *)wordhash_next(h))
87     {
88 	wordprop = (wordprop_t *)node->data;
89 	switch (ds_read(list->dsh, node->key, &val)) {
90 	    case DS_ABORT_RETRY:
91 		rand_sleep(4*1000,1000*1000);
92 		goto retry;
93 	    case 0:
94 	    case 1:
95 		break;
96 	    default:
97 		fprintf(stderr, "cannot read from data base.\n");
98 		exit(EX_ERROR);
99 	}
100 	if (incr != IX_UNDF) {
101 	    u_int32_t *counts = val.count;
102 	    counts[incr] += wordprop->freq;
103 	}
104 	if (decr != IX_UNDF) {
105 	    u_int32_t *counts = val.count;
106 	    counts[decr] = ((long)counts[decr] < wordprop->freq) ? 0 : counts[decr] - wordprop->freq;
107 	}
108 	switch (ds_write(list->dsh, node->key, &val)) {
109 	    case DS_ABORT_RETRY:
110 		rand_sleep(4*1000,1000*1000);
111 		goto retry;
112 	    case 0:
113 		break;
114 	    default:
115 		fprintf(stderr, "cannot write to data base.\n");
116 		exit(EX_ERROR);
117 	}
118     }
119 
120     switch (ds_get_msgcounts(list->dsh, &val)) {
121 	case 0:
122 	case 1:
123 	    break;
124 	case DS_ABORT_RETRY:
125 	    rand_sleep(4 * 1000, 1000 * 1000);
126 	    goto retry;
127 	default:
128 	    fprintf(stderr, "cannot get message count values.\n");
129 	    exit(EX_ERROR);
130     }
131     list->msgcount[IX_SPAM] = val.spamcount;
132     list->msgcount[IX_GOOD] = val.goodcount;
133 
134     if (incr != IX_UNDF)
135 	list->msgcount[incr] += msgcount;
136 
137     if (decr != IX_UNDF) {
138 	if (list->msgcount[decr] > msgcount)
139 	    list->msgcount[decr] -= msgcount;
140 	else
141 	    list->msgcount[decr] = 0;
142     }
143 
144     val.spamcount = list->msgcount[IX_SPAM];
145     val.goodcount = list->msgcount[IX_GOOD];
146 
147     switch (ds_set_msgcounts(list->dsh, &val)) {
148 	case 0:
149 	    break;
150 	case DS_ABORT_RETRY:
151 	    rand_sleep(4 * 1000, 1000 * 1000);
152 	    goto retry;
153 	default:
154 	    fprintf(stderr, "cannot set message count values\n");
155 	    exit(EX_ERROR);
156     }
157 
158     if (DEBUG_REGISTER(1))
159 	(void)fprintf(dbgout, "bogofilter: list %s (%s) - %ul spam, %ul good\n",
160 		      list->listname, list->bfp->filepath, val.spamcount, val.goodcount);
161 
162     run_type = save_run_type;
163 }
164