2 d1f529f4 2005-10-29 devnull * RFC822 message tokenizer (really feature generator) for spam filter.
4 d1f529f4 2005-10-29 devnull * See Paul Graham's musings on spam filtering for theory.
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"
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*);
21 d1f529f4 2005-10-29 devnull usage(void)
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");
28 d1f529f4 2005-10-29 devnull main(int argc, char **argv)
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];
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;
44 d1f529f4 2005-10-29 devnull case 'n':
45 d1f529f4 2005-10-29 devnull maxtoklen = atoi(EARGF(usage()));
47 d1f529f4 2005-10-29 devnull case 'r':
48 d1f529f4 2005-10-29 devnull refile = EARGF(usage());
54 d1f529f4 2005-10-29 devnull if(argc > 1)
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]);
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;
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;
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)
88 d1f529f4 2005-10-29 devnull if(p >= ep)
91 d1f529f4 2005-10-29 devnull if(*p == 0){
93 d1f529f4 2005-10-29 devnull continue;
96 d1f529f4 2005-10-29 devnull if(hdr && p[-1]=='\n'){
97 d1f529f4 2005-10-29 devnull if(p[0]=='\n')
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*";
108 d1f529f4 2005-10-29 devnull tag = nil;
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');
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));
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 */
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';
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;
146 d1f529f4 2005-10-29 devnull memmove(buf, p, m[1]);
147 d1f529f4 2005-10-29 devnull buf[m[1]] = 0;
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];
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);
159 d1f529f4 2005-10-29 devnull Bterm(&bout);
160 d1f529f4 2005-10-29 devnull exits(0);
164 d1f529f4 2005-10-29 devnull buildre(Dreprog *re[3])
166 d1f529f4 2005-10-29 devnull Biobuf *b;
168 d1f529f4 2005-10-29 devnull if((b = Bopen(refile, OREAD)) == nil)
169 d1f529f4 2005-10-29 devnull sysfatal("open %s: %r", refile);
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);
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);
180 d1f529f4 2005-10-29 devnull /* perhaps this belongs in the tokenizer */
182 d1f529f4 2005-10-29 devnull trim(char *s)
184 d1f529f4 2005-10-29 devnull char *p, *op;
185 d1f529f4 2005-10-29 devnull int mix, mix1;
187 d1f529f4 2005-10-29 devnull if(*s == '*')
188 d1f529f4 2005-10-29 devnull return -1;
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)
194 d1f529f4 2005-10-29 devnull while(*p && !isalpha(*p))
196 d1f529f4 2005-10-29 devnull if(strlen(p) < 2)
198 d1f529f4 2005-10-29 devnull return -1;
200 d1f529f4 2005-10-29 devnull memmove(s, p, strlen(p)+1);
202 d1f529f4 2005-10-29 devnull /* strip suffix of punctuation */
203 d1f529f4 2005-10-29 devnull p = s+strlen(s);
205 d1f529f4 2005-10-29 devnull while(p > s && (uchar)p[-1]<0x80 && !isalpha(p[-1]))
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;
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;
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;
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;
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;
244 d1f529f4 2005-10-29 devnull return -1;