Import ezmlm-idx 0.40
[ezmlm] / ezmlm-send.c
1 /* $Id: ezmlm-send.c,v 1.77 1999/10/29 02:49:14 lindberg Exp $*/
2 /* $Name: ezmlm-idx-040 $*/
3 #include "stralloc.h"
4 #include "subfd.h"
5 #include "strerr.h"
6 #include "error.h"
7 #include "qmail.h"
8 #include "env.h"
9 #include "lock.h"
10 #include "sig.h"
11 #include "open.h"
12 #include "getln.h"
13 #include "case.h"
14 #include "scan.h"
15 #include "str.h"
16 #include "fmt.h"
17 #include "readwrite.h"
18 #include "exit.h"
19 #include "substdio.h"
20 #include "getconf.h"
21 #include "constmap.h"
22 #include "byte.h"
23 #include "sgetopt.h"
24 #include "quote.h"
25 #include "subscribe.h"
26 #include "mime.h"
27 #include "errtxt.h"
28 #include "makehash.h"
29 #include "cookie.h"
30 #include "idx.h"
31 #include "copy.h"
32
33 int flagnoreceived = 1; /* suppress received headers by default. They*/
34 /* are still archived. =0 => archived and */
35 /* copied. */
36 int flaglog = 1; /* for lists with mysql support, use tags */
37 /* and log traffic to the database */
38 #define FATAL "ezmlm-send: fatal: "
39
40 void die_usage()
41 {
42 strerr_die1x(100,"ezmlm-send: usage: ezmlm-send [-cClLqQrR] [-h header] dir");
43 }
44 void die_nomem()
45 {
46 strerr_die2x(111,FATAL,ERR_NOMEM);
47 }
48
49 /* for writing new index file indexn later moved to index. */
50 substdio ssindexn;
51 char indexnbuf[1024];
52
53 char strnum[FMT_ULONG];
54 char szmsgnum[FMT_ULONG];
55 char hash[HASHLEN];
56
57 stralloc fnadir = {0};
58 stralloc fnaf = {0};
59 stralloc fnif = {0};
60 stralloc fnifn = {0};
61 stralloc fnsub = {0};
62 stralloc line = {0};
63 stralloc qline = {0};
64 stralloc lines = {0};
65 stralloc subject = {0};
66 stralloc from = {0};
67 stralloc received = {0};
68 stralloc prefix = {0};
69 stralloc content = {0};
70 stralloc boundary = {0};
71 stralloc charset = {0};
72 stralloc dcprefix = {0};
73 stralloc dummy = {0};
74 stralloc qmqpservers = {0};
75
76 void die_indexn()
77 {
78 strerr_die4x(111,FATAL,ERR_WRITE,fnifn.s,": ");
79 }
80
81 void *psql = (void *) 0;
82
83 unsigned long innum;
84 unsigned long outnum;
85 unsigned long msgnum;
86 unsigned long hash_lo = 0L;
87 unsigned long hash_hi = 52L;
88 unsigned long msgsize = 0L;
89 unsigned long cumsize = 0L; /* cumulative archive size bytes / 256 */
90 char flagcd = '\0'; /* no transfer-encoding for trailer */
91 char encin = '\0';
92 int flagindexed;
93 int flagfoundokpart; /* Found something to pass on. If multipart */
94 /* we set to 0 and then set to 1 for any */
95 /* acceptable mime part. If 0 -> reject */
96 int flagreceived;
97 int flagprefixed;
98 unsigned int serial = 0;
99 int flagarchived;
100 int fdarchive;
101 int fdindex;
102 int fdindexn;
103 char hashout[COOKIE+1];
104
105 substdio ssarchive;
106 char archivebuf[1024];
107
108 int flagsublist;
109 stralloc sublist = {0};
110 stralloc mailinglist = {0};
111 stralloc outlocal = {0};
112 stralloc outhost = {0};
113 stralloc headerremove = {0};
114 struct constmap headerremovemap;
115 stralloc mimeremove = {0};
116 struct constmap mimeremovemap;
117 char *dir;
118
119 struct qmail qq;
120 substdio ssin;
121 char inbuf[1024];
122 substdio ssout;
123 char outbuf[1];
124
125 char textbuf[512];
126 substdio sstext;
127
128 unsigned int mywrite(fd,buf,len)
129 int fd;
130 char *buf;
131 unsigned int len;
132 {
133 qmail_put(&qq,buf,len);
134 return len;
135 }
136
137 int subto(s,l)
138 char *s;
139 unsigned int l;
140 {
141 qmail_put(&qq,"T",1);
142 qmail_put(&qq,s,l);
143 qmail_put(&qq,"",1);
144 return (int) l;
145 }
146
147 void die_archive()
148 {
149 strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": ");
150 }
151 void die_numnew()
152 {
153 strerr_die3sys(111,FATAL,ERR_CREATE,"numnew: ");
154 }
155
156 void qa_put(buf,len) char *buf; unsigned int len;
157 {
158 qmail_put(&qq,buf,len);
159 if (flagarchived)
160 if (substdio_put(&ssarchive,buf,len) == -1) die_archive();
161 }
162
163 void qa_puts(buf) char *buf;
164 {
165 qmail_puts(&qq,buf);
166 if (flagarchived)
167 if (substdio_puts(&ssarchive,buf) == -1) die_archive();
168 }
169
170 int sublistmatch(sender)
171 char *sender;
172 {
173 unsigned int i;
174 unsigned int j;
175
176 j = str_len(sender);
177 if (j < sublist.len) return 0;
178
179 i = byte_rchr(sublist.s,sublist.len,'@');
180 if (i == sublist.len) return 1;
181
182 if (byte_diff(sublist.s,i,sender)) return 0;
183 if (case_diffb(sublist.s + i,sublist.len - i,sender + j - (sublist.len - i)))
184 return 0;
185
186 return 1;
187 }
188
189 substdio ssnumnew;
190 char numnewbuf[16];
191
192 char buf0[256];
193 substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
194
195 void numwrite()
196 { /* this one deals with msgnum, not outnum! */
197 int fd;
198
199 fd = open_trunc("numnew");
200 if (fd == -1) die_numnew();
201 substdio_fdbuf(&ssnumnew,write,fd,numnewbuf,sizeof(numnewbuf));
202 if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,msgnum)) == -1)
203 die_numnew();
204 if (substdio_puts(&ssnumnew,":") == -1) die_numnew();
205 if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,cumsize)) == -1)
206 die_numnew();
207
208 if (substdio_puts(&ssnumnew,"\n") == -1) die_numnew();
209 if (substdio_flush(&ssnumnew) == -1) die_numnew();
210 if (fsync(fd) == -1) die_numnew();
211 if (close(fd) == -1) die_numnew(); /* NFS stupidity */
212 if (rename("numnew","num") == -1)
213 strerr_die3sys(111,FATAL,ERR_MOVE,"numnew: ");
214 }
215
216 stralloc mydtline = {0};
217
218 int idx_copy_insertsubject()
219 /* copies old index file up to but not including msg, then adds a line with */
220 /* 'sub' trimmed of reply indicators, then closes the new index and moves it*/
221 /* to the name 'index'. Errors are dealt with directly, and if the routine */
222 /* returns, it was successful. 'fatal' points to a program-specific error */
223 /* string. Sub is not destroyed, but from is!!! */
224 /* returns 1 if reply-indicators were found, 0 otherwise. */
225 /* no terminal \n or \0 in any of the strallocs! */
226 {
227 char *cp;
228 unsigned long idx;
229 int match;
230 int r;
231 unsigned int pos;
232
233 if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
234 if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,outnum / 100)))
235 die_nomem();
236 if (!stralloc_copy(&fnif,&fnadir)) die_nomem();
237 if (!stralloc_copy(&fnifn,&fnif)) die_nomem();
238 if (!stralloc_cats(&fnif,"/index")) die_nomem();
239 if (!stralloc_cats(&fnifn,"/indexn")) die_nomem();
240 if (!stralloc_0(&fnif)) die_nomem();
241 if (!stralloc_0(&fnifn)) die_nomem();
242 if (!stralloc_0(&fnadir)) die_nomem();
243
244 /* may not exists since we run before ezmlm-send */
245 if (mkdir(fnadir.s,0755) == -1)
246 if (errno != error_exist)
247 strerr_die4x(111,FATAL,ERR_CREATE,fnadir.s,": ");
248
249 /* Open indexn */
250 fdindexn = open_trunc(fnifn.s);
251 if (fdindexn == -1)
252 strerr_die4x(111,FATAL,ERR_WRITE,fnifn.s,": ");
253
254 /* set up buffers for indexn */
255 substdio_fdbuf(&ssindexn,write,fdindexn,indexnbuf,sizeof(indexnbuf));
256
257 concatHDR(subject.s,subject.len,&lines,FATAL); /* make 1 line */
258 decodeHDR(lines.s,lines.len,&qline,charset.s,FATAL); /* decode mime */
259 r = unfoldHDR(qline.s,qline.len,&lines,charset.s,&dcprefix,1,FATAL);
260 /* trim mime */
261
262 fdindex = open_read(fnif.s);
263 if (fdindex == -1) {
264 if (errno != error_noent)
265 strerr_die4x(111,FATAL,ERR_OPEN, fnif.s, ": ");
266 } else {
267 substdio_fdbuf(&ssin,read,fdindex,inbuf,sizeof(inbuf));
268 for(;;) {
269 if (getln(&ssin,&qline,&match,'\n') == -1)
270 strerr_die4sys(111,FATAL,ERR_READ, fnif.s, ": ");
271 if (!match)
272 break;
273 pos = scan_ulong(qline.s,&idx);
274 if (!idx) /* "impossible!" */
275 strerr_die2x(111,FATAL,ERR_BAD_INDEX);
276 if (idx >= outnum)
277 break; /* messages always come in order */
278 if (substdio_put(&ssindexn,qline.s,qline.len) == -1)
279 die_indexn();
280 if (qline.s[pos] == ':') { /* has author line */
281 if (getln(&ssin,&qline,&match,'\n') == -1)
282 strerr_die4x(111,FATAL,ERR_READ, fnif.s, ": ");
283 if (!match && qline.s[0] != '\t') /* "impossible! */
284 strerr_die2x(111,FATAL,ERR_BAD_INDEX);
285 if (substdio_put(&ssindexn,qline.s,qline.len) == -1)
286 die_indexn();
287 }
288 }
289 close(fdindex);
290 }
291 if (!stralloc_copyb(&qline,strnum,fmt_ulong(strnum,outnum))) die_nomem();
292 if (!stralloc_cats(&qline,": ")) die_nomem(); /* ':' for new ver */
293 makehash(lines.s,lines.len,hash);
294 if (!stralloc_catb(&qline,hash,HASHLEN)) die_nomem();
295 if (!stralloc_cats(&qline," ")) die_nomem();
296 if (r & 1) /* reply */
297 if (!stralloc_cats(&qline,"Re: ")) die_nomem();
298 if (!stralloc_cat(&qline,&lines)) die_nomem();
299 if (!stralloc_cats(&qline,"\n\t")) die_nomem();
300 if (!stralloc_cat(&qline,&received)) die_nomem();
301 if (!stralloc_cats(&qline,";")) die_nomem();
302
303 concatHDR(from.s,from.len,&lines,FATAL);
304 mkauthhash(lines.s,lines.len,hash);
305
306 if (!stralloc_catb(&qline,hash,HASHLEN)) die_nomem();
307 if (!stralloc_cats(&qline," ")) die_nomem();
308
309 decodeHDR(cp,author_name(&cp,lines.s,lines.len),&from,charset.s,FATAL);
310 (void) unfoldHDR(from.s,from.len,&lines,charset.s,&dcprefix,0,FATAL);
311 if (!stralloc_cat(&qline,&lines)) die_nomem();
312
313 if (!stralloc_cats(&qline,"\n")) die_nomem();
314 if (substdio_put(&ssindexn,qline.s,qline.len) == -1) die_indexn();
315 if (substdio_flush(&ssindexn) == -1) die_indexn();
316 if (fsync(fdindexn) == -1) die_indexn();
317 if (fchmod(fdindexn,MODE_ARCHIVE | 0700) == -1) die_indexn();
318 if (close(fdindexn) == -1) die_indexn(); /* NFS stupidity */
319 if (rename(fnifn.s,fnif.s) == -1)
320 strerr_die4x(111,FATAL,ERR_MOVE,fnifn.s,": ");
321 return r;
322 }
323
324 void transferenc()
325 {
326 if (flagcd) {
327 qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
328 if (flagcd == 'Q')
329 qmail_puts(&qq,"Quoted-printable\n\n");
330 else
331 qmail_puts(&qq,"base64\n\n");
332 } else
333 qmail_puts(&qq,"\n\n");
334 }
335
336 void getcharset()
337 {
338 if (getconf_line(&charset,"charset",0,FATAL,dir)) {
339 if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
340 if (charset.s[charset.len - 1] == 'B' ||
341 charset.s[charset.len - 1] == 'Q') {
342 flagcd = charset.s[charset.len - 1];
343 charset.s[charset.len - 2] = '\0';
344 }
345 }
346 } else
347 if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
348
349 if (!stralloc_0(&charset)) die_nomem();
350 }
351
352 void main(argc,argv)
353 int argc;
354 char **argv;
355 {
356 unsigned long subs;
357 int fdlock;
358 char *sender;
359 char *mlheader = (char *) 0;
360 char *ret;
361 char *err;
362 int flagmlwasthere;
363 int flagqmqp = 0; /* don't use qmqp by default */
364 int flaglistid = 0; /* no listid header added */
365 int match;
366 unsigned int i;
367 int r,fd;
368 int flaginheader;
369 int flagbadfield;
370 int flagbadpart;
371 int flagseenext;
372 int flagsubline;
373 int flagfromline;
374 int flagcontline;
375 int flagarchiveonly;
376 int flagtrailer;
377 unsigned int pos;
378 int opt;
379 char *cp, *cpstart, *cpafter;
380
381 umask(022);
382 sig_pipeignore();
383
384 while ((opt = getopt(argc,argv,"cCh:H:lLrRqQs:S:vV")) != opteof)
385 switch(opt) {
386 case 'c': case 'C': break; /* ignore for backwards compat */
387 case 'h':
388 case 'H': mlheader = optarg; /* Alternative sublist check header */
389 mlheader[str_chr(mlheader,':')] = '\0';
390 break;
391 case 'l': flaglog = 1; break;
392 case 'L': flaglog = 0; break;
393 case 'r': flagnoreceived = 0; break;
394 case 'R': flagnoreceived = 1; break;
395 case 's':
396 case 'S': pos = scan_ulong(optarg,&hash_lo);
397 if (!optarg[pos++]) break;
398 (void) scan_ulong(optarg+pos,&hash_hi);
399 if (hash_hi > 52L) hash_hi = 52L;
400 if (hash_lo > hash_hi) hash_lo = hash_hi;
401
402 break;
403 case 'q': flagqmqp = 0; break;
404 case 'Q': flagqmqp = 1; break;
405 case 'v':
406 case 'V': strerr_die2x(0,
407 "ezmlm-send version: ezmlm-0.53+",EZIDX_VERSION);
408 default:
409 die_usage();
410 }
411
412
413 dir = argv[optind++];
414 if (!dir) die_usage();
415
416 sender = env_get("SENDER");
417
418 if (chdir(dir) == -1)
419 strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
420
421 fdlock = open_append("lock");
422 if (fdlock == -1)
423 strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: ");
424 if (lock_ex(fdlock) == -1)
425 strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: ");
426
427 flagarchived = getconf_line(&line,"archived",0,FATAL,dir);
428 flagindexed = getconf_line(&line,"indexed",0,FATAL,dir);
429 getcharset();
430 flagprefixed = getconf_line(&prefix,"prefix",0,FATAL,dir);
431 if (prefix.len) { /* encoding and serial # support */
432 /* no sanity checks - you put '\n' or '\0' */
433 /* into the coded string, you pay */
434
435 decodeHDR(prefix.s,prefix.len,&line,charset.s,FATAL);
436 (void) unfoldHDR(line.s,line.len,&dcprefix,charset.s,&dummy,0,FATAL);
437 if (!stralloc_copy(&dcprefix,&line)) die_nomem();
438 serial = byte_rchr(prefix.s,prefix.len,'#');
439 }
440 if ((fd = open_read("text/trailer")) == -1) { /* see if there is a trailer */
441 if (errno == error_noent) flagtrailer = 0;
442 else strerr_die2sys(111,ERR_OPEN,"text/trailer: ");
443 } else {
444 close(fd);
445 flagtrailer = 1;
446 }
447
448 getconf(&mimeremove,"mimeremove",0,FATAL,dir);
449
450 if (getconf_line(&line,"num",0,FATAL,dir)) { /* Now non-FATAL, def=0 */
451 if (!stralloc_0(&line)) die_nomem();
452 cp = line.s + scan_ulong(line.s,&msgnum);
453 ++msgnum;
454 if (*cp++ == ':')
455 scan_ulong(cp,&cumsize);
456 } else
457 msgnum = 1L; /* if num not there */
458
459 getconf_line(&outhost,"outhost",1,FATAL,dir);
460 getconf_line(&outlocal,"outlocal",1,FATAL,dir);
461 set_cpoutlocal(&outlocal);
462 set_cpouthost(&outhost);
463 flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir);
464
465 if (flagqmqp) { /* forward compatibility ;-) */
466 if (!stralloc_copys(&line,QMQPSERVERS)) die_nomem();
467 if (!stralloc_cats(&line,"/0")) die_nomem();
468 if (!stralloc_0(&line)) die_nomem();
469 (void) getconf_line(&qmqpservers,line.s,0,FATAL,dir);
470 }
471
472 getconf(&headerremove,"headerremove",1,FATAL,dir);
473 if (!constmap_init(&headerremovemap,headerremove.s,headerremove.len,0))
474 die_nomem();
475
476 if (!stralloc_copys(&mydtline,"Delivered-To: mailing list ")) die_nomem();
477 if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
478 if (!stralloc_cats(&mydtline,"@")) die_nomem();
479 if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
480 if (!stralloc_cats(&mydtline,"\n")) die_nomem();
481
482 if (sender) {
483 if (!*sender)
484 strerr_die2x(100,FATAL,ERR_BOUNCE);
485 if (str_equal(sender,"#@[]"))
486 strerr_die2x(100,FATAL,ERR_BOUNCE);
487 if (flagsublist)
488 if (!sublistmatch(sender))
489 strerr_die2x(100,FATAL,ERR_NOT_PARENT);
490 }
491 innum = msgnum; /* innum = incoming */
492 outnum = msgnum; /* outnum = outgoing */
493 if (flagsublist && !flagarchived) { /* msgnum = archive */
494 pos = byte_rchr(sublist.s,sublist.len,'@'); /* checked in sublistmatch */
495 if (str_start(sender+pos,"-return-"))
496 pos += 8;
497 pos += scan_ulong(sender+pos,&innum);
498 if (!flagarchived && innum && sender[pos] == '-')
499 outnum = innum;
500 }
501 szmsgnum[fmt_ulong(szmsgnum,outnum)] = '\0';
502 set_cpnum(szmsgnum); /* for copy */
503
504 if (flagarchived) {
505 if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
506 if (!stralloc_catb(&fnadir,strnum,
507 fmt_ulong(strnum,outnum / 100))) die_nomem();
508 if (!stralloc_copy(&fnaf,&fnadir)) die_nomem();
509 if (!stralloc_cats(&fnaf,"/")) die_nomem();
510 if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,
511 (unsigned int) (outnum % 100),2))) die_nomem();
512 if (!stralloc_0(&fnadir)) die_nomem();
513 if (!stralloc_0(&fnaf)) die_nomem();
514
515 if (mkdir(fnadir.s,0755) == -1)
516 if (errno != error_exist)
517 strerr_die4sys(111,FATAL,ERR_CREATE,fnadir.s,": ");
518 fdarchive = open_trunc(fnaf.s);
519 if (fdarchive == -1)
520 strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": ");
521
522 substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf));
523 /* return-path to archive */
524 if (!stralloc_copys(&line,"Return-Path: <")) die_nomem();
525 if (sender) { /* same as qmail-local */
526 if (!quote2(&qline,sender)) die_nomem();
527 for (i = 0;i < qline.len;++i) if (qline.s[i] == '\n') qline.s[i] = '_';
528 if (!stralloc_cat(&line,&qline)) die_nomem();
529 }
530 if (!stralloc_cats(&line,">\n")) die_nomem();
531 if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive();
532 }
533
534 if (flagqmqp) {
535 if (qmail_open(&qq,&qmqpservers) == -1) /* open qmqp */
536 strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
537 } else if (qmail_open(&qq,(stralloc *) 0) == -1) /* open queue */
538 strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
539
540 if (!flagsublist) {
541 getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
542 qa_puts("Mailing-List: ");
543 qa_put(mailinglist.s,mailinglist.len);
544 if (getconf_line(&line,"listid",0,FATAL,dir)) {
545 flaglistid = 1;
546 qmail_puts(&qq,"\nList-ID: ");
547 qmail_put(&qq,line.s,line.len);
548 }
549 qa_puts("\n");
550 }
551 copy(&qq,"headeradd",'H',FATAL);
552 qa_put(mydtline.s,mydtline.len);
553
554 flagmlwasthere = 0;
555 flaginheader = 1;
556 flagfoundokpart = 1;
557 flagbadfield = 0;
558 flagbadpart = 0;
559 flagseenext = 0;
560 flagsubline = 0;
561 flagfromline = 0;
562 flagreceived = 0;
563 for (;;) {
564 if (getln(&ss0,&line,&match,'\n') == -1)
565 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
566 if (flaginheader && match) {
567 if (line.len == 1) { /* end of header */
568 flaginheader = 0;
569 if (flagindexed) /* std entry */
570 r = idx_copy_insertsubject(); /* all indexed lists */
571 if (flagprefixed && !flagsublist) {
572 qa_puts("Subject:");
573 if (!flagindexed) { /* non-indexed prefixed lists */
574 concatHDR(subject.s,subject.len,&lines,FATAL);
575 decodeHDR(lines.s,lines.len,&qline,charset.s,FATAL);
576 r = unfoldHDR(qline.s,qline.len,&lines,
577 charset.s,&dcprefix,1,FATAL);
578 }
579 if (!(r & 2)) {
580 qmail_puts(&qq," ");
581 if (serial == prefix.len)
582 qmail_put(&qq,prefix.s,prefix.len);
583 else {
584 qmail_put(&qq,prefix.s,serial);
585 qmail_puts(&qq,szmsgnum);
586 qmail_put(&qq,prefix.s+serial+1,prefix.len-serial-1);
587 }
588 }
589 qa_put(subject.s,subject.len);
590 }
591 /* do other stuff to do with post header processing here */
592 if (content.len) { /* get MIME boundary, if exists */
593 concatHDR(content.s,content.len,&qline,FATAL);
594 if (!stralloc_copy(&content,&qline)) die_nomem();
595
596 if (flagtrailer && /* trailer only for some multipart */
597 case_startb(content.s,content.len,"multipart/"))
598 if (!case_startb(content.s+10,content.len-10,"mixed") &&
599 !case_startb(content.s+10,content.len-10,"digest") &&
600 !case_startb(content.s+10,content.len-10,"parallel"))
601 flagtrailer = 0;
602
603 cp = content.s;
604 cpafter = cp + content.len; /* check after each ';' */
605 while ((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
606 ++cp;
607 while (cp < cpafter &&
608 (*cp == ' ' || *cp == '\t' || *cp == '\n')) ++cp;
609 if (case_startb(cp,cpafter-cp,"boundary=")) {
610 cp += 9; /* after boundary= */
611 if (*cp == '"') { /* quoted boundary */
612 ++cp;
613 cpstart = cp;
614 while (cp < cpafter && *cp != '"') ++cp;
615 if (cp == cpafter)
616 strerr_die1x(100,ERR_MIME_QUOTE);
617 } else { /* non-quoted boundary */
618 cpstart = cp; /* find terminator of boundary */
619 while (cp < cpafter && *cp != ';' &&
620 *cp != ' ' && *cp != '\t' && *cp != '\n') ++cp;
621 }
622 if (!stralloc_copys(&boundary,"--")) die_nomem();
623 if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
624 die_nomem();
625 flagfoundokpart = 0;
626 if (!constmap_init(&mimeremovemap,mimeremove.s,mimeremove.len,0))
627 die_nomem();
628 flagbadpart = 1; /* skip before first boundary */
629 qa_puts("\n"); /* to make up for the lost '\n' */
630 }
631 }
632 }
633 } else if ((*line.s != ' ') && (*line.s != '\t')) {
634 flagsubline = 0;
635 flagfromline = 0;
636 flagbadfield = 0;
637 flagarchiveonly = 0;
638 flagcontline = 0;
639 if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
640 flagbadfield = 1;
641 if ((flagnoreceived || !flagreceived) &&
642 case_startb(line.s,line.len,"Received:")) {
643 if (!flagreceived) { /* get date from first rec'd */
644 flagreceived = 1; /* line (done by qmail) */
645 pos = byte_chr(line.s,line.len,';');
646 if (pos != line.len) /* has '\n' */
647 if (!stralloc_copyb(&received,line.s+pos+2,line.len - pos - 3))
648 die_nomem();
649 } else { /* suppress, but archive */
650 flagarchiveonly = 1; /* but do not suppress the */
651 flagbadfield = 1; /* top one added by qmail */
652 }
653 } else if (case_startb(line.s,line.len,"Mailing-List:"))
654 flagmlwasthere = 1; /* sublists always ok ezmlm masters */
655 else if (mlheader && case_startb(line.s,line.len,mlheader))
656 flagmlwasthere = 1; /* mlheader treated as ML */
657 else if ((mimeremove.len || flagtrailer) && /* else no MIME need*/
658 case_startb(line.s,line.len,"Content-Type:")) {
659 if (!stralloc_copyb(&content,line.s+13,line.len-13)) die_nomem();
660 flagcontline = 1;
661 } else if (case_startb(line.s,line.len,"Subject:")) {
662 if (!stralloc_copyb(&subject,line.s+8,line.len-8)) die_nomem();
663 flagsubline = 1;
664 if (flagprefixed && !flagsublist) /* don't prefix for sublists */
665 flagbadfield = 1; /* we'll print our own */
666 } else if (flagtrailer &&
667 case_startb(line.s,line.len,"Content-Transfer-Encoding:")) {
668 cp = line.s + 26;
669 cpafter = cp + line.len;
670 while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp;
671 if (case_startb(cp,cpafter-cp,"base64")) encin = 'B';
672 else if (case_startb(cp,cpafter-cp,"Quoted-Printable")) encin = 'Q';
673 } else if (flaglistid && case_startb(line.s,line.len,"list-id:"))
674 flagbadfield = 1; /* suppress if we added our own */
675 else if (flagindexed) {
676
677 if (case_startb(line.s,line.len,"From:")) {
678 flagfromline = 1;
679 if (!stralloc_copyb(&from,line.s+5,line.len-5)) die_nomem();
680 }
681 } else if (line.len == mydtline.len)
682 if (!byte_diff(line.s,line.len,mydtline.s))
683 strerr_die2x(100,FATAL,ERR_LOOPING);
684 } else { /* continuation lines */
685 if (flagsubline) {
686 if (!stralloc_cat(&subject,&line)) die_nomem();
687 } else if (flagfromline) {
688 if (!stralloc_cat(&from,&line)) die_nomem();
689 } else if (flagcontline) {
690 if (!stralloc_cat(&content,&line)) die_nomem();
691 }
692 }
693 } else /* body */
694 msgsize += line.len; /* always for tstdig support */
695
696 if (!(flaginheader && flagbadfield)) {
697 if (boundary.len && line.len > boundary.len &&
698 !str_diffn(line.s,boundary.s,boundary.len)) {
699 if (line.s[boundary.len] == '-' && line.s[boundary.len+1] == '-') {
700 flagbadpart = 0; /* end boundary should be output */
701 if (flagtrailer) {
702 qmail_puts(&qq,"\n");
703 qmail_put(&qq,boundary.s,boundary.len);
704 qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
705 qmail_puts(&qq,charset.s);
706 transferenc(); /* trailer for multipart message */
707 copy(&qq,"text/trailer",flagcd,FATAL);
708 if (flagcd == 'B') { /* need to do our own flushing */
709 encodeB("",0,&qline,2,FATAL);
710 qmail_put(&qq,qline.s,qline.len);
711 }
712 }
713 } else { /* new part */
714 flagbadpart = 1; /* skip lines */
715 if (!stralloc_copy(&lines,&line)) die_nomem(); /* but save */
716 flagseenext = 1; /* need to check Cont-type */
717 }
718 } else if (flagseenext) { /* last was boundary, now stored */
719 if (case_startb(line.s,line.len,"content-type:")) {
720 flagseenext = 0; /* done thinking about it */
721 cp = line.s + 13; /* start of type */
722 while (*cp == ' ' || *cp == '\t') ++cp;
723 cpstart = cp; /* end of type */
724 while (*cp != '\n' && *cp != '\t' && *cp != ' ' && *cp != ';') ++cp;
725 if (constmap(&mimeremovemap,cpstart,cp-cpstart)) {
726 flagbadpart = 1;
727 } else {
728 flagfoundokpart = 1;
729 qa_put(lines.s,lines.len); /* saved lines */
730 flagbadpart = 0; /* do this part */
731 }
732 } else if (line.len == 1) { /* end of content desc */
733 flagbadpart = 0; /* default type, so ok */
734 flagseenext = 0; /* done thinking about it */
735 } else /* save line in cont desc */
736 if (!stralloc_cat(&lines,&line)) die_nomem();
737 }
738 if (!flagbadpart)
739 qa_put(line.s,line.len);
740
741 } else if (flagarchiveonly && flagarchived) /* received headers */
742 if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive();
743 if (!match)
744 break;
745 }
746 if (!boundary.len && flagtrailer) {
747 qmail_puts(&qq,"\n"); /* trailer for non-multipart message */
748 if (!encin || encin == 'Q') { /* can add for QP, but not for base64 */
749 copy(&qq,"text/trailer",encin,FATAL);
750 qmail_puts(&qq,"\n"); /* no need to flush for plain/QP */
751 }
752 }
753
754 cumsize += (msgsize + 128L) >> 8; /* round to 256 byte 'records' */
755 /* check message tag */
756 if (flagsublist) { /* sublists need tag if selected/suppt*/
757 if (flaglog)
758 if ((ret = checktag(dir,innum,hash_lo+1L,"m",(char *) 0,hashout))) {
759 if (*ret) strerr_die2x(111,FATAL,ret);
760 else strerr_die2x(100,FATAL,ERR_NOT_PARENT);
761 }
762 if (!flagmlwasthere) /* sublists need ML header */
763 strerr_die2x(100,FATAL,ERR_SUBLIST);
764 } else /* others are not allowed to have one */
765 if (flagmlwasthere)
766 strerr_die2x(100,FATAL,ERR_MAILING_LIST);
767 if (!flagfoundokpart) /* all parts were on the strip list */
768 strerr_die2x(100,FATAL,ERR_BAD_ALL);
769
770 if (flagarchived) {
771 if (substdio_flush(&ssarchive) == -1) die_archive();
772 if (fsync(fdarchive) == -1) die_archive();
773 if (fchmod(fdarchive,MODE_ARCHIVE | 0700) == -1) die_archive();
774 if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */
775 }
776
777 if (flaglog) {
778 tagmsg(dir,innum,sender,"m",hashout,qq.msgbytes,53L,FATAL);
779 hashout[COOKIE] = '\0';
780 }
781
782 numwrite();
783 if (!stralloc_copy(&line,&outlocal)) die_nomem();
784 if (!stralloc_cats(&line,"-return-")) die_nomem();
785 if (!stralloc_cats(&line,szmsgnum)) die_nomem();
786 if (!stralloc_cats(&line,"-@")) die_nomem();
787 if (!stralloc_cat(&line,&outhost)) die_nomem();
788 if (!stralloc_cats(&line,"-@[]")) die_nomem();
789 if (!stralloc_0(&line)) die_nomem();
790 qmail_from(&qq,line.s); /* envelope sender */
791 subs = putsubs(dir,hash_lo,hash_hi,subto,1,FATAL); /* subscribers */
792 if (flagsublist) hash_lo++;
793
794 if (*(err = qmail_close(&qq)) == '\0') {
795 if (flaglog) /* mysql logging */
796 (void) logmsg(dir,outnum,hash_lo,subs,flagsublist ? 3 : 4);
797 closesql();
798 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
799 strerr_die2x(0,"ezmlm-send: info: qp ",strnum);
800 } else {
801 --msgnum;
802 cumsize -= (msgsize + 128L) >> 8;
803 numwrite();
804 strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
805 }
806 }