Commit | Line | Data |
---|---|---|
f8beb284 MW |
1 | /*$Id: ezmlm-get.c,v 1.113 1999/11/22 01:47:45 lindberg Exp $*/ |
2 | /*$Name: ezmlm-idx-040 $*/ | |
3 | ||
4 | #include <sys/types.h> | |
5 | #include <sys/stat.h> | |
6 | #include "alloc.h" | |
7 | #include "error.h" | |
8 | #include "stralloc.h" | |
9 | #include "str.h" | |
10 | #include "env.h" | |
11 | #include "sig.h" | |
12 | #include "slurp.h" | |
13 | #include "getconf.h" | |
14 | #include "strerr.h" | |
15 | #include "byte.h" | |
16 | #include "getln.h" | |
17 | #include "case.h" | |
18 | #include "qmail.h" | |
19 | #include "substdio.h" | |
20 | #include "readwrite.h" | |
21 | #include "seek.h" | |
22 | #include "quote.h" | |
23 | #include "datetime.h" | |
24 | #include "now.h" | |
25 | #include "date822fmt.h" | |
26 | #include "fmt.h" | |
27 | #include "sgetopt.h" | |
28 | #include "cookie.h" | |
29 | #include "makehash.h" | |
30 | #include "copy.h" | |
31 | #include "constmap.h" | |
32 | #include "subscribe.h" | |
33 | #include "idxthread.h" | |
34 | #include "idx.h" | |
35 | #include "mime.h" | |
36 | #include "errtxt.h" | |
37 | ||
38 | int flagdo = 1; /* React to commands (doesn't affect -dig)*/ | |
39 | int flagbottom = 1; /* copy text/bottom + request */ | |
40 | int flagpublic = 2; /* 0 = non-public, 1 = public, 2 = respect*/ | |
41 | /* dir/public. */ | |
42 | char flagcd = '\0'; /* default: don't use quoted-printable */ | |
43 | int flagsub = 0; /* =1 subscribers only for get/index/thread */ | |
44 | char *digsz = | |
45 | "from\\to\\subject\\reply-to\\date\\message-id\\cc\\" | |
46 | "mime-version\\content-type\\content-transfer-encoding"; | |
47 | ||
48 | #define FATAL "ezmlm-get: fatal: " | |
49 | ||
50 | void die_usage() { | |
51 | strerr_die1x(100, | |
52 | "ezmlm-get: usage: " | |
53 | "ezmlm-get [-bBcClLpPsSvV] [-f fmt] [digestcode]"); | |
54 | } | |
55 | ||
56 | void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); } | |
57 | ||
58 | void die_badaddr() | |
59 | { | |
60 | strerr_die2x(100,FATAL,ERR_BAD_ADDRESS); | |
61 | } | |
62 | ||
63 | stralloc inhost = {0}; | |
64 | stralloc outhost = {0}; | |
65 | stralloc inlocal = {0}; | |
66 | stralloc outlocal = {0}; | |
67 | stralloc listname = {0}; | |
68 | stralloc mailinglist = {0}; | |
69 | stralloc qmqpservers = {0}; | |
70 | stralloc fn = {0}; | |
71 | stralloc moddir = {0}; | |
72 | stralloc charset = {0}; | |
73 | stralloc mydtline = {0}; | |
74 | stralloc digheaders = {0}; | |
75 | stralloc seed = {0}; | |
76 | struct constmap digheadersmap; | |
77 | ||
78 | char schar[] = "00_"; | |
79 | stralloc listno = {0}; | |
80 | void *psql = (void *) 0; | |
81 | ||
82 | datetime_sec when; | |
83 | struct datetime dt; | |
84 | unsigned long cumsize = 0L; /* cumulative msgs / 256 */ | |
85 | unsigned long cumsizen = 0L; /* new cumulative msgs / 256 */ | |
86 | unsigned long max = 0L; /* Last message in archive */ | |
87 | unsigned long msgsize = 0L; /* for digest accounting */ | |
88 | datetime_sec digwhen; /* last digest */ | |
89 | ||
90 | char strnum[FMT_ULONG]; | |
91 | char szmsgnum[FMT_ULONG]; | |
92 | char date[DATE822FMT]; | |
93 | char boundary[COOKIE]; | |
94 | char hashout[COOKIE]; | |
95 | stralloc line = {0}; | |
96 | stralloc line2 = {0}; | |
97 | stralloc qline = {0}; | |
98 | stralloc quoted = {0}; | |
99 | stralloc msgnum = {0}; | |
100 | stralloc num = {0}; | |
101 | stralloc subject = {0}; | |
102 | ||
103 | /* for copy archive */ | |
104 | stralloc archdate = {0}; | |
105 | stralloc archfrom = {0}; | |
106 | stralloc archto = {0}; | |
107 | stralloc archcc = {0}; | |
108 | stralloc archsubject = {0}; | |
109 | stralloc archmessageid = {0}; | |
110 | stralloc archkeywords = {0}; | |
111 | stralloc archblanklines = {0}; | |
112 | char archtype=' '; | |
113 | ||
114 | /* for mods on non-public lists (needed for future fuzzy sub dbs) */ | |
115 | stralloc mod = {0}; /* moderator addr for non-public lists */ | |
116 | char *pmod = (char *) 0; /* pointer to above */ | |
117 | ||
118 | /* for digest */ | |
119 | stralloc ddir = {0}; | |
120 | stralloc edir = {0}; | |
121 | ||
122 | int act = AC_NONE; /* Action we do */ | |
123 | int flageditor = 0; /* if we're invoked for within dir/editor */ | |
124 | struct stat st; | |
125 | ||
126 | int flaglocked = 0; /* if directory is locked */ | |
127 | int flagarchived; /* if list is archived */ | |
128 | int flagindexed; /* if list is indexed */ | |
129 | int flagq = 0; /* don't use 'quoted-printable' */ | |
130 | ||
131 | char *dir; | |
132 | char *workdir; | |
133 | char *sender; | |
134 | char *digestcode; | |
135 | ||
136 | struct qmail qq; | |
137 | int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len; | |
138 | { | |
139 | qmail_put(&qq,buf,len); | |
140 | return len; | |
141 | } | |
142 | ||
143 | int subto(s,l) | |
144 | char *s; | |
145 | unsigned int l; | |
146 | { | |
147 | qmail_put(&qq,"T",1); | |
148 | qmail_put(&qq,s,l); | |
149 | qmail_put(&qq,"",1); | |
150 | return (int) l; | |
151 | } | |
152 | ||
153 | char qqbuf[1]; | |
154 | substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf)); | |
155 | ||
156 | char inbuf[1024]; | |
157 | substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf)); | |
158 | substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf)); | |
159 | ||
160 | substdio ssnum; | |
161 | char numbuf[16]; | |
162 | ||
163 | substdio sstext; | |
164 | char textbuf[1024]; | |
165 | ||
166 | substdio ssindex; | |
167 | char indexbuf[1024]; | |
168 | ||
169 | int fdlock; | |
170 | ||
171 | void lockup() | |
172 | /* lock unless locked */ | |
173 | { | |
174 | if(!flaglocked) { | |
175 | fdlock = open_append("lock"); | |
176 | if (fdlock == -1) | |
177 | strerr_die2sys(111,FATAL,ERR_OPEN_LOCK); | |
178 | if (lock_ex(fdlock) == -1) { | |
179 | close(fdlock); | |
180 | strerr_die2sys(111,FATAL,ERR_OBTAIN_LOCK); | |
181 | } | |
182 | flaglocked = 1; | |
183 | } | |
184 | } | |
185 | ||
186 | void unlock() | |
187 | /* unlock if locked */ | |
188 | { | |
189 | if (flaglocked) { | |
190 | close(fdlock); | |
191 | flaglocked = 0; | |
192 | } | |
193 | } | |
194 | ||
195 | void code_qput(s,n) | |
196 | char *s; | |
197 | unsigned int n; | |
198 | { | |
199 | if (!flagcd) | |
200 | qmail_put(&qq,s,n); | |
201 | else { | |
202 | if (flagcd == 'B') | |
203 | encodeB(s,n,&qline,0,FATAL); | |
204 | else | |
205 | encodeQ(s,n,&qline,FATAL); | |
206 | qmail_put(&qq,qline.s,qline.len); | |
207 | msgsize += qline.len; | |
208 | } | |
209 | } | |
210 | ||
211 | void transferenc() | |
212 | { | |
213 | if (flagcd) { | |
214 | qmail_puts(&qq,"\nContent-Transfer-Encoding: "); | |
215 | if (flagcd == 'Q') | |
216 | qmail_puts(&qq,"Quoted-printable\n\n"); | |
217 | else | |
218 | qmail_puts(&qq,"base64\n\n"); | |
219 | } else | |
220 | qmail_puts(&qq,"\n\n"); | |
221 | } | |
222 | ||
223 | void zapnonsub(szerr) | |
224 | /* fatal error if flagsub is set and sender is not a subscriber */ | |
225 | /* expects the current dir to be the list dir. Error is szerr */ | |
226 | /* added check for undefined sender as a precaution */ | |
227 | char *szerr; | |
228 | { | |
229 | if (sender && *sender) { /* "no sender" is not a subscriber */ | |
230 | if (!flagsub) | |
231 | return; | |
232 | if (issub(dir,sender,(char *) 0,FATAL)) | |
233 | return; /* subscriber */ | |
234 | if (issub(ddir.s,sender,(char *) 0,FATAL)) | |
235 | return; /* digest subscriber */ | |
236 | if (issub(edir.s,sender,(char *) 0,FATAL)) | |
237 | return; /* allow addresses */ | |
238 | } | |
239 | strerr_die4x(100,FATAL,ERR_SUBSCRIBER_CAN,szerr,ERR_571); | |
240 | } | |
241 | ||
242 | void tosender() | |
243 | { | |
244 | qmail_puts(&qq,"To: "); | |
245 | if (!quote2("ed,sender)) die_nomem(); | |
246 | qmail_put(&qq,quoted.s,quoted.len); | |
247 | qmail_puts(&qq,"\n"); | |
248 | } | |
249 | ||
250 | void get_num() | |
251 | { | |
252 | /* read dir/num -> max. max/cumsizen left alone if not present */ | |
253 | /* Both of these should have been initialized to 0L */ | |
254 | ||
255 | unsigned int pos; | |
256 | if (getconf_line(&num,"num",0,FATAL,dir)) { | |
257 | if(!stralloc_0(&num)) die_nomem(); | |
258 | pos = scan_ulong(num.s,&max); | |
259 | if (num.s[pos] == ':') pos++; | |
260 | scan_ulong(num.s+pos,&cumsizen); | |
261 | } | |
262 | } | |
263 | ||
264 | unsigned long dignum() | |
265 | { | |
266 | /* return dignum if exists, 0 otherwise. */ | |
267 | ||
268 | unsigned long retval; | |
269 | if (!stralloc_copys(&num,"")) die_nomem(); /* zap */ | |
270 | getconf_line(&num,"dignum",0,FATAL,dir); | |
271 | if(!stralloc_0(&num)) die_nomem(); | |
272 | scan_ulong(num.s,&retval); | |
273 | return retval; | |
274 | } | |
275 | ||
276 | void write_ulong(num,cum,dat,fn,fnn) | |
277 | /* write num to "fnn" add ':' & cum if cum <>0, then move "fnn" to "fn" */ | |
278 | char *fn, *fnn; | |
279 | unsigned long num,cum,dat; | |
280 | { | |
281 | int fd; | |
282 | ||
283 | fd = open_trunc(fnn); | |
284 | if (fd == -1) | |
285 | strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fnn,": "); | |
286 | substdio_fdbuf(&ssnum,write,fd,numbuf,sizeof(numbuf)); | |
287 | if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,num)) == -1) | |
288 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); | |
289 | if (substdio_puts(&ssnum,":") == -1) | |
290 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); | |
291 | if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,cum)) == -1) | |
292 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); | |
293 | if (dat) { | |
294 | if (substdio_puts(&ssnum,":") == -1) | |
295 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); | |
296 | if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,dat)) == -1) | |
297 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); | |
298 | } | |
299 | if (substdio_puts(&ssnum,"\n") == -1) | |
300 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); | |
301 | if (substdio_flush(&ssnum) == -1) | |
302 | strerr_die6sys(111,FATAL,ERR_FLUSH,dir,"/",fnn,": "); | |
303 | if (fsync(fd) == -1) | |
304 | strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",fnn,": "); | |
305 | if (close(fd) == -1) | |
306 | strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",fnn,": "); | |
307 | if (rename(fnn,fn) == -1) | |
308 | strerr_die4sys(111,FATAL,ERR_MOVE,fnn,": "); | |
309 | } | |
310 | ||
311 | void normal_bottom(format) | |
312 | char format; | |
313 | /* Copies bottom text and the original message to the new message */ | |
314 | { | |
315 | if (flagbottom) { | |
316 | copy(&qq,"text/bottom",flagcd,FATAL); | |
317 | if (flagcd && format != RFC1153) { | |
318 | if (flagcd == 'B') { | |
319 | encodeB("",0,&line,2,FATAL); /* flush */ | |
320 | qmail_put(&qq,line.s,line.len); | |
321 | } | |
322 | qmail_puts(&qq,"\n--"); | |
323 | qmail_put(&qq,boundary,COOKIE); | |
324 | qmail_puts(&qq,"\nContent-Type: message/rfc822"); | |
325 | qmail_puts(&qq,"\nContent-Disposition: inline; filename=request.msg\n\n"); | |
326 | } | |
327 | qmail_puts(&qq,"Return-Path: <"); | |
328 | if (!quote2("ed,sender)) die_nomem(); | |
329 | qmail_put(&qq,quoted.s,quoted.len); | |
330 | qmail_puts(&qq,">\n"); | |
331 | if (seek_begin(0) == -1) | |
332 | strerr_die2sys(111,FATAL,ERR_SEEK_INPUT); | |
333 | if (substdio_copy(&ssqq,&ssin2) != 0) | |
334 | strerr_die2sys(111,FATAL,ERR_READ_INPUT); | |
335 | } else { | |
336 | if (flagcd == 'B' && format != RFC1153) { | |
337 | encodeB("",0,&line,2,FATAL); /* flush */ | |
338 | qmail_put(&qq,line.s,line.len); | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
343 | void presub(from,to,subject,factype,format) | |
344 | /* Starts within header, outputs "subject" and optional headers, terminates*/ | |
345 | /* header and handles output before table-of-contents */ | |
346 | unsigned long from,to; | |
347 | stralloc *subject; | |
348 | int factype; /* action type (AC_THREAD, AC_GET, AC_DIGEST) */ | |
349 | char format; /* output format type (see idx.h) */ | |
350 | { | |
351 | qmail_puts(&qq,"MIME-Version: 1.0\n"); | |
352 | switch(format) { | |
353 | case MIME: | |
354 | case VIRGIN: | |
355 | case NATIVE: | |
356 | case MIXED: | |
357 | qmail_puts(&qq,"Content-Type: multipart/"); | |
358 | if (format == MIXED) | |
359 | qmail_puts(&qq,"mixed"); | |
360 | else | |
361 | qmail_puts(&qq,"digest"); | |
362 | qmail_puts(&qq,"; boundary="); | |
363 | qmail_put(&qq,boundary,COOKIE); | |
364 | qmail_puts(&qq,"\nSubject: "); | |
365 | qmail_put(&qq,subject->s,subject->len); | |
366 | qmail_puts(&qq,"\n\n\n--"); | |
367 | qmail_put(&qq,boundary,COOKIE); | |
368 | qmail_puts(&qq,"\nContent-Type: text/plain; charset="); | |
369 | qmail_puts(&qq,charset.s); | |
370 | transferenc(); /* content-transfer-enc header if needed */ | |
371 | qmail_puts(&qq,"\n"); | |
372 | break; | |
373 | case RFC1153: | |
374 | qmail_puts(&qq,"Content-type: text/plain; charset="); | |
375 | qmail_puts(&qq,charset.s); | |
376 | qmail_puts(&qq,"\nSubject: "); | |
377 | qmail_put(&qq,subject->s,subject->len); | |
378 | qmail_puts(&qq,"\n\n"); | |
379 | flagcd = '\0'; /* We make 8-bit messages, not QP/bas64 for rfc1153 */ | |
380 | break; /* Since messages themselves aren't encoded */ | |
381 | } | |
382 | if (!stralloc_cats(subject,"\n\n")) die_nomem(); | |
383 | code_qput(subject->s,subject->len); | |
384 | if (format != NATIVE && factype != AC_THREAD && factype != AC_INDEX) { | |
385 | if (!stralloc_copys(&line,TXT_TOP_TOPICS)) die_nomem(); | |
386 | if (!stralloc_cats(&line,TXT_TOP_MESSAGES)) die_nomem(); | |
387 | if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,from))) die_nomem(); | |
388 | if (!stralloc_cats(&line,TXT_TOP_THROUGH)) die_nomem(); | |
389 | if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,to))) die_nomem(); | |
390 | if (!stralloc_cats(&line,TXT_TOP_LAST)) die_nomem(); | |
391 | code_qput(line.s,line.len); | |
392 | } | |
393 | } | |
394 | ||
395 | void postsub(factype,format) | |
396 | /* output after TOC and before first message. */ | |
397 | int factype; /* action type (AC_THREAD, AC_GET, AC_DIGEST) */ | |
398 | char format; /* output format type (see idx.h) */ | |
399 | { | |
400 | code_qput(TXT_ADMINISTRIVIA,str_len(TXT_ADMINISTRIVIA)); | |
401 | if(factype == AC_DIGEST) { | |
402 | copy(&qq,"text/digest",flagcd,FATAL); | |
403 | if (flagcd == 'B') { | |
404 | encodeB("",0,&line,2,FATAL); /* flush */ | |
405 | qmail_put(&qq,line.s,line.len); | |
406 | } | |
407 | } else | |
408 | normal_bottom(format); | |
409 | if (!flagcd || format == RFC1153) | |
410 | qmail_puts(&qq,"\n----------------------------------------------------------------------\n"); | |
411 | else | |
412 | qmail_puts(&qq,"\n"); | |
413 | } | |
414 | ||
415 | void postmsg(format) | |
416 | char format; | |
417 | { | |
418 | switch(format) { | |
419 | case MIME: | |
420 | case VIRGIN: | |
421 | case NATIVE: | |
422 | case MIXED: | |
423 | qmail_puts(&qq,"\n--"); | |
424 | qmail_put(&qq,boundary,COOKIE); /* digest boundary */ | |
425 | qmail_puts(&qq,"--\n"); | |
426 | break; | |
427 | case RFC1153: | |
428 | qmail_puts(&qq,"End of "); | |
429 | qmail_put(&qq,listname.s,listname.len); | |
430 | qmail_puts(&qq," Digest"); | |
431 | qmail_puts(&qq,"\n***********************************\n"); | |
432 | break; | |
433 | } | |
434 | } | |
435 | ||
436 | void copymsg(msg,fd,format) | |
437 | /* Copy archive message "msg" itself from open file handle fd, in "format" */ | |
438 | unsigned long msg; int fd; char format; | |
439 | { | |
440 | int match; | |
441 | int flaginheader; | |
442 | int flagskipblanks; | |
443 | int flaggoodfield; | |
444 | ||
445 | switch(format) { | |
446 | case VIRGIN: | |
447 | case NATIVE: | |
448 | substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); | |
449 | for (;;) { | |
450 | if (getln(&sstext,&line,&match,'\n') == -1) | |
451 | strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); | |
452 | if (match) { | |
453 | qmail_put(&qq,line.s,line.len); | |
454 | msgsize += line.len; | |
455 | } else | |
456 | break; | |
457 | } | |
458 | break; | |
459 | case MIME: | |
460 | case MIXED: | |
461 | flaginheader = 1; | |
462 | flaggoodfield = 0; | |
463 | substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); | |
464 | for (;;) { | |
465 | if (getln(&sstext,&line,&match,'\n') == -1) | |
466 | strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); | |
467 | if (match) { | |
468 | if (flaginheader) { | |
469 | if (line.len == 1) { | |
470 | flaginheader = 0; | |
471 | flaggoodfield = 1; | |
472 | } else if (line.s[0] != ' ' && line.s[0] != '\t') { | |
473 | flaggoodfield = 0; | |
474 | if (constmap(&digheadersmap,line.s, | |
475 | byte_chr(line.s,line.len,':'))) | |
476 | flaggoodfield = 1; | |
477 | } | |
478 | if (flaggoodfield) { | |
479 | qmail_put(&qq,line.s,line.len); /* header */ | |
480 | msgsize += line.len; | |
481 | } | |
482 | } else { | |
483 | qmail_put(&qq,line.s,line.len); /* body */ | |
484 | msgsize += line.len; | |
485 | } | |
486 | } else | |
487 | break; | |
488 | } | |
489 | break; | |
490 | case RFC1153: /* Not worth optimizing. Rarely used */ | |
491 | flaginheader = 1; | |
492 | flagskipblanks = 1; /* must skip terminal blanks acc to rfc1153 */ | |
493 | archtype = ' '; /* rfc1153 requires ordered headers */ | |
494 | if (!stralloc_copys(&archblanklines,"")) die_nomem(); | |
495 | substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); | |
496 | for (;;) { | |
497 | if (getln(&sstext,&line,&match,'\n') == -1) | |
498 | strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); | |
499 | if (match) { | |
500 | if (flaginheader) { | |
501 | if (line.len == 1) { | |
502 | flaginheader = 0; | |
503 | if (archdate.len) { | |
504 | qmail_put(&qq,archdate.s,archdate.len); | |
505 | archdate.len = 0; | |
506 | msgsize += archdate.len; | |
507 | } | |
508 | if (archto.len) { | |
509 | qmail_put(&qq,archto.s,archto.len); | |
510 | msgsize += archto.len; | |
511 | archto.len = 0; | |
512 | } | |
513 | if (archfrom.len) { | |
514 | qmail_put(&qq,archfrom.s,archfrom.len); | |
515 | msgsize += archfrom.len; | |
516 | archfrom.len = 0; | |
517 | } | |
518 | if (archcc.len) { | |
519 | qmail_put(&qq,archcc.s,archcc.len); | |
520 | msgsize += archcc.len; | |
521 | archcc.len = 0; | |
522 | } | |
523 | if (archsubject.len) { | |
524 | qmail_put(&qq,archsubject.s,archsubject.len); | |
525 | msgsize += archsubject.len; | |
526 | archsubject.len = 0; | |
527 | } | |
528 | if (archmessageid.len) { | |
529 | qmail_put(&qq,archmessageid.s,archmessageid.len); | |
530 | msgsize += archmessageid.len; | |
531 | archmessageid.len = 0; | |
532 | } | |
533 | if (archkeywords.len) { | |
534 | qmail_put(&qq,archkeywords.s,archkeywords.len); | |
535 | msgsize += archkeywords.len; | |
536 | archkeywords.len = 0; | |
537 | } | |
538 | qmail_puts(&qq,"\n"); | |
539 | } else if (line.s[0] == ' ' || line.s[0] == '\t') { | |
540 | switch (archtype) { /* continuation lines */ | |
541 | case ' ': | |
542 | break; | |
543 | case 'D': | |
544 | if (!stralloc_cat(&archdate,&line)) die_nomem(); break; | |
545 | case 'F': | |
546 | if (!stralloc_cat(&archfrom,&line)) die_nomem(); break; | |
547 | case 'T': | |
548 | if (!stralloc_cat(&archto,&line)) die_nomem(); break; | |
549 | case 'C': | |
550 | if (!stralloc_cat(&archcc,&line)) die_nomem(); break; | |
551 | case 'S': | |
552 | if (!stralloc_cat(&archsubject,&line)) die_nomem(); break; | |
553 | case 'M': | |
554 | if (!stralloc_cat(&archmessageid,&line)) die_nomem(); break; | |
555 | case 'K': | |
556 | if (!stralloc_cat(&archkeywords,&line)) die_nomem(); break; | |
557 | default: | |
558 | strerr_die2x(111,FATAL, | |
559 | "Program error: Bad archive header type"); | |
560 | } | |
561 | } else { | |
562 | archtype = ' '; | |
563 | if (case_startb(line.s,line.len,"cc:")) { | |
564 | archtype='C'; | |
565 | if (!stralloc_copy(&archcc,&line)) die_nomem(); | |
566 | } | |
567 | else if (case_startb(line.s,line.len,"date:")) { | |
568 | archtype='D'; | |
569 | if (!stralloc_copy(&archdate,&line)) die_nomem(); | |
570 | } | |
571 | else if (case_startb(line.s,line.len,"from:")) { | |
572 | archtype='F'; | |
573 | if (!stralloc_copy(&archfrom,&line)) die_nomem(); | |
574 | } | |
575 | else if (case_startb(line.s,line.len,"keywords:")) { | |
576 | archtype='K'; | |
577 | if (!stralloc_copy(&archkeywords,&line)) die_nomem(); | |
578 | } | |
579 | else if (case_startb(line.s,line.len,"message-id:")) { | |
580 | archtype='M'; | |
581 | if (!stralloc_copy(&archmessageid,&line)) die_nomem(); | |
582 | } | |
583 | else if (case_startb(line.s,line.len,"subject:")) { | |
584 | archtype='S'; | |
585 | if (!stralloc_copy(&archsubject,&line)) die_nomem(); | |
586 | } | |
587 | else if (case_startb(line.s,line.len,"to:")) { | |
588 | archtype='T'; | |
589 | if (!stralloc_copy(&archto,&line)) die_nomem(); | |
590 | } | |
591 | } | |
592 | } else if (line.len == 1) { | |
593 | if (!flagskipblanks) | |
594 | if (!stralloc_copys(&archblanklines,"\n")) die_nomem(); | |
595 | } else { | |
596 | if (archblanklines.len) { | |
597 | qmail_put(&qq,archblanklines.s,archblanklines.len); | |
598 | archblanklines.len = 0; | |
599 | } | |
600 | flagskipblanks = 0; | |
601 | qmail_put(&qq,line.s,line.len); | |
602 | msgsize += line.len; | |
603 | } | |
604 | } else | |
605 | break; | |
606 | } | |
607 | break; | |
608 | default: | |
609 | strerr_die2x(100,FATAL,"Program error: bad format in copymsg()"); | |
610 | } | |
611 | } | |
612 | ||
613 | void mime_getbad(msg) | |
614 | /* Message not found as a MIME multipart */ | |
615 | unsigned long msg; | |
616 | { | |
617 | qmail_puts(&qq,"\n--"); | |
618 | qmail_put(&qq,boundary,COOKIE); | |
619 | qmail_puts(&qq,"\nContent-Type: text/plain; charset="); | |
620 | qmail_puts(&qq,charset.s); | |
621 | qmail_puts(&qq,"\nContent-Disposition: inline; filename=\""); | |
622 | qmail_put(&qq,listname.s,listname.len); | |
623 | qmail_puts(&qq,"_"); | |
624 | qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); | |
625 | qmail_puts(&qq,".ezm\"\n"); | |
626 | transferenc(); | |
627 | copy(&qq,"text/get-bad",flagcd,FATAL); | |
628 | } | |
629 | ||
630 | void msgout(msg,format) | |
631 | /* Outputs message (everything that's needed per message) */ | |
632 | unsigned long msg; char format; | |
633 | { | |
634 | int fd; | |
635 | unsigned int len; | |
636 | ||
637 | if (!stralloc_copys(&fn,"archive/")) die_nomem(); | |
638 | ||
639 | len = fmt_ulong(strnum, msg / 100); | |
640 | if (!stralloc_catb(&fn,strnum,len)) die_nomem(); | |
641 | if (!stralloc_cats(&fn,"/")) die_nomem(); | |
642 | len = fmt_uint0(strnum, (unsigned int) (msg % 100),2); | |
643 | if (!stralloc_catb(&fn,strnum,len)) die_nomem(); | |
644 | if (!stralloc_0(&fn)) die_nomem(); | |
645 | ||
646 | switch(format) { | |
647 | case MIME: | |
648 | case VIRGIN: | |
649 | case NATIVE: | |
650 | case MIXED: | |
651 | fd = open_read(fn.s); | |
652 | if (fd == -1) { | |
653 | if (errno != error_noent) | |
654 | strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); | |
655 | else | |
656 | mime_getbad(msg); | |
657 | } else if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) { | |
658 | close(fd); | |
659 | mime_getbad(msg); | |
660 | } else { | |
661 | qmail_puts(&qq,"\n--"); | |
662 | qmail_put(&qq,boundary,COOKIE); | |
663 | qmail_puts(&qq,"\nContent-Type: message/rfc822"); | |
664 | qmail_puts(&qq,"\nContent-Disposition: inline; filename=\""); | |
665 | qmail_put(&qq,listname.s,listname.len); | |
666 | qmail_puts(&qq,"_"); | |
667 | qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); | |
668 | qmail_puts(&qq,".ezm\"\n\n"); | |
669 | copymsg(msg,fd,format); | |
670 | close(fd); | |
671 | } | |
672 | break; | |
673 | case RFC1153: | |
674 | fd = open_read(fn.s); | |
675 | if (fd == -1) { | |
676 | if (errno != error_noent) | |
677 | strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); | |
678 | else { | |
679 | qmail_puts(&qq,"\n== "); | |
680 | qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); | |
681 | qmail_puts(&qq," ==\n\n"); | |
682 | copy(&qq,"text/get-bad",flagcd,FATAL); | |
683 | } | |
684 | } else { | |
685 | if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) { | |
686 | close(fd); | |
687 | qmail_puts(&qq,"\n== "); | |
688 | qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); | |
689 | qmail_puts(&qq," ==\n\n"); | |
690 | copy(&qq,"text/get-bad",flagcd,FATAL); | |
691 | } else { | |
692 | copymsg(msg,fd,format); | |
693 | close(fd); | |
694 | } | |
695 | } | |
696 | qmail_puts(&qq,"\n------------------------------\n\n"); | |
697 | break; | |
698 | default: | |
699 | strerr_die2x(100,FATAL,"Program error: Unrecognized format in msgout"); | |
700 | break; | |
701 | } | |
702 | } | |
703 | ||
704 | void digest(msgtable,subtable,authtable,from,to,subj,factype,format) | |
705 | /* Output digest range from-to as per msgtable/subtable (from mkthread(s)). */ | |
706 | /* "Subject is the subject of the _entire_ digest/set. */ | |
707 | msgentry *msgtable; subentry *subtable; authentry *authtable; | |
708 | unsigned long from,to; stralloc *subj; int factype; char format; | |
709 | { | |
710 | msgentry *pmsgt; | |
711 | subentry *psubt; | |
712 | char *cp; | |
713 | int ffirstmsg; | |
714 | unsigned int len; | |
715 | unsigned long msg; | |
716 | unsigned long subnum; | |
717 | ||
718 | psubt = subtable; | |
719 | presub(from,to,subj,factype,format); | |
720 | ||
721 | if (format != NATIVE) { | |
722 | while (psubt->sub) { | |
723 | ffirstmsg = 1; | |
724 | /* ptr to first message with this subject */ | |
725 | pmsgt = msgtable + psubt->firstmsg - from; | |
726 | subnum = (unsigned long) (psubt - subtable +1); | |
727 | for (msg=psubt->firstmsg; msg<=to; msg++) { | |
728 | if (pmsgt->subnum == subnum) { | |
729 | if(ffirstmsg) { | |
730 | ffirstmsg = 0; | |
731 | if (!stralloc_copys(&line,"\n")) die_nomem(); | |
732 | if (psubt->sublen <= HASHLEN + 2) { | |
733 | if (!stralloc_cats(&line,"(null)\n")) die_nomem(); | |
734 | } else | |
735 | if (!stralloc_catb(&line,psubt->sub + HASHLEN + 1, | |
736 | psubt->sublen - HASHLEN - 1)) die_nomem(); | |
737 | } else | |
738 | if (!stralloc_copys(&line,"")) die_nomem(); | |
739 | if (!stralloc_cats(&line,"\t")) die_nomem(); | |
740 | if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem(); | |
741 | if (!stralloc_cats(&line,TXT_BY)) die_nomem(); | |
742 | if (pmsgt->authnum) { | |
743 | cp = authtable[pmsgt->authnum - 1].auth; | |
744 | len = authtable[pmsgt->authnum - 1].authlen; | |
745 | if (len > HASHLEN) { | |
746 | if (!stralloc_catb(&line,cp + HASHLEN + 1, | |
747 | len - HASHLEN - 1)) die_nomem(); | |
748 | } else | |
749 | if (!stralloc_catb(&line,cp,len)) die_nomem(); | |
750 | } else | |
751 | if (!stralloc_cats(&line,"\n")) die_nomem(); | |
752 | code_qput(line.s,line.len); | |
753 | } | |
754 | pmsgt++; | |
755 | } | |
756 | psubt++; | |
757 | } | |
758 | } | |
759 | postsub(factype,format); | |
760 | ||
761 | psubt = subtable; | |
762 | while (psubt->sub) { | |
763 | pmsgt = msgtable + psubt->firstmsg - from; | |
764 | subnum = (unsigned long) (psubt - subtable +1); | |
765 | for (msg=psubt->firstmsg; msg<=to; msg++) { | |
766 | if (pmsgt->subnum == subnum) | |
767 | msgout(msg,format); | |
768 | pmsgt++; | |
769 | } | |
770 | psubt++; | |
771 | } | |
772 | postmsg(format); | |
773 | idx_destroythread(msgtable,subtable,authtable); | |
774 | } | |
775 | ||
776 | void doheaders() | |
777 | { | |
778 | int flaggoodfield,match; | |
779 | ||
780 | if (act == AC_DIGEST) | |
781 | copy(&qq,"headeradd",'H',FATAL); | |
782 | ||
783 | qmail_puts(&qq,"Mailing-List: "); | |
784 | qmail_put(&qq,mailinglist.s,mailinglist.len); | |
785 | if (getconf_line(&line,"listid",0,FATAL,dir)) { | |
786 | qmail_puts(&qq,"\nList-ID: "); | |
787 | qmail_put(&qq,line.s,line.len); | |
788 | } | |
789 | qmail_puts(&qq,"\nDate: "); | |
790 | qmail_put(&qq,date,date822fmt(date,&dt)); | |
791 | qmail_puts(&qq,"Message-ID: <"); | |
792 | if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when))) | |
793 | die_nomem(); | |
794 | if (!stralloc_append(&line,".")) die_nomem(); | |
795 | if (!stralloc_catb(&line,strnum, | |
796 | fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); | |
797 | if (!stralloc_cats(&line,".ezmlm@")) die_nomem(); | |
798 | if (!stralloc_cat(&line,&outhost)) die_nomem(); | |
799 | if (!stralloc_0(&line)) die_nomem(); | |
800 | qmail_puts(&qq,line.s); | |
801 | /* "unique" MIME boundary as hash of messageid */ | |
802 | makehash(line.s,line.len,boundary); | |
803 | qmail_puts(&qq,">\nFrom: "); | |
804 | if (!quote("ed,&outlocal)) die_nomem(); | |
805 | qmail_put(&qq,quoted.s,quoted.len); | |
806 | qmail_puts(&qq,"-help@"); | |
807 | qmail_put(&qq,outhost.s,outhost.len); | |
808 | qmail_puts(&qq,"\n"); | |
809 | if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem(); | |
810 | if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); | |
811 | if (!stralloc_cats(&mydtline,"@")) die_nomem(); | |
812 | if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); | |
813 | if (!stralloc_cats(&mydtline,"\n")) die_nomem(); | |
814 | ||
815 | qmail_put(&qq,mydtline.s,mydtline.len); | |
816 | ||
817 | flaggoodfield = 0; | |
818 | if (act != AC_DIGEST) | |
819 | for (;;) { | |
820 | if (getln(&ssin,&line,&match,'\n') == -1) | |
821 | strerr_die2sys(111,FATAL,ERR_READ_INPUT); | |
822 | if (!match) break; | |
823 | if (line.len == 1) break; | |
824 | if ((line.s[0] != ' ') && (line.s[0] != '\t')) { | |
825 | flaggoodfield = 0; | |
826 | if (case_startb(line.s,line.len,"mailing-list:")) | |
827 | if (flageditor) /* we may be running from a sublist */ | |
828 | flaggoodfield = 0; | |
829 | else | |
830 | strerr_die2x(100,FATAL,ERR_MAILING_LIST); | |
831 | if (line.len == mydtline.len) | |
832 | if (byte_equal(line.s,line.len,mydtline.s)) | |
833 | strerr_die2x(100,FATAL,ERR_LOOPING); | |
834 | if (case_startb(line.s,line.len,"delivered-to:")) | |
835 | flaggoodfield = 1; | |
836 | if (case_startb(line.s,line.len,"received:")) | |
837 | flaggoodfield = 1; | |
838 | } | |
839 | if (flaggoodfield) | |
840 | qmail_put(&qq,line.s,line.len); | |
841 | } | |
842 | } | |
843 | ||
844 | ||
845 | void main(argc,argv) | |
846 | int argc; | |
847 | char **argv; | |
848 | { | |
849 | char *def; | |
850 | char *local; | |
851 | char *action = ""; | |
852 | char *psz; | |
853 | char *err; | |
854 | int fd; | |
855 | unsigned int i,j; | |
856 | int flagremote; | |
857 | int flagqmqp = 0; | |
858 | int match; | |
859 | int goodexit = 0; /* exit code for normal exit */ | |
860 | /* in manager this will be set to 0 */ | |
861 | unsigned long from,u,to,issue,prevmax,mno; | |
862 | unsigned long chunk; | |
863 | unsigned long subs; | |
864 | unsigned int pos,pos1; | |
865 | unsigned int len; | |
866 | int opt; | |
867 | char outformat = DEFAULT_FORMAT; /* default output format */ | |
868 | msgentry *msgtable; | |
869 | subentry *subtable; | |
870 | authentry *authtable; | |
871 | dateentry *datetable; | |
872 | ||
873 | (void) umask(022); | |
874 | sig_pipeignore(); | |
875 | when = now(); | |
876 | datetime_tai(&dt,when); | |
877 | ||
878 | while ((opt = getopt(argc,argv,"bBcCf:pPsSt:vV")) != opteof) | |
879 | switch (opt) { | |
880 | case 'b': flagbottom = 1; break; /* add text/bottom (default) */ | |
881 | case 'B': flagbottom = 0; break; /* suppress text/bottom */ | |
882 | case 'c': flagdo = 1; break; /* do commands */ | |
883 | case 'C': flagdo = 0; break; /* ignore commands X dig */ | |
884 | case 'f': if (FORMATS[str_chr(FORMATS,optarg[0])]) | |
885 | outformat = optarg[0]; | |
886 | break; | |
887 | case 'p': flagpublic = 1; break; /* always public */ | |
888 | case 'P': flagpublic = 0; break; /* never public = only mods do cmd*/ | |
889 | /* def = 2: respect DIR/public */ | |
890 | case 's': flagsub = 1; break; /* only subs have archive access */ | |
891 | case 'S': flagsub = 0; break; /* everyone has archive access */ | |
892 | case 'v': | |
893 | case 'V': strerr_die2x(0,"ezmlm-get version: ",EZIDX_VERSION); | |
894 | default: | |
895 | die_usage(); | |
896 | } | |
897 | ||
898 | dir = argv[optind++]; | |
899 | if (!dir) die_usage(); | |
900 | if (chdir(dir) == -1) | |
901 | strerr_die4x(111,FATAL,ERR_SWITCH,dir,": "); | |
902 | ||
903 | digestcode = argv[optind]; /* code to activate digest (-digest-code)*/ | |
904 | /* ignore any extra args */ | |
905 | ||
906 | getconf_line(&outlocal,"outlocal",1,FATAL,dir); | |
907 | if (!stralloc_copy(&subject,&outlocal)) die_nomem(); /* for subjects */ | |
908 | if (!stralloc_copy(&listname,&outlocal)) die_nomem(); /* for content disp */ | |
909 | ||
910 | local = env_get("LOCAL"); | |
911 | def = env_get("DEFAULT"); | |
912 | sender = env_get("SENDER"); | |
913 | if (local && *local) { /* in editor local = inlocal */ | |
914 | if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER); | |
915 | if (!*sender) | |
916 | strerr_die2x(100,FATAL,ERR_BOUNCE); | |
917 | if (str_equal(sender,"#@[]")) | |
918 | strerr_die2x(100,FATAL,ERR_BOUNCE); | |
919 | if (!sender[str_chr(sender,'@')]) | |
920 | strerr_die2x(100,FATAL,ERR_ANONYMOUS); | |
921 | if (def) { /* qmail>=1.02 support only */ | |
922 | if (*def) { | |
923 | action = def; | |
924 | goodexit = 99; | |
925 | } else | |
926 | _exit(0); /* list-@host should do -help from manager */ | |
927 | } else { /* editor */ | |
928 | act = AC_DIGEST; /* on list-@host ! */ | |
929 | flageditor = 1; /* to avoid Mailing-list error on sublists */ | |
930 | /* when running out of dir/editor. */ | |
931 | } | |
932 | if (case_starts(action,"dig")) { | |
933 | action += 3; | |
934 | if (action[0] == '-' || action [0] == '.') { | |
935 | action++; | |
936 | if (!digestcode) | |
937 | strerr_die2x(100,FATAL,ERR_BAD_DIGCODE); | |
938 | len = str_len(digestcode); | |
939 | if (len <= str_len(action) && case_startb(action,len,digestcode)) { | |
940 | if (FORMATS[str_chr(FORMATS,*(action+len))]) | |
941 | outformat = *(action+len); | |
942 | act = AC_DIGEST; | |
943 | } else | |
944 | strerr_die2x(100,FATAL,ERR_BAD_DIGCODE); | |
945 | } | |
946 | } | |
947 | } else /* Command line operation */ | |
948 | act = AC_DIGEST; | |
949 | ||
950 | /* Things we deal with. If anything else just die with success! */ | |
951 | /* At the moment this is -index, -thread, and -get. */ | |
952 | /* If flagdo = 0 we only service -dig commands. This is to support*/ | |
953 | /* "secret" lists that are still archived and digested. -c on */ | |
954 | /* cmd line. */ | |
955 | ||
956 | if (act == AC_NONE) { | |
957 | if (case_equals(action,ACTION_DIGEST)) { | |
958 | act = AC_GET; /* list-digest@ => msg since last digest */ | |
959 | action = ACTION_GET; | |
960 | } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET)) | |
961 | act = AC_GET; | |
962 | else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX)) | |
963 | act = AC_INDEX; | |
964 | else if (case_starts(action,ACTION_THREAD) || | |
965 | case_starts(action,ALT_THREAD)) | |
966 | act = AC_THREAD; | |
967 | } | |
968 | if (act == AC_NONE) /* not for us. Pass the buck. */ | |
969 | _exit(0); | |
970 | if (act != AC_INDEX) { /* need to do header processing */ | |
971 | if(!getconf(&digheaders,"digheaders",0,FATAL,dir)) { | |
972 | if(!stralloc_copys(&digheaders,digsz)) die_nomem(); | |
973 | if (!stralloc_0(&digheaders)) die_nomem(); | |
974 | psz = digheaders.s; | |
975 | while (*psz) { | |
976 | if (*psz == '\\') *psz = '\0'; | |
977 | ++psz; | |
978 | } | |
979 | } | |
980 | if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0)) | |
981 | die_nomem(); | |
982 | } | |
983 | if (act != AC_DIGEST) { | |
984 | if (!flagdo) /* only do digests */ | |
985 | strerr_die2x(100,FATAL,ERR_NOCMD); | |
986 | if (flagpublic == 2) | |
987 | flagpublic = getconf_line(&line,"public",0,FATAL,dir); | |
988 | if (!flagpublic) { | |
989 | /* This all to take care of non-public lists. They should*/ | |
990 | /* still do digests, but do other things only for */ | |
991 | /* moderators that have remote access. Since this is rare*/ | |
992 | /* efforts have been made to keep everything that's not */ | |
993 | /* needed elsewhere in here. */ | |
994 | getconf_line(&moddir,"modsub",0,FATAL,dir); | |
995 | flagremote = getconf_line(&line,"remote",0,FATAL,dir); | |
996 | if (!flagremote) | |
997 | strerr_die2x(100,FATAL,ERR_NOT_PUBLIC); | |
998 | if (!moddir.len || moddir.s[0] != '/') { | |
999 | if (line.len && line.s[0] == '/') { | |
1000 | if (!stralloc_copy(&moddir,&line)) die_nomem(); | |
1001 | } else { | |
1002 | if (!stralloc_copys(&moddir,dir)) die_nomem(); | |
1003 | if (!stralloc_cats(&moddir,"/mod")) die_nomem(); | |
1004 | } | |
1005 | } | |
1006 | if (!stralloc_0(&moddir)) die_nomem(); | |
1007 | pmod = issub(moddir.s,sender,(char *) 0,FATAL); | |
1008 | if (!pmod) /* sender = moderator? */ | |
1009 | strerr_die2x(100,FATAL,ERR_NOT_PUBLIC); | |
1010 | else { | |
1011 | if (!stralloc_copys(&mod,pmod)) die_nomem(); | |
1012 | if (!stralloc_0(&mod)) die_nomem(); | |
1013 | pmod = mod.s; /* send to address in list not matching bait */ | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | flagindexed = getconf_line(&line,"indexed",0,FATAL,dir); | |
1019 | flagarchived = getconf_line(&line,"archived",0,FATAL,dir); | |
1020 | ||
1021 | if (getconf_line(&charset,"charset",0,FATAL,dir)) { | |
1022 | if (charset.len >= 2 && charset.s[charset.len - 2] == ':') { | |
1023 | if (charset.s[charset.len - 1] == 'B' || | |
1024 | charset.s[charset.len - 1] == 'Q') { | |
1025 | flagcd = charset.s[charset.len - 1]; | |
1026 | charset.s[charset.len - 2] = '\0'; | |
1027 | } | |
1028 | } | |
1029 | } else | |
1030 | if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem(); | |
1031 | if (!stralloc_0(&charset)) die_nomem(); | |
1032 | getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); | |
1033 | getconf_line(&outhost,"outhost",1,FATAL,dir); | |
1034 | set_cpouthost(&outhost); | |
1035 | ||
1036 | if (!stralloc_copys(&ddir,dir)) die_nomem(); | |
1037 | if (!stralloc_cats(&ddir,"/digest")) die_nomem(); | |
1038 | if (!stralloc_0(&ddir)) die_nomem(); | |
1039 | if (act == AC_DIGEST) { | |
1040 | workdir = ddir.s; | |
1041 | if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); | |
1042 | if (getconf_line(&line,"chunk",0,FATAL,dir)) { | |
1043 | if (!stralloc_0(&line)) die_nomem(); | |
1044 | (void) scan_ulong(line.s,&chunk); /* same chunk as main list */ | |
1045 | if (chunk == 0) /* limit range to 1-53 */ | |
1046 | chunk = 1L; | |
1047 | else if (chunk > 52) | |
1048 | chunk = 52L; | |
1049 | } else { | |
1050 | chunk = 0L; /* maybe direct qmqp? */ | |
1051 | if (!stralloc_copys(&line,QMQPSERVERS)) die_nomem(); | |
1052 | if (!stralloc_cats(&line,"/0")) die_nomem(); | |
1053 | if (!stralloc_0(&line)) die_nomem(); | |
1054 | flagqmqp = getconf(&qmqpservers,line.s,0,FATAL,dir); | |
1055 | } | |
1056 | } else | |
1057 | workdir = dir; | |
1058 | ||
1059 | if (!stralloc_copys(&edir,dir)) die_nomem(); /* not needed for -dig, but */ | |
1060 | if (!stralloc_cats(&edir,"/allow")) die_nomem(); /* be safe */ | |
1061 | if (!stralloc_0(&edir)) die_nomem(); | |
1062 | set_cpoutlocal(&outlocal); /* needed for copy */ | |
1063 | ||
1064 | if (flagqmqp) { | |
1065 | if (qmail_open(&qq,&qmqpservers) == -1) /* open qmail */ | |
1066 | strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); | |
1067 | } else if (qmail_open(&qq,(stralloc *) 0) == -1) /* open qmail */ | |
1068 | strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); | |
1069 | ||
1070 | set_cpnum(""); /* default for <#n#> replacement */ | |
1071 | ||
1072 | if (act == AC_DIGEST) { | |
1073 | /* -dig{.|-}'digestcode'[f] returns an rfc1153 digest */ | |
1074 | /* of messages from the archive. Messages */ | |
1075 | /* dignum+1 through the last message received by the list are processed and */ | |
1076 | /* dignum is updated to the last message processed. digissue is advanced. */ | |
1077 | ||
1078 | if (!flagarchived) | |
1079 | strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED); | |
1080 | ||
1081 | get_num(); /* max = last successful message */ | |
1082 | to = max; | |
1083 | lockup(); /* another digest could corrupt dignum */ | |
1084 | /* but will be saved only if flagdigrange==0 */ | |
1085 | if(getconf_line(&num,"dignum",0,FATAL,dir)) { | |
1086 | if(!stralloc_0(&num)) die_nomem(); | |
1087 | pos = scan_ulong(num.s,&prevmax); | |
1088 | if (num.s[pos] == ':') pos++; | |
1089 | pos += 1 + scan_ulong(num.s+pos,&cumsize); /* last cumsize */ | |
1090 | if (num.s[pos] == ':') pos++; | |
1091 | scan_ulong(num.s+pos,&digwhen); /* last reg dig */ | |
1092 | } else { | |
1093 | prevmax = 0L; | |
1094 | cumsize = 0L; | |
1095 | digwhen = 0L; | |
1096 | } | |
1097 | mno = prevmax + 1L; | |
1098 | if(!max || mno > max) /* if a digest-list is "sending" the request, */ | |
1099 | /* don't make noise: errors go to postmaster!*/ | |
1100 | strerr_die2x(goodexit,FATAL,ERR_EMPTY_DIGEST); | |
1101 | szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0'; | |
1102 | set_cpnum(szmsgnum); /* for copy */ | |
1103 | /* prepare subject to get entropy for tagmsg*/ | |
1104 | if (!stralloc_cats(&subject," Digest ")) die_nomem(); | |
1105 | if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1)) | |
1106 | die_nomem(); /* skip trailing in date '\n' */ | |
1107 | if (!stralloc_cats(&subject," Issue ")) die_nomem(); | |
1108 | if (getconf_line(&num,"digissue",0,FATAL,dir)) { | |
1109 | if(!stralloc_0(&num)) die_nomem(); | |
1110 | scan_ulong(num.s,&issue); | |
1111 | issue++; | |
1112 | } else { | |
1113 | issue = 1; | |
1114 | } | |
1115 | if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue))) | |
1116 | die_nomem(); | |
1117 | /* use the subject as entropy */ | |
1118 | if (!stralloc_copy(&line,&subject)) die_nomem(); | |
1119 | if (!stralloc_0(&line)) die_nomem(); | |
1120 | ||
1121 | if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem(); | |
1122 | seed.len = HASHLEN + 1; | |
1123 | seed.s[HASHLEN] = '\0'; | |
1124 | makehash(line.s,line.len,seed.s); | |
1125 | if (chunk) { /* only if slaves are used */ | |
1126 | qmail_puts(&qq,"Ezauth: "); | |
1127 | qmail_put(&qq,seed.s,HASHLEN); | |
1128 | qmail_puts(&qq,"\n"); | |
1129 | } | |
1130 | ||
1131 | doheaders(); | |
1132 | qmail_puts(&qq,"To: "); | |
1133 | if (!quote("ed,&listname)) die_nomem(); | |
1134 | qmail_put(&qq,quoted.s,quoted.len); | |
1135 | qmail_puts(&qq,"@"); | |
1136 | qmail_put(&qq,outhost.s,outhost.len); | |
1137 | qmail_puts(&qq,"\n"); | |
1138 | if (flagindexed && (outformat != NATIVE)) | |
1139 | idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, | |
1140 | mno,to,max,flaglocked,FATAL); | |
1141 | else | |
1142 | idx_mklist(&msgtable,&subtable,&authtable,mno,to,FATAL); | |
1143 | digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat); | |
1144 | ||
1145 | write_ulong(issue,0L,0L,"digissue","digissuen"); | |
1146 | write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn"); | |
1147 | } | |
1148 | ||
1149 | else if (act == AC_GET) { | |
1150 | ||
1151 | /* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted */ | |
1152 | /* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET. */ | |
1153 | ||
1154 | if (!flagarchived) | |
1155 | strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED); | |
1156 | zapnonsub(ACTION_GET); /* restrict to subs if requested */ | |
1157 | tosender(); | |
1158 | /* for rfc1153 */ | |
1159 | if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); | |
1160 | if (!stralloc_cats(&subject,action)) die_nomem(); | |
1161 | ||
1162 | to = 0; | |
1163 | pos = str_len(ACTION_GET); | |
1164 | if (!case_starts(action,ACTION_GET)) | |
1165 | pos = str_len(ALT_GET); | |
1166 | if (FORMATS[str_chr(FORMATS,action[pos])]) { | |
1167 | outformat = action[pos]; | |
1168 | ++pos; | |
1169 | } | |
1170 | /* optional - or . after '-get' */ | |
1171 | if (action[pos] == '-' || action[pos] == '.') pos++; | |
1172 | get_num(); /* max = last successful message */ | |
1173 | /* accept any separator. It may be */ | |
1174 | /* the terminal '\n', but then */ | |
1175 | /* scan will = 0 on the \0 so should*/ | |
1176 | /* be safe */ | |
1177 | if (!max) | |
1178 | strerr_die2x(100,FATAL,ERR_EMPTY_LIST); | |
1179 | szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; | |
1180 | set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ | |
1181 | doheaders(); | |
1182 | if(action[pos += scan_ulong(action + pos,&u)]) | |
1183 | scan_ulong(action + pos + 1, &to); | |
1184 | if (u == 0 && to == 0) { /* default: messages since last */ | |
1185 | /* digest, or last MAXGET if too many */ | |
1186 | to= max; | |
1187 | u = dignum(); | |
1188 | if (u == 0) { /* no digest => last up to HISTGET msgs */ | |
1189 | to = max; | |
1190 | if (max > HISTGET) u = max - HISTGET; else u = 1; | |
1191 | } | |
1192 | if (to - u >= MAXGET) u = to - MAXGET + 1; /* max MAXGET */ | |
1193 | } else if (u > max) { | |
1194 | if (to) { /* -get.999999_x returns 30 and msg since last*/ | |
1195 | to = max; /* digest 30*/ | |
1196 | u = dignum(); | |
1197 | if (u > HISTGET) u -= HISTGET; else u = 1; | |
1198 | if (to - u >= MAXGET) u = to - MAXGET + 1; | |
1199 | } else | |
1200 | u = max; | |
1201 | } | |
1202 | if (u == 0) u = 1; /* -get.5 => 1-5 */ | |
1203 | if (to < u) to = u; /* -get23_2 => 23 */ | |
1204 | if (to >= u + MAXGET) to = u + MAXGET - 1; | |
1205 | /* no more than MAXGET at a time */ | |
1206 | if (to > max) to = max; | |
1207 | if (flagindexed && (outformat != NATIVE)) /* fake out threading */ | |
1208 | idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, | |
1209 | u,to,max,0,FATAL); | |
1210 | else | |
1211 | idx_mklist(&msgtable,&subtable,&authtable,u,to,FATAL); | |
1212 | digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat); | |
1213 | } | |
1214 | ||
1215 | else if (act == AC_INDEX) { | |
1216 | ||
1217 | /* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/ | |
1218 | /* sets of 100. */ | |
1219 | /* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */ | |
1220 | /* limited to num+MAXINDEX to limit the amount of data sent. */ | |
1221 | ||
1222 | if (!flagindexed) | |
1223 | strerr_die2x(100,FATAL,ERR_NOT_INDEXED); | |
1224 | if (flagsub) | |
1225 | zapnonsub(ACTION_INDEX); /* restrict to subs if requested */ | |
1226 | to = 0; | |
1227 | pos = str_len(ACTION_INDEX); | |
1228 | if (!case_starts(action,ACTION_INDEX)) | |
1229 | pos = str_len(ALT_INDEX); | |
1230 | if (FORMATS[str_chr(FORMATS,action[pos])]) { | |
1231 | outformat = action[pos]; /* ignored, but be nice ... */ | |
1232 | ++pos; | |
1233 | } | |
1234 | get_num(); /* max = last successful message */ | |
1235 | if (!max) | |
1236 | strerr_die2x(100,FATAL,ERR_EMPTY_LIST); | |
1237 | szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; | |
1238 | set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ | |
1239 | ||
1240 | doheaders(); | |
1241 | tosender(); | |
1242 | if (!stralloc_cats(&subject," Result of: ")) die_nomem(); | |
1243 | if (!stralloc_cats(&subject,action)) die_nomem(); | |
1244 | presub(1,1,&subject,AC_INDEX,outformat); | |
1245 | ||
1246 | if (action[pos] == '-' || action[pos] == '.') pos++; | |
1247 | if(action[pos += scan_ulong(action + pos,&u)]) | |
1248 | scan_ulong(action + pos + 1, &to); | |
1249 | ||
1250 | if (u == 0 && to == 0) { to = max; u = max - 100; } | |
1251 | if (u <= 0) u = 1; | |
1252 | if (u > max) u = max; | |
1253 | if (to < u) to = u; | |
1254 | if (to > u + MAXINDEX) to = u+MAXINDEX; /* max MAXINDEX index files */ | |
1255 | if (to > max) to = max; | |
1256 | u /= 100; | |
1257 | to /= 100; | |
1258 | while (u <= to) { | |
1259 | if (!stralloc_copys(&fn,"archive/")) die_nomem(); | |
1260 | if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem(); | |
1261 | if (!stralloc_cats(&fn,"/index")) die_nomem(); | |
1262 | if (!stralloc_0(&fn)) die_nomem(); | |
1263 | ||
1264 | if (u == max/100) /* lock if last index file in archive */ | |
1265 | lockup(); | |
1266 | ||
1267 | fd = open_read(fn.s); | |
1268 | if (fd == -1) | |
1269 | if (errno != error_noent) | |
1270 | strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); | |
1271 | else | |
1272 | code_qput(TXT_NOINDEX,str_len(TXT_NOINDEX)); | |
1273 | else { | |
1274 | substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); | |
1275 | for (;;) { | |
1276 | if (getln(&sstext,&line,&match,'\n') == -1) | |
1277 | strerr_die4sys(111,FATAL,ERR_READ,fn.s,": "); | |
1278 | if (match) { | |
1279 | if (line.s[0] != '\t') { /* subject line */ | |
1280 | pos = byte_chr(line.s,line.len,' '); | |
1281 | if (pos && pos != line.len && line.s[pos - 1] == ':') | |
1282 | pos1 = pos + HASHLEN + 1; /* after hash */ | |
1283 | if (pos1 >= line.len) { /* bad! */ | |
1284 | pos = 0; | |
1285 | pos1 = 0; /* output as is */ | |
1286 | } | |
1287 | if (!stralloc_copyb(&line2,line.s,pos)) die_nomem(); | |
1288 | if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem(); | |
1289 | } else { | |
1290 | pos = byte_chr(line.s,line.len,';'); | |
1291 | if (pos + HASHLEN + 1 < line.len && pos > 15 && | |
1292 | line.s[pos + 1] != ' ') { | |
1293 | if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem(); | |
1294 | pos++; | |
1295 | if (!stralloc_catb(&line2,line.s + pos + HASHLEN, | |
1296 | line.len - pos - HASHLEN)) die_nomem(); | |
1297 | } else /* old format - no author hash */ | |
1298 | if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem(); | |
1299 | } | |
1300 | code_qput(line2.s,line2.len); | |
1301 | } else | |
1302 | break; | |
1303 | } | |
1304 | close(fd); | |
1305 | } | |
1306 | ||
1307 | if (u == max/100) /* unlock if last index in archive file */ | |
1308 | unlock(); | |
1309 | ||
1310 | u++; | |
1311 | } | |
1312 | normal_bottom(outformat); | |
1313 | postmsg(outformat); | |
1314 | } | |
1315 | ||
1316 | else if (act == AC_THREAD) { | |
1317 | ||
1318 | /* -thread[f][-|.]num returns messages with subject matching message */ | |
1319 | /* 'num' in the subject index. If 'num' is not in[1..last_message] an error */ | |
1320 | /* message is returned. */ | |
1321 | ||
1322 | if (!flagarchived) | |
1323 | strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED); | |
1324 | if (!flagindexed) | |
1325 | strerr_die2x(100,FATAL,ERR_NOT_INDEXED); | |
1326 | ||
1327 | zapnonsub(ACTION_THREAD); /* restrict to subs if requested*/ | |
1328 | ||
1329 | get_num(); /* max = last successful message */ | |
1330 | if (!max) | |
1331 | strerr_die2x(100,FATAL,ERR_EMPTY_LIST); | |
1332 | szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; | |
1333 | set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ | |
1334 | ||
1335 | doheaders(); | |
1336 | tosender(); | |
1337 | /* for rfc1153 */ | |
1338 | if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); | |
1339 | if (!stralloc_cats(&subject,action)) die_nomem(); | |
1340 | ||
1341 | to = 0; | |
1342 | pos = str_len(ACTION_THREAD); | |
1343 | if (!case_starts(action,ACTION_THREAD)) | |
1344 | pos = str_len(ALT_THREAD); | |
1345 | if (FORMATS[str_chr(FORMATS,action[pos])]) { | |
1346 | outformat = action[pos]; | |
1347 | ++pos; | |
1348 | } | |
1349 | if (action[pos] == '-' || action[pos] == '.') pos++; | |
1350 | if(action[pos += scan_ulong(action + pos,&u)]) | |
1351 | scan_ulong(action + pos + 1, &to); | |
1352 | ||
1353 | if(u == 0 || u > max) { | |
1354 | if (!stralloc_cats(&subject,"\n\n")) die_nomem(); | |
1355 | qmail_puts(&qq,"Subject: "); | |
1356 | qmail_put(&qq,subject.s,subject.len); | |
1357 | copy(&qq,"text/get-bad",flagcd,FATAL); | |
1358 | } else { /* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */ | |
1359 | if (u > THREAD_BEFORE) | |
1360 | from = u-THREAD_BEFORE; | |
1361 | else | |
1362 | from = 1L; | |
1363 | if (u + THREAD_AFTER > max) { | |
1364 | idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0,FATAL); | |
1365 | digest(msgtable,subtable,authtable,from,max,&subject, | |
1366 | AC_THREAD,outformat); | |
1367 | } else { | |
1368 | idx_mkthread(&msgtable,&subtable,&authtable, | |
1369 | from,u+THREAD_AFTER,u,max,0,FATAL); | |
1370 | digest(msgtable,subtable,authtable,from,u+THREAD_AFTER, | |
1371 | &subject,AC_THREAD,outformat); | |
1372 | } | |
1373 | } | |
1374 | } | |
1375 | ||
1376 | else | |
1377 | /* This happens if the initial check at the beginning of 'main' */ | |
1378 | /* matches something that isn't matched here. Conversely, just */ | |
1379 | /* adding an action here is not enough - it has to be added to the */ | |
1380 | /* initial check as well. */ | |
1381 | ||
1382 | strerr_die2x(100,FATAL, | |
1383 | "Program error: I'm supposed to deal with this but I didn't"); | |
1384 | ||
1385 | if (!stralloc_copy(&line,&outlocal)) die_nomem(); | |
1386 | if (act == AC_DIGEST) { | |
1387 | if (chunk) { | |
1388 | if (!stralloc_cats(&line,"-return-g-")) die_nomem(); | |
1389 | } else | |
1390 | if (!stralloc_cats(&line,"-return-")) die_nomem(); | |
1391 | strnum[fmt_ulong(strnum,mno)] = '\0'; | |
1392 | if (!stralloc_cats(&line,strnum)) die_nomem(); | |
1393 | if (!stralloc_cats(&line,"-@")) die_nomem(); | |
1394 | ||
1395 | if (!stralloc_cat(&line,&outhost)) die_nomem(); | |
1396 | if (!stralloc_cats(&line,"-@[]")) die_nomem(); | |
1397 | } else { | |
1398 | if (!stralloc_cats(&line,"-return-@")) die_nomem(); | |
1399 | if (!stralloc_cat(&line,&outhost)) die_nomem(); | |
1400 | } | |
1401 | if (!stralloc_0(&line)) die_nomem(); | |
1402 | ||
1403 | qmail_from(&qq,line.s); | |
1404 | if (act == AC_DIGEST) { /* Do recipients */ | |
1405 | tagmsg(workdir,mno,seed.s,"d",hashout,qq.msgbytes,chunk,FATAL); | |
1406 | if (chunk) { | |
1407 | if (!stralloc_copys(&line,"T")) die_nomem(); | |
1408 | if (!stralloc_cat(&line,&outlocal)) die_nomem(); | |
1409 | if (!stralloc_cats(&line,"-s-d-")) die_nomem(); | |
1410 | if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem(); | |
1411 | if (!stralloc_cats(&line,"-")) die_nomem(); | |
1412 | if (!stralloc_cats(&line,strnum)) die_nomem(); | |
1413 | if (!stralloc_cats(&line,"-")) die_nomem(); | |
1414 | if (!stralloc_copys(&line2,"@")) die_nomem(); | |
1415 | if (!stralloc_cat(&line2,&outhost)) die_nomem(); | |
1416 | if (!stralloc_0(&line2)) die_nomem(); | |
1417 | j = 0; | |
1418 | for (i = 0; i <= 52; i += chunk) { /* To slaves */ | |
1419 | qmail_put(&qq,line.s,line.len); | |
1420 | schar[0] = '0' + i / 10; | |
1421 | schar[1] = '0' + (i % 10); | |
1422 | qmail_put(&qq,schar,3); | |
1423 | j += (chunk - 1); | |
1424 | if (j > 52) j = 52; | |
1425 | schar[0] = '0' + j / 10; | |
1426 | schar[1] = '0' + (j % 10); | |
1427 | qmail_put(&qq,schar,2); | |
1428 | qmail_put(&qq,line2.s,line2.len); | |
1429 | } | |
1430 | } else | |
1431 | subs = putsubs(workdir,0L,52L,&subto,1,FATAL); | |
1432 | } else { /* if local is set, sender is checked */ | |
1433 | if (pmod) | |
1434 | qmail_to(&qq,pmod); | |
1435 | else | |
1436 | qmail_to(&qq,sender); | |
1437 | } | |
1438 | ||
1439 | if (*(err = qmail_close(&qq)) == '\0') { /* Done. Skip rest. */ | |
1440 | if (act == AC_DIGEST) { | |
1441 | if (chunk) | |
1442 | (void) logmsg(workdir,mno,0L,0L,2); | |
1443 | else | |
1444 | (void) logmsg(workdir,mno,0L,subs,4); | |
1445 | } | |
1446 | closesql(); /* close db connection */ | |
1447 | unlock(); /* NOP if nothing locked */ | |
1448 | strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; | |
1449 | strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum); | |
1450 | } else { /* failed. Reset last msg & issue for digest */ | |
1451 | if(act == AC_DIGEST) { | |
1452 | issue--; | |
1453 | write_ulong(issue,0L,0L,"digissue","digissuen"); | |
1454 | write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn"); | |
1455 | } | |
1456 | unlock(); /* NOP if nothing locked */ | |
1457 | strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1); | |
1458 | } | |
1459 | } |