Import ezmlm-idx 0.40
[ezmlm] / ezmlm-get.c
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(&quoted,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(&quoted,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(&quoted,&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(&quoted,&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 }