1 #include "common.h" 2 #include "spam.h" 3 4 int cflag; 5 int debug; 6 int hflag; 7 int nflag; 8 int sflag; 9 int tflag; 10 int vflag; 11 Biobuf bin, bout, *cout; 12 13 /* file names */ 14 char patfile[128]; 15 char linefile[128]; 16 char holdqueue[128]; 17 char copydir[128]; 18 19 char header[Hdrsize+2]; 20 char cmd[1024]; 21 char **qname; 22 char **qdir; 23 char *sender; 24 String *recips; 25 26 char* canon(Biobuf*, char*, char*, int*); 27 int matcher(char*, Pattern*, char*, Resub*); 28 int matchaction(int, char*, Resub*); 29 Biobuf *opencopy(char*); 30 Biobuf *opendump(char*); 31 char *qmail(char**, char*, int, Biobuf*); 32 void saveline(char*, char*, Resub*); 33 34 void 35 usage(void) 36 { 37 fprint(2, "missing or bad arguments to qer\n"); 38 exits("usage"); 39 } 40 41 void * 42 Malloc(long n) 43 { 44 void *p; 45 46 p = malloc(n); 47 if(p == 0) 48 exits("malloc"); 49 return p; 50 } 51 52 void* 53 Realloc(void *p, ulong n) 54 { 55 p = realloc(p, n); 56 if(p == 0) 57 exits("realloc"); 58 return p; 59 } 60 61 void 62 main(int argc, char *argv[]) 63 { 64 int i, n, nolines; 65 char **args, **a, *cp, *buf; 66 char body[Bodysize+2]; 67 Resub match[1]; 68 Biobuf *bp; 69 70 a = args = Malloc((argc+1)*sizeof(char*)); 71 sprint(patfile, "%s/patterns", UPASLIB); 72 sprint(linefile, "%s/lines", UPASLOG); 73 sprint(holdqueue, "%s/queue.hold", SPOOL); 74 sprint(copydir, "%s/copy", SPOOL); 75 76 *a++ = argv[0]; 77 for(argc--, argv++; argv[0] && argv[0][0] == '-'; argc--, argv++){ 78 switch(argv[0][1]){ 79 case 'c': /* save copy of message */ 80 cflag = 1; 81 break; 82 case 'd': /* debug */ 83 debug++; 84 *a++ = argv[0]; 85 break; 86 case 'h': /* queue held messages by sender domain */ 87 hflag = 1; /* -q flag must be set also */ 88 break; 89 case 'n': /* NOHOLD mode */ 90 nflag = 1; 91 break; 92 case 'p': /* pattern file */ 93 if(argv[0][2] || argv[1] == 0) 94 usage(); 95 argc--; 96 argv++; 97 strcpy(patfile, *argv); 98 break; 99 case 'q': /* queue name */ 100 if(argv[0][2] || argv[1] == 0) 101 usage(); 102 *a++ = argv[0]; 103 argc--; 104 argv++; 105 qname = a; 106 *a++ = argv[0]; 107 break; 108 case 's': /* save copy of dumped message */ 109 sflag = 1; 110 break; 111 case 't': /* test mode - don't log match 112 * and write message to /dev/null 113 */ 114 tflag = 1; 115 break; 116 case 'v': /* vebose - print matches */ 117 vflag = 1; 118 break; 119 default: 120 *a++ = argv[0]; 121 break; 122 } 123 } 124 125 if(argc < 3) 126 usage(); 127 128 Binit(&bin, 0, OREAD); 129 bp = Bopen(patfile, OREAD); 130 if(bp){ 131 parsepats(bp); 132 Bterm(bp); 133 } 134 qdir = a; 135 sender = argv[2]; 136 137 /* copy the rest of argv, acummulating the recipients as we go */ 138 for(i = 0; argv[i]; i++){ 139 *a++ = argv[i]; 140 if(i < 4) /* skip queue, 'mail', sender, dest sys */ 141 continue; 142 /* recipients and smtp flags - skip the latter*/ 143 if(strcmp(argv[i], "-g") == 0){ 144 *a++ = argv[++i]; 145 continue; 146 } 147 if(recips) 148 s_append(recips, ", "); 149 else 150 recips = s_new(); 151 s_append(recips, argv[i]); 152 } 153 *a = 0; 154 /* construct a command string for matching */ 155 snprint(cmd, sizeof(cmd)-1, "%s %s", sender, s_to_c(recips)); 156 cmd[sizeof(cmd)-1] = 0; 157 for(cp = cmd; *cp; cp++) 158 *cp = tolower(*cp); 159 160 /* canonicalize a copy of the header and body. 161 * buf points to orginal message and n contains 162 * number of bytes of original message read during 163 * canonicalization. 164 */ 165 *body = 0; 166 *header = 0; 167 buf = canon(&bin, header+1, body+1, &n); 168 if (buf == 0) 169 exits("read"); 170 171 /* Turn off line logging, if command line matches */ 172 nolines = matchaction(Lineoff, cmd, match); 173 174 for(i = 0; patterns[i].action; i++){ 175 /* Lineoff patterns were already done above */ 176 if(i == Lineoff) 177 continue; 178 /* don't apply "Line" patterns if excluded above */ 179 if(nolines && i == SaveLine) 180 continue; 181 /* apply patterns to the sender/recips, header and body */ 182 if(matchaction(i, cmd, match)) 183 break; 184 if(matchaction(i, header+1, match)) 185 break; 186 if(i == HoldHeader) 187 continue; 188 if(matchaction(i, body+1, match)) 189 break; 190 } 191 if(cflag && patterns[i].action == 0) /* no match found - save msg */ 192 cout = opencopy(sender); 193 194 exits(qmail(args, buf, n, cout)); 195 } 196 197 char* 198 qmail(char **argv, char *buf, int n, Biobuf *cout) 199 { 200 Waitmsg *status; 201 int i, pid, pipefd[2]; 202 char path[512]; 203 Biobuf *bp; 204 205 pid = 0; 206 if(tflag == 0){ 207 if(pipe(pipefd) < 0) 208 exits("pipe"); 209 pid = fork(); 210 if(pid == 0){ 211 dup(pipefd[0], 0); 212 for(i = sysfiles(); i >= 3; i--) 213 close(i); 214 snprint(path, sizeof(path), "%s/qer", UPASBIN); 215 *argv=path; 216 exec(path, argv); 217 exits("exec"); 218 } 219 Binit(&bout, pipefd[1], OWRITE); 220 bp = &bout; 221 } else 222 bp = Bopen("/dev/null", OWRITE); 223 224 while(n > 0){ 225 Bwrite(bp, buf, n); 226 if(cout) 227 Bwrite(cout, buf, n); 228 n = Bread(&bin, buf, sizeof(buf)-1); 229 } 230 Bterm(bp); 231 if(cout) 232 Bterm(cout); 233 if(tflag) 234 return 0; 235 236 close(pipefd[1]); 237 close(pipefd[0]); 238 for(;;){ 239 status = wait(); 240 if(status == nil || status->pid == pid) 241 break; 242 free(status); 243 } 244 if(status == nil) 245 strcpy(buf, "wait failed"); 246 else{ 247 strcpy(buf, status->msg); 248 free(status); 249 } 250 return buf; 251 } 252 253 char* 254 canon(Biobuf *bp, char *header, char *body, int *n) 255 { 256 int hsize; 257 char *raw; 258 259 hsize = 0; 260 *header = 0; 261 *body = 0; 262 raw = readmsg(bp, &hsize, n); 263 if(raw){ 264 if(convert(raw, raw+hsize, header, Hdrsize, 0)) 265 conv64(raw+hsize, raw+*n, body, Bodysize); /* base64 */ 266 else 267 convert(raw+hsize, raw+*n, body, Bodysize, 1); /* text */ 268 } 269 return raw; 270 } 271 272 int 273 matchaction(int action, char *message, Resub *m) 274 { 275 char *name; 276 Pattern *p; 277 278 if(message == 0 || *message == 0) 279 return 0; 280 281 name = patterns[action].action; 282 p = patterns[action].strings; 283 if(p) 284 if(matcher(name, p, message, m)) 285 return 1; 286 287 for(p = patterns[action].regexps; p; p = p->next) 288 if(matcher(name, p, message, m)) 289 return 1; 290 return 0; 291 } 292 293 int 294 matcher(char *action, Pattern *p, char *message, Resub *m) 295 { 296 char *cp; 297 String *s; 298 299 for(cp = message; matchpat(p, cp, m); cp = m->ep){ 300 switch(p->action){ 301 case SaveLine: 302 if(vflag) 303 xprint(2, action, m); 304 saveline(linefile, sender, m); 305 break; 306 case HoldHeader: 307 case Hold: 308 if(nflag) 309 continue; 310 if(vflag) 311 xprint(2, action, m); 312 *qdir = holdqueue; 313 if(hflag && qname){ 314 cp = strchr(sender, '!'); 315 if(cp){ 316 *cp = 0; 317 *qname = strdup(sender); 318 *cp = '!'; 319 } else 320 *qname = strdup(sender); 321 } 322 return 1; 323 case Dump: 324 if(vflag) 325 xprint(2, action, m); 326 *(m->ep) = 0; 327 if(!tflag){ 328 s = s_new(); 329 s_append(s, sender); 330 s = unescapespecial(s); 331 syslog(0, "smtpd", "Dumped %s [%s] to %s", s_to_c(s), m->sp, 332 s_to_c(s_restart(recips))); 333 s_free(s); 334 } 335 tflag = 1; 336 if(sflag) 337 cout = opendump(sender); 338 return 1; 339 default: 340 break; 341 } 342 } 343 return 0; 344 } 345 346 void 347 saveline(char *file, char *sender, Resub *rp) 348 { 349 char *p, *q; 350 int i, c; 351 Biobuf *bp; 352 353 if(rp->sp == 0 || rp->ep == 0) 354 return; 355 /* back up approx 20 characters to whitespace */ 356 for(p = rp->sp, i = 0; *p && i < 20; i++, p--) 357 ; 358 while(*p && *p != ' ') 359 p--; 360 p++; 361 362 /* grab about 20 more chars beyond the end of the match */ 363 for(q = rp->ep, i = 0; *q && i < 20; i++, q++) 364 ; 365 while(*q && *q != ' ') 366 q++; 367 368 c = *q; 369 *q = 0; 370 bp = sysopen(file, "al", 0644); 371 if(bp){ 372 Bprint(bp, "%s-> %s\n", sender, p); 373 Bterm(bp); 374 } 375 else if(debug) 376 fprint(2, "can't save line: (%s) %s\n", sender, p); 377 *q = c; 378 } 379 380 Biobuf* 381 opendump(char *sender) 382 { 383 int i; 384 ulong h; 385 char buf[512]; 386 Biobuf *b; 387 char *cp; 388 389 cp = ctime(time(0)); 390 cp[7] = 0; 391 cp[10] = 0; 392 if(cp[8] == ' ') 393 sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]); 394 else 395 sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]); 396 cp = buf+strlen(buf); 397 if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0){ 398 syslog(0, "smtpd", "couldn't dump mail from %s: %r", sender); 399 return 0; 400 } 401 402 h = 0; 403 while(*sender) 404 h = h*257 + *sender++; 405 for(i = 0; i < 50; i++){ 406 h += lrand(); 407 sprint(cp, "/%lud", h); 408 b = sysopen(buf, "wlc", 0644); 409 if(b){ 410 if(vflag) 411 fprint(2, "saving in %s\n", buf); 412 return b; 413 } 414 } 415 return 0; 416 } 417 418 Biobuf* 419 opencopy(char *sender) 420 { 421 int i; 422 ulong h; 423 char buf[512]; 424 Biobuf *b; 425 426 h = 0; 427 while(*sender) 428 h = h*257 + *sender++; 429 for(i = 0; i < 50; i++){ 430 h += lrand(); 431 sprint(buf, "%s/%lud", copydir, h); 432 b = sysopen(buf, "wlc", 0600); 433 if(b) 434 return b; 435 } 436 return 0; 437 } 438