Commit | Line | Data |
---|---|---|
f8beb284 MW |
1 | /* $Id: ezmlm-send.c,v 1.77 1999/10/29 02:49:14 lindberg Exp $*/ |
2 | /* $Name: ezmlm-idx-040 $*/ | |
5b62e993 MW |
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" | |
f8beb284 MW |
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 */ | |
5b62e993 MW |
38 | #define FATAL "ezmlm-send: fatal: " |
39 | ||
40 | void die_usage() | |
41 | { | |
f8beb284 | 42 | strerr_die1x(100,"ezmlm-send: usage: ezmlm-send [-cClLqQrR] [-h header] dir"); |
5b62e993 MW |
43 | } |
44 | void die_nomem() | |
45 | { | |
f8beb284 | 46 | strerr_die2x(111,FATAL,ERR_NOMEM); |
5b62e993 MW |
47 | } |
48 | ||
f8beb284 MW |
49 | /* for writing new index file indexn later moved to index. */ |
50 | substdio ssindexn; | |
51 | char indexnbuf[1024]; | |
52 | ||
5b62e993 | 53 | char strnum[FMT_ULONG]; |
f8beb284 MW |
54 | char szmsgnum[FMT_ULONG]; |
55 | char hash[HASHLEN]; | |
5b62e993 MW |
56 | |
57 | stralloc fnadir = {0}; | |
58 | stralloc fnaf = {0}; | |
f8beb284 MW |
59 | stralloc fnif = {0}; |
60 | stralloc fnifn = {0}; | |
5b62e993 MW |
61 | stralloc fnsub = {0}; |
62 | stralloc line = {0}; | |
f8beb284 MW |
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 | } | |
5b62e993 | 80 | |
f8beb284 MW |
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; | |
5b62e993 MW |
99 | int flagarchived; |
100 | int fdarchive; | |
f8beb284 MW |
101 | int fdindex; |
102 | int fdindexn; | |
103 | char hashout[COOKIE+1]; | |
104 | ||
5b62e993 MW |
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; | |
f8beb284 MW |
115 | stralloc mimeremove = {0}; |
116 | struct constmap mimeremovemap; | |
117 | char *dir; | |
5b62e993 MW |
118 | |
119 | struct qmail qq; | |
120 | substdio ssin; | |
121 | char inbuf[1024]; | |
122 | substdio ssout; | |
123 | char outbuf[1]; | |
124 | ||
f8beb284 MW |
125 | char textbuf[512]; |
126 | substdio sstext; | |
127 | ||
128 | unsigned int mywrite(fd,buf,len) | |
5b62e993 MW |
129 | int fd; |
130 | char *buf; | |
131 | unsigned int len; | |
132 | { | |
133 | qmail_put(&qq,buf,len); | |
134 | return len; | |
135 | } | |
136 | ||
f8beb284 MW |
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 | ||
5b62e993 MW |
147 | void die_archive() |
148 | { | |
f8beb284 | 149 | strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": "); |
5b62e993 MW |
150 | } |
151 | void die_numnew() | |
152 | { | |
f8beb284 | 153 | strerr_die3sys(111,FATAL,ERR_CREATE,"numnew: "); |
5b62e993 MW |
154 | } |
155 | ||
f8beb284 | 156 | void qa_put(buf,len) char *buf; unsigned int len; |
5b62e993 MW |
157 | { |
158 | qmail_put(&qq,buf,len); | |
159 | if (flagarchived) | |
160 | if (substdio_put(&ssarchive,buf,len) == -1) die_archive(); | |
161 | } | |
162 | ||
f8beb284 | 163 | void qa_puts(buf) char *buf; |
5b62e993 MW |
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 | { | |
f8beb284 MW |
173 | unsigned int i; |
174 | unsigned int j; | |
5b62e993 MW |
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]; | |
5b62e993 MW |
191 | |
192 | char buf0[256]; | |
193 | substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0)); | |
194 | ||
195 | void numwrite() | |
f8beb284 | 196 | { /* this one deals with msgnum, not outnum! */ |
5b62e993 MW |
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(); | |
f8beb284 MW |
204 | if (substdio_puts(&ssnumnew,":") == -1) die_numnew(); |
205 | if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,cumsize)) == -1) | |
206 | die_numnew(); | |
207 | ||
5b62e993 MW |
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) | |
f8beb284 | 213 | strerr_die3sys(111,FATAL,ERR_MOVE,"numnew: "); |
5b62e993 MW |
214 | } |
215 | ||
216 | stralloc mydtline = {0}; | |
217 | ||
f8beb284 MW |
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 | ||
5b62e993 MW |
352 | void main(argc,argv) |
353 | int argc; | |
354 | char **argv; | |
355 | { | |
f8beb284 | 356 | unsigned long subs; |
5b62e993 MW |
357 | int fdlock; |
358 | char *sender; | |
f8beb284 MW |
359 | char *mlheader = (char *) 0; |
360 | char *ret; | |
361 | char *err; | |
5b62e993 | 362 | int flagmlwasthere; |
f8beb284 MW |
363 | int flagqmqp = 0; /* don't use qmqp by default */ |
364 | int flaglistid = 0; /* no listid header added */ | |
5b62e993 | 365 | int match; |
f8beb284 MW |
366 | unsigned int i; |
367 | int r,fd; | |
5b62e993 MW |
368 | int flaginheader; |
369 | int flagbadfield; | |
f8beb284 MW |
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; | |
5b62e993 MW |
380 | |
381 | umask(022); | |
382 | sig_pipeignore(); | |
383 | ||
f8beb284 MW |
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++]; | |
5b62e993 MW |
414 | if (!dir) die_usage(); |
415 | ||
416 | sender = env_get("SENDER"); | |
417 | ||
418 | if (chdir(dir) == -1) | |
f8beb284 | 419 | strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": "); |
5b62e993 MW |
420 | |
421 | fdlock = open_append("lock"); | |
422 | if (fdlock == -1) | |
f8beb284 | 423 | strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: "); |
5b62e993 | 424 | if (lock_ex(fdlock) == -1) |
f8beb284 | 425 | strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: "); |
5b62e993 MW |
426 | |
427 | flagarchived = getconf_line(&line,"archived",0,FATAL,dir); | |
f8beb284 MW |
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 | } | |
5b62e993 | 447 | |
f8beb284 MW |
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 */ | |
5b62e993 MW |
458 | |
459 | getconf_line(&outhost,"outhost",1,FATAL,dir); | |
460 | getconf_line(&outlocal,"outlocal",1,FATAL,dir); | |
f8beb284 MW |
461 | set_cpoutlocal(&outlocal); |
462 | set_cpouthost(&outhost); | |
5b62e993 MW |
463 | flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir); |
464 | ||
f8beb284 MW |
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 | } | |
5b62e993 | 471 | |
f8beb284 MW |
472 | getconf(&headerremove,"headerremove",1,FATAL,dir); |
473 | if (!constmap_init(&headerremovemap,headerremove.s,headerremove.len,0)) | |
474 | die_nomem(); | |
5b62e993 MW |
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) | |
f8beb284 | 484 | strerr_die2x(100,FATAL,ERR_BOUNCE); |
5b62e993 | 485 | if (str_equal(sender,"#@[]")) |
f8beb284 | 486 | strerr_die2x(100,FATAL,ERR_BOUNCE); |
5b62e993 MW |
487 | if (flagsublist) |
488 | if (!sublistmatch(sender)) | |
f8beb284 | 489 | strerr_die2x(100,FATAL,ERR_NOT_PARENT); |
5b62e993 | 490 | } |
f8beb284 MW |
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 */ | |
5b62e993 MW |
503 | |
504 | if (flagarchived) { | |
505 | if (!stralloc_copys(&fnadir,"archive/")) die_nomem(); | |
f8beb284 MW |
506 | if (!stralloc_catb(&fnadir,strnum, |
507 | fmt_ulong(strnum,outnum / 100))) die_nomem(); | |
5b62e993 MW |
508 | if (!stralloc_copy(&fnaf,&fnadir)) die_nomem(); |
509 | if (!stralloc_cats(&fnaf,"/")) die_nomem(); | |
f8beb284 MW |
510 | if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum, |
511 | (unsigned int) (outnum % 100),2))) die_nomem(); | |
5b62e993 MW |
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) | |
f8beb284 | 517 | strerr_die4sys(111,FATAL,ERR_CREATE,fnadir.s,": "); |
5b62e993 MW |
518 | fdarchive = open_trunc(fnaf.s); |
519 | if (fdarchive == -1) | |
f8beb284 | 520 | strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": "); |
5b62e993 MW |
521 | |
522 | substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf)); | |
f8beb284 MW |
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(); | |
5b62e993 MW |
532 | } |
533 | ||
f8beb284 MW |
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 | ||
5b62e993 | 540 | if (!flagsublist) { |
f8beb284 MW |
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"); | |
5b62e993 | 550 | } |
f8beb284 MW |
551 | copy(&qq,"headeradd",'H',FATAL); |
552 | qa_put(mydtline.s,mydtline.len); | |
5b62e993 MW |
553 | |
554 | flagmlwasthere = 0; | |
555 | flaginheader = 1; | |
f8beb284 | 556 | flagfoundokpart = 1; |
5b62e993 | 557 | flagbadfield = 0; |
f8beb284 MW |
558 | flagbadpart = 0; |
559 | flagseenext = 0; | |
560 | flagsubline = 0; | |
561 | flagfromline = 0; | |
562 | flagreceived = 0; | |
5b62e993 MW |
563 | for (;;) { |
564 | if (getln(&ss0,&line,&match,'\n') == -1) | |
f8beb284 | 565 | strerr_die2sys(111,FATAL,ERR_READ_INPUT); |
5b62e993 | 566 | if (flaginheader && match) { |
f8beb284 | 567 | if (line.len == 1) { /* end of header */ |
5b62e993 | 568 | flaginheader = 0; |
f8beb284 MW |
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; | |
5b62e993 MW |
639 | if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':'))) |
640 | flagbadfield = 1; | |
f8beb284 MW |
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) | |
5b62e993 | 682 | if (!byte_diff(line.s,line.len,mydtline.s)) |
f8beb284 MW |
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 | } | |
5b62e993 | 692 | } |
f8beb284 MW |
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); | |
5b62e993 | 740 | |
f8beb284 MW |
741 | } else if (flagarchiveonly && flagarchived) /* received headers */ |
742 | if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive(); | |
5b62e993 MW |
743 | if (!match) |
744 | break; | |
745 | } | |
f8beb284 MW |
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 | } | |
5b62e993 | 753 | |
f8beb284 MW |
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 */ | |
5b62e993 | 765 | if (flagmlwasthere) |
f8beb284 MW |
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); | |
5b62e993 MW |
769 | |
770 | if (flagarchived) { | |
771 | if (substdio_flush(&ssarchive) == -1) die_archive(); | |
772 | if (fsync(fdarchive) == -1) die_archive(); | |
f8beb284 | 773 | if (fchmod(fdarchive,MODE_ARCHIVE | 0700) == -1) die_archive(); |
5b62e993 MW |
774 | if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */ |
775 | } | |
776 | ||
f8beb284 MW |
777 | if (flaglog) { |
778 | tagmsg(dir,innum,sender,"m",hashout,qq.msgbytes,53L,FATAL); | |
779 | hashout[COOKIE] = '\0'; | |
780 | } | |
5b62e993 | 781 | |
f8beb284 | 782 | numwrite(); |
5b62e993 MW |
783 | if (!stralloc_copy(&line,&outlocal)) die_nomem(); |
784 | if (!stralloc_cats(&line,"-return-")) die_nomem(); | |
f8beb284 | 785 | if (!stralloc_cats(&line,szmsgnum)) die_nomem(); |
5b62e993 MW |
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(); | |
f8beb284 MW |
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(); | |
5b62e993 MW |
798 | strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; |
799 | strerr_die2x(0,"ezmlm-send: info: qp ",strnum); | |
f8beb284 | 800 | } else { |
5b62e993 | 801 | --msgnum; |
f8beb284 | 802 | cumsize -= (msgsize + 128L) >> 8; |
5b62e993 | 803 | numwrite(); |
f8beb284 | 804 | strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1); |
5b62e993 MW |
805 | } |
806 | } |