Blame


1 d1f529f4 2005-10-29 devnull /*
2 d1f529f4 2005-10-29 devnull * RFC822 message tokenizer (really feature generator) for spam filter.
3 fa325e9b 2020-01-10 cross *
4 d1f529f4 2005-10-29 devnull * See Paul Graham's musings on spam filtering for theory.
5 d1f529f4 2005-10-29 devnull */
6 d1f529f4 2005-10-29 devnull
7 d1f529f4 2005-10-29 devnull #include <u.h>
8 d1f529f4 2005-10-29 devnull #include <libc.h>
9 d1f529f4 2005-10-29 devnull #include <bio.h>
10 d1f529f4 2005-10-29 devnull #include <regexp.h>
11 d1f529f4 2005-10-29 devnull #include <ctype.h>
12 d1f529f4 2005-10-29 devnull #include "dfa.h"
13 d1f529f4 2005-10-29 devnull
14 d1f529f4 2005-10-29 devnull void buildre(Dreprog*[3]);
15 d1f529f4 2005-10-29 devnull int debug;
16 b5f65921 2006-02-11 devnull char *refile = "#9/mail/lib/classify.re";
17 d1f529f4 2005-10-29 devnull int maxtoklen = 20;
18 d1f529f4 2005-10-29 devnull int trim(char*);
19 d1f529f4 2005-10-29 devnull
20 d1f529f4 2005-10-29 devnull void
21 d1f529f4 2005-10-29 devnull usage(void)
22 d1f529f4 2005-10-29 devnull {
23 d1f529f4 2005-10-29 devnull fprint(2, "usage: msgtok [-D] [-r /mail/lib/classify.re] [file]\n");
24 d1f529f4 2005-10-29 devnull exits("usage");
25 d1f529f4 2005-10-29 devnull }
26 d1f529f4 2005-10-29 devnull
27 d1f529f4 2005-10-29 devnull void
28 d1f529f4 2005-10-29 devnull main(int argc, char **argv)
29 d1f529f4 2005-10-29 devnull {
30 d1f529f4 2005-10-29 devnull int i, hdr, n, eof, off;
31 d1f529f4 2005-10-29 devnull Dreprog *re[3];
32 d1f529f4 2005-10-29 devnull int m[3];
33 d1f529f4 2005-10-29 devnull char *p, *ep, *tag;
34 d1f529f4 2005-10-29 devnull Biobuf bout, bin;
35 d1f529f4 2005-10-29 devnull char msg[1024+1];
36 d1f529f4 2005-10-29 devnull char buf[1024];
37 d1f529f4 2005-10-29 devnull
38 b5f65921 2006-02-11 devnull refile = unsharp(refile);
39 d1f529f4 2005-10-29 devnull buildre(re);
40 d1f529f4 2005-10-29 devnull ARGBEGIN{
41 d1f529f4 2005-10-29 devnull case 'D':
42 d1f529f4 2005-10-29 devnull debug = 1;
43 d1f529f4 2005-10-29 devnull break;
44 d1f529f4 2005-10-29 devnull case 'n':
45 d1f529f4 2005-10-29 devnull maxtoklen = atoi(EARGF(usage()));
46 d1f529f4 2005-10-29 devnull break;
47 d1f529f4 2005-10-29 devnull case 'r':
48 d1f529f4 2005-10-29 devnull refile = EARGF(usage());
49 d1f529f4 2005-10-29 devnull break;
50 d1f529f4 2005-10-29 devnull default:
51 d1f529f4 2005-10-29 devnull usage();
52 d1f529f4 2005-10-29 devnull }ARGEND;
53 d1f529f4 2005-10-29 devnull
54 d1f529f4 2005-10-29 devnull if(argc > 1)
55 d1f529f4 2005-10-29 devnull usage();
56 d1f529f4 2005-10-29 devnull if(argc == 1){
57 d1f529f4 2005-10-29 devnull close(0);
58 d1f529f4 2005-10-29 devnull if(open(argv[0], OREAD) < 0)
59 d1f529f4 2005-10-29 devnull sysfatal("open %s: %r", argv[0]);
60 d1f529f4 2005-10-29 devnull }
61 d1f529f4 2005-10-29 devnull
62 d1f529f4 2005-10-29 devnull tag = nil;
63 d1f529f4 2005-10-29 devnull Binit(&bin, 0, OREAD);
64 d1f529f4 2005-10-29 devnull Binit(&bout, 1, OWRITE);
65 d1f529f4 2005-10-29 devnull ep = msg;
66 d1f529f4 2005-10-29 devnull p = msg;
67 d1f529f4 2005-10-29 devnull eof = 0;
68 d1f529f4 2005-10-29 devnull off = 0;
69 d1f529f4 2005-10-29 devnull hdr = 1;
70 d1f529f4 2005-10-29 devnull for(;;){
71 d1f529f4 2005-10-29 devnull /* replenish buffer */
72 d1f529f4 2005-10-29 devnull if(ep - p < 512 && !eof){
73 d1f529f4 2005-10-29 devnull if(p > msg + 1){
74 d1f529f4 2005-10-29 devnull n = ep - p;
75 d1f529f4 2005-10-29 devnull memmove(msg, p-1, ep-(p-1));
76 d1f529f4 2005-10-29 devnull off += (p-1) - msg;
77 d1f529f4 2005-10-29 devnull p = msg+1;
78 d1f529f4 2005-10-29 devnull ep = p + n;
79 d1f529f4 2005-10-29 devnull }
80 d1f529f4 2005-10-29 devnull n = Bread(&bin, ep, msg+(sizeof msg - 1)- ep);
81 d1f529f4 2005-10-29 devnull if(n < 0)
82 d1f529f4 2005-10-29 devnull sysfatal("read error: %r");
83 d1f529f4 2005-10-29 devnull if(n == 0)
84 d1f529f4 2005-10-29 devnull eof = 1;
85 d1f529f4 2005-10-29 devnull ep += n;
86 d1f529f4 2005-10-29 devnull *ep = 0;
87 d1f529f4 2005-10-29 devnull }
88 d1f529f4 2005-10-29 devnull if(p >= ep)
89 d1f529f4 2005-10-29 devnull break;
90 d1f529f4 2005-10-29 devnull
91 d1f529f4 2005-10-29 devnull if(*p == 0){
92 d1f529f4 2005-10-29 devnull p++;
93 d1f529f4 2005-10-29 devnull continue;
94 d1f529f4 2005-10-29 devnull }
95 d1f529f4 2005-10-29 devnull
96 d1f529f4 2005-10-29 devnull if(hdr && p[-1]=='\n'){
97 d1f529f4 2005-10-29 devnull if(p[0]=='\n')
98 d1f529f4 2005-10-29 devnull hdr = 0;
99 d1f529f4 2005-10-29 devnull else if(cistrncmp(p-1, "\nfrom:", 6) == 0)
100 d1f529f4 2005-10-29 devnull tag = "From*";
101 d1f529f4 2005-10-29 devnull else if(cistrncmp(p-1, "\nto:", 4) == 0)
102 d1f529f4 2005-10-29 devnull tag = "To*";
103 d1f529f4 2005-10-29 devnull else if(cistrncmp(p-1, "\nsubject:", 9) == 0)
104 d1f529f4 2005-10-29 devnull tag = "Subject*";
105 d1f529f4 2005-10-29 devnull else if(cistrncmp(p-1, "\nreturn-path:", 13) == 0)
106 d1f529f4 2005-10-29 devnull tag = "Return-Path*";
107 d1f529f4 2005-10-29 devnull else
108 d1f529f4 2005-10-29 devnull tag = nil;
109 d1f529f4 2005-10-29 devnull }
110 d1f529f4 2005-10-29 devnull m[0] = dregexec(re[0], p, p==msg || p[-1]=='\n');
111 d1f529f4 2005-10-29 devnull m[1] = dregexec(re[1], p, p==msg || p[-1]=='\n');
112 d1f529f4 2005-10-29 devnull m[2] = dregexec(re[2], p, p==msg || p[-1]=='\n');
113 d1f529f4 2005-10-29 devnull
114 d1f529f4 2005-10-29 devnull n = m[0];
115 d1f529f4 2005-10-29 devnull if(n < m[1])
116 d1f529f4 2005-10-29 devnull n = m[1];
117 d1f529f4 2005-10-29 devnull if(n < m[2])
118 d1f529f4 2005-10-29 devnull n = m[2];
119 d1f529f4 2005-10-29 devnull if(n <= 0){
120 d1f529f4 2005-10-29 devnull fprint(2, "«%s» %.2ux", p, p[0]);
121 d1f529f4 2005-10-29 devnull sysfatal("no regexps matched at %ld", off + (p-msg));
122 d1f529f4 2005-10-29 devnull }
123 d1f529f4 2005-10-29 devnull
124 d1f529f4 2005-10-29 devnull if(m[0] >= m[1] && m[0] >= m[2]){
125 d1f529f4 2005-10-29 devnull /* "From " marks start of new message */
126 d1f529f4 2005-10-29 devnull Bprint(&bout, "*From*\n");
127 d1f529f4 2005-10-29 devnull n = m[0];
128 d1f529f4 2005-10-29 devnull hdr = 1;
129 d1f529f4 2005-10-29 devnull }else if(m[2] > 1){
130 d1f529f4 2005-10-29 devnull /* ignore */
131 d1f529f4 2005-10-29 devnull n = m[2];
132 d1f529f4 2005-10-29 devnull }else if(m[1] >= m[0] && m[1] >= m[2] && m[1] > 2 && m[1] <= maxtoklen){
133 d1f529f4 2005-10-29 devnull /* keyword */
134 d1f529f4 2005-10-29 devnull /* should do UTF-aware lowercasing, too much bother */
135 d1f529f4 2005-10-29 devnull /*
136 d1f529f4 2005-10-29 devnull for(i=0; i<n; i++)
137 d1f529f4 2005-10-29 devnull if('A' <= p[i] && p[i] <= 'Z')
138 d1f529f4 2005-10-29 devnull p[i] += 'a' - 'A';
139 d1f529f4 2005-10-29 devnull */
140 d1f529f4 2005-10-29 devnull if(tag){
141 fa325e9b 2020-01-10 cross i = strlen(tag);
142 d1f529f4 2005-10-29 devnull memmove(buf, tag, i);
143 d1f529f4 2005-10-29 devnull memmove(buf+i, p, m[1]);
144 d1f529f4 2005-10-29 devnull buf[i+m[1]] = 0;
145 d1f529f4 2005-10-29 devnull }else{
146 d1f529f4 2005-10-29 devnull memmove(buf, p, m[1]);
147 d1f529f4 2005-10-29 devnull buf[m[1]] = 0;
148 d1f529f4 2005-10-29 devnull }
149 d1f529f4 2005-10-29 devnull Bprint(&bout, "%s\n", buf);
150 d1f529f4 2005-10-29 devnull while(trim(buf) >= 0)
151 d1f529f4 2005-10-29 devnull Bprint(&bout, "stem*%s\n", buf);
152 d1f529f4 2005-10-29 devnull n = m[1];
153 d1f529f4 2005-10-29 devnull }else
154 d1f529f4 2005-10-29 devnull n = m[2];
155 d1f529f4 2005-10-29 devnull if(debug)
156 d1f529f4 2005-10-29 devnull fprint(2, "%.*s¦", utfnlen(p, n), p);
157 d1f529f4 2005-10-29 devnull p += n;
158 d1f529f4 2005-10-29 devnull }
159 d1f529f4 2005-10-29 devnull Bterm(&bout);
160 d1f529f4 2005-10-29 devnull exits(0);
161 d1f529f4 2005-10-29 devnull }
162 d1f529f4 2005-10-29 devnull
163 d1f529f4 2005-10-29 devnull void
164 d1f529f4 2005-10-29 devnull buildre(Dreprog *re[3])
165 d1f529f4 2005-10-29 devnull {
166 d1f529f4 2005-10-29 devnull Biobuf *b;
167 d1f529f4 2005-10-29 devnull
168 d1f529f4 2005-10-29 devnull if((b = Bopen(refile, OREAD)) == nil)
169 d1f529f4 2005-10-29 devnull sysfatal("open %s: %r", refile);
170 d1f529f4 2005-10-29 devnull
171 d1f529f4 2005-10-29 devnull re[0] = Breaddfa(b);
172 d1f529f4 2005-10-29 devnull re[1] = Breaddfa(b);
173 d1f529f4 2005-10-29 devnull re[2] = Breaddfa(b);
174 d1f529f4 2005-10-29 devnull
175 d1f529f4 2005-10-29 devnull if(re[0]==nil || re[1]==nil || re[2]==nil)
176 d1f529f4 2005-10-29 devnull sysfatal("Breaddfa: %r");
177 d1f529f4 2005-10-29 devnull Bterm(b);
178 d1f529f4 2005-10-29 devnull }
179 d1f529f4 2005-10-29 devnull
180 d1f529f4 2005-10-29 devnull /* perhaps this belongs in the tokenizer */
181 d1f529f4 2005-10-29 devnull int
182 d1f529f4 2005-10-29 devnull trim(char *s)
183 d1f529f4 2005-10-29 devnull {
184 d1f529f4 2005-10-29 devnull char *p, *op;
185 d1f529f4 2005-10-29 devnull int mix, mix1;
186 d1f529f4 2005-10-29 devnull
187 d1f529f4 2005-10-29 devnull if(*s == '*')
188 d1f529f4 2005-10-29 devnull return -1;
189 d1f529f4 2005-10-29 devnull
190 d1f529f4 2005-10-29 devnull /* strip leading punctuation */
191 d1f529f4 2005-10-29 devnull p = strchr(s, '*');
192 d1f529f4 2005-10-29 devnull if(p == nil)
193 d1f529f4 2005-10-29 devnull p = s;
194 d1f529f4 2005-10-29 devnull while(*p && !isalpha(*p))
195 d1f529f4 2005-10-29 devnull p++;
196 d1f529f4 2005-10-29 devnull if(strlen(p) < 2)
197 d1f529f4 2005-10-29 devnull {
198 d1f529f4 2005-10-29 devnull return -1;
199 d1f529f4 2005-10-29 devnull }
200 d1f529f4 2005-10-29 devnull memmove(s, p, strlen(p)+1);
201 d1f529f4 2005-10-29 devnull
202 d1f529f4 2005-10-29 devnull /* strip suffix of punctuation */
203 d1f529f4 2005-10-29 devnull p = s+strlen(s);
204 d1f529f4 2005-10-29 devnull op = p;
205 d1f529f4 2005-10-29 devnull while(p > s && (uchar)p[-1]<0x80 && !isalpha(p[-1]))
206 d1f529f4 2005-10-29 devnull p--;
207 d1f529f4 2005-10-29 devnull
208 d1f529f4 2005-10-29 devnull /* chop punctuation */
209 d1f529f4 2005-10-29 devnull if(p > s){
210 d1f529f4 2005-10-29 devnull /* free!!! -> free! */
211 d1f529f4 2005-10-29 devnull if(p+1 < op){
212 d1f529f4 2005-10-29 devnull p[1] = 0;
213 d1f529f4 2005-10-29 devnull return 0;
214 d1f529f4 2005-10-29 devnull }
215 d1f529f4 2005-10-29 devnull /* free! -> free */
216 d1f529f4 2005-10-29 devnull if(p < op){
217 d1f529f4 2005-10-29 devnull p[0] = 0;
218 d1f529f4 2005-10-29 devnull return 0;
219 d1f529f4 2005-10-29 devnull }
220 d1f529f4 2005-10-29 devnull }
221 d1f529f4 2005-10-29 devnull
222 d1f529f4 2005-10-29 devnull mix = mix1 = 0;
223 d1f529f4 2005-10-29 devnull if(isupper(s[0]))
224 d1f529f4 2005-10-29 devnull mix = 1;
225 d1f529f4 2005-10-29 devnull for(p=s+1; *p; p++)
226 d1f529f4 2005-10-29 devnull if(isupper(*p)){
227 d1f529f4 2005-10-29 devnull mix1 = 1;
228 d1f529f4 2005-10-29 devnull break;
229 d1f529f4 2005-10-29 devnull }
230 d1f529f4 2005-10-29 devnull
231 d1f529f4 2005-10-29 devnull /* turn FREE into Free */
232 d1f529f4 2005-10-29 devnull if(mix1){
233 d1f529f4 2005-10-29 devnull for(p=s+1; *p; p++)
234 d1f529f4 2005-10-29 devnull if(isupper(*p))
235 d1f529f4 2005-10-29 devnull *p += 'a'-'A';
236 d1f529f4 2005-10-29 devnull return 0;
237 d1f529f4 2005-10-29 devnull }
238 d1f529f4 2005-10-29 devnull
239 d1f529f4 2005-10-29 devnull /* turn Free into free */
240 d1f529f4 2005-10-29 devnull if(mix){
241 d1f529f4 2005-10-29 devnull *s += 'a'-'A';
242 d1f529f4 2005-10-29 devnull return 0;
243 d1f529f4 2005-10-29 devnull }
244 d1f529f4 2005-10-29 devnull return -1;
245 fa325e9b 2020-01-10 cross }