1 /* $Id: ezmlm-send.c,v 1.77 1999/10/29 02:49:14 lindberg Exp $*/
2 /* $Name: ezmlm-idx-040 $*/
17 #include "readwrite.h"
25 #include "subscribe.h"
33 int flagnoreceived
= 1; /* suppress received headers by default. They*/
34 /* are still archived. =0 => archived and */
36 int flaglog
= 1; /* for lists with mysql support, use tags */
37 /* and log traffic to the database */
38 #define FATAL "ezmlm-send: fatal: "
42 strerr_die1x(100,"ezmlm-send: usage: ezmlm-send [-cClLqQrR] [-h header] dir");
46 strerr_die2x(111,FATAL
,ERR_NOMEM
);
49 /* for writing new index file indexn later moved to index. */
53 char strnum
[FMT_ULONG
];
54 char szmsgnum
[FMT_ULONG
];
57 stralloc fnadir
= {0};
65 stralloc subject
= {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};
74 stralloc qmqpservers
= {0};
78 strerr_die4x(111,FATAL
,ERR_WRITE
,fnifn
.s
,": ");
81 void *psql
= (void *) 0;
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 */
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 */
98 unsigned int serial
= 0;
103 char hashout
[COOKIE
+1];
106 char archivebuf
[1024];
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
;
128 unsigned int mywrite(fd
,buf
,len
)
133 qmail_put(&qq
,buf
,len
);
141 qmail_put(&qq
,"T",1);
149 strerr_die4sys(111,FATAL
,ERR_WRITE
,fnaf
.s
,": ");
153 strerr_die3sys(111,FATAL
,ERR_CREATE
,"numnew: ");
156 void qa_put(buf
,len
) char *buf
; unsigned int len
;
158 qmail_put(&qq
,buf
,len
);
160 if (substdio_put(&ssarchive
,buf
,len
) == -1) die_archive();
163 void qa_puts(buf
) char *buf
;
167 if (substdio_puts(&ssarchive
,buf
) == -1) die_archive();
170 int sublistmatch(sender
)
177 if (j
< sublist
.len
) return 0;
179 i
= byte_rchr(sublist
.s
,sublist
.len
,'@');
180 if (i
== sublist
.len
) return 1;
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
)))
193 substdio ss0
= SUBSTDIO_FDBUF(read
,0,buf0
,sizeof(buf0
));
196 { /* this one deals with msgnum, not outnum! */
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)
204 if (substdio_puts(&ssnumnew
,":") == -1) die_numnew();
205 if (substdio_put(&ssnumnew
,strnum
,fmt_ulong(strnum
,cumsize
)) == -1)
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: ");
216 stralloc mydtline
= {0};
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! */
233 if (!stralloc_copys(&fnadir
,"archive/")) die_nomem();
234 if (!stralloc_catb(&fnadir
,strnum
,fmt_ulong(strnum
,outnum
/ 100)))
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();
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
,": ");
250 fdindexn
= open_trunc(fnifn
.s
);
252 strerr_die4x(111,FATAL
,ERR_WRITE
,fnifn
.s
,": ");
254 /* set up buffers for indexn */
255 substdio_fdbuf(&ssindexn
,write
,fdindexn
,indexnbuf
,sizeof(indexnbuf
));
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
);
262 fdindex
= open_read(fnif
.s
);
264 if (errno
!= error_noent
)
265 strerr_die4x(111,FATAL
,ERR_OPEN
, fnif
.s
, ": ");
267 substdio_fdbuf(&ssin
,read
,fdindex
,inbuf
,sizeof(inbuf
));
269 if (getln(&ssin
,&qline
,&match
,'\n') == -1)
270 strerr_die4sys(111,FATAL
,ERR_READ
, fnif
.s
, ": ");
273 pos
= scan_ulong(qline
.s
,&idx
);
274 if (!idx
) /* "impossible!" */
275 strerr_die2x(111,FATAL
,ERR_BAD_INDEX
);
277 break; /* messages always come in order */
278 if (substdio_put(&ssindexn
,qline
.s
,qline
.len
) == -1)
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)
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();
303 concatHDR(from
.s
,from
.len
,&lines
,FATAL
);
304 mkauthhash(lines
.s
,lines
.len
,hash
);
306 if (!stralloc_catb(&qline
,hash
,HASHLEN
)) die_nomem();
307 if (!stralloc_cats(&qline
," ")) die_nomem();
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();
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
,": ");
327 qmail_puts(&qq
,"\nContent-Transfer-Encoding: ");
329 qmail_puts(&qq
,"Quoted-printable\n\n");
331 qmail_puts(&qq
,"base64\n\n");
333 qmail_puts(&qq
,"\n\n");
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';
347 if (!stralloc_copys(&charset
,TXT_DEF_CHARSET
)) die_nomem();
349 if (!stralloc_0(&charset
)) die_nomem();
359 char *mlheader
= (char *) 0;
363 int flagqmqp
= 0; /* don't use qmqp by default */
364 int flaglistid
= 0; /* no listid header added */
379 char *cp
, *cpstart
, *cpafter
;
384 while ((opt
= getopt(argc
,argv
,"cCh:H:lLrRqQs:S:vV")) != opteof
)
386 case 'c': case 'C': break; /* ignore for backwards compat */
388 case 'H': mlheader
= optarg
; /* Alternative sublist check header */
389 mlheader
[str_chr(mlheader
,':')] = '\0';
391 case 'l': flaglog
= 1; break;
392 case 'L': flaglog
= 0; break;
393 case 'r': flagnoreceived
= 0; break;
394 case 'R': flagnoreceived
= 1; break;
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
;
403 case 'q': flagqmqp
= 0; break;
404 case 'Q': flagqmqp
= 1; break;
406 case 'V': strerr_die2x(0,
407 "ezmlm-send version: ezmlm-0.53+",EZIDX_VERSION
);
413 dir
= argv
[optind
++];
414 if (!dir
) die_usage();
416 sender
= env_get("SENDER");
418 if (chdir(dir
) == -1)
419 strerr_die4sys(111,FATAL
,ERR_SWITCH
,dir
,": ");
421 fdlock
= open_append("lock");
423 strerr_die4sys(111,FATAL
,ERR_OPEN
,dir
,"/lock: ");
424 if (lock_ex(fdlock
) == -1)
425 strerr_die4sys(111,FATAL
,ERR_OBTAIN
,dir
,"/lock: ");
427 flagarchived
= getconf_line(&line
,"archived",0,FATAL
,dir
);
428 flagindexed
= getconf_line(&line
,"indexed",0,FATAL
,dir
);
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 */
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
,'#');
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: ");
448 getconf(&mimeremove
,"mimeremove",0,FATAL
,dir
);
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
);
455 scan_ulong(cp
,&cumsize
);
457 msgnum
= 1L; /* if num not there */
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
);
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
);
472 getconf(&headerremove
,"headerremove",1,FATAL
,dir
);
473 if (!constmap_init(&headerremovemap
,headerremove
.s
,headerremove
.len
,0))
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();
484 strerr_die2x(100,FATAL
,ERR_BOUNCE
);
485 if (str_equal(sender
,"#@[]"))
486 strerr_die2x(100,FATAL
,ERR_BOUNCE
);
488 if (!sublistmatch(sender
))
489 strerr_die2x(100,FATAL
,ERR_NOT_PARENT
);
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-"))
497 pos
+= scan_ulong(sender
+pos
,&innum
);
498 if (!flagarchived
&& innum
&& sender
[pos
] == '-')
501 szmsgnum
[fmt_ulong(szmsgnum
,outnum
)] = '\0';
502 set_cpnum(szmsgnum
); /* for copy */
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();
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
);
520 strerr_die4sys(111,FATAL
,ERR_WRITE
,fnaf
.s
,": ");
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();
530 if (!stralloc_cats(&line
,">\n")) die_nomem();
531 if (substdio_put(&ssarchive
,line
.s
,line
.len
) == -1) die_archive();
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
);
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
)) {
546 qmail_puts(&qq
,"\nList-ID: ");
547 qmail_put(&qq
,line
.s
,line
.len
);
551 copy(&qq
,"headeradd",'H',FATAL
);
552 qa_put(mydtline
.s
,mydtline
.len
);
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 */
569 if (flagindexed
) /* std entry */
570 r
= idx_copy_insertsubject(); /* all indexed lists */
571 if (flagprefixed
&& !flagsublist
) {
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
);
581 if (serial
== prefix
.len
)
582 qmail_put(&qq
,prefix
.s
,prefix
.len
);
584 qmail_put(&qq
,prefix
.s
,serial
);
585 qmail_puts(&qq
,szmsgnum
);
586 qmail_put(&qq
,prefix
.s
+serial
+1,prefix
.len
-serial
-1);
589 qa_put(subject
.s
,subject
.len
);
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();
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"))
604 cpafter
= cp
+ content
.len
; /* check after each ';' */
605 while ((cp
+= byte_chr(cp
,cpafter
-cp
,';')) != cpafter
) {
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 */
614 while (cp
< cpafter
&& *cp
!= '"') ++cp
;
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
;
622 if (!stralloc_copys(&boundary
,"--")) die_nomem();
623 if (!stralloc_catb(&boundary
,cpstart
,cp
-cpstart
))
626 if (!constmap_init(&mimeremovemap
,mimeremove
.s
,mimeremove
.len
,0))
628 flagbadpart
= 1; /* skip before first boundary */
629 qa_puts("\n"); /* to make up for the lost '\n' */
633 } else if ((*line
.s
!= ' ') && (*line
.s
!= '\t')) {
639 if (constmap(&headerremovemap
,line
.s
,byte_chr(line
.s
,line
.len
,':')))
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))
649 } else { /* suppress, but archive */
650 flagarchiveonly
= 1; /* but do not suppress the */
651 flagbadfield
= 1; /* top one added by qmail */
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();
661 } else if (case_startb(line
.s
,line
.len
,"Subject:")) {
662 if (!stralloc_copyb(&subject
,line
.s
+8,line
.len
-8)) die_nomem();
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:")) {
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
) {
677 if (case_startb(line
.s
,line
.len
,"From:")) {
679 if (!stralloc_copyb(&from
,line
.s
+5,line
.len
-5)) die_nomem();
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 */
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();
694 msgsize
+= line
.len
; /* always for tstdig support */
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 */
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
);
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 */
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
)) {
729 qa_put(lines
.s
,lines
.len
); /* saved lines */
730 flagbadpart
= 0; /* do this part */
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();
739 qa_put(line
.s
,line
.len
);
741 } else if (flagarchiveonly
&& flagarchived
) /* received headers */
742 if (substdio_put(&ssarchive
,line
.s
,line
.len
) == -1) die_archive();
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 */
754 cumsize
+= (msgsize
+ 128L) >> 8; /* round to 256 byte 'records' */
755 /* check message tag */
756 if (flagsublist
) { /* sublists need tag if selected/suppt*/
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
);
762 if (!flagmlwasthere
) /* sublists need ML header */
763 strerr_die2x(100,FATAL
,ERR_SUBLIST
);
764 } else /* others are not allowed to have one */
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
);
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 */
778 tagmsg(dir
,innum
,sender
,"m",hashout
,qq
.msgbytes
,53L,FATAL
);
779 hashout
[COOKIE
] = '\0';
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
++;
794 if (*(err
= qmail_close(&qq
)) == '\0') {
795 if (flaglog
) /* mysql logging */
796 (void) logmsg(dir
,outnum
,hash_lo
,subs
,flagsublist ?
3 : 4);
798 strnum
[fmt_ulong(strnum
,qmail_qp(&qq
))] = 0;
799 strerr_die2x(0,"ezmlm-send: info: qp ",strnum
);
802 cumsize
-= (msgsize
+ 128L) >> 8;
804 strerr_die3x(111,FATAL
,ERR_TMP_QMAIL_QUEUE
,err
+ 1);