Import ezmlm-idx 0.40
[ezmlm] / ezmlm-request.c
1 /*$Id: ezmlm-request.c,v 1.34 1999/08/18 01:50:04 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
3 #include "stralloc.h"
4 #include "subfd.h"
5 #include "strerr.h"
6 #include "error.h"
7 #include "qmail.h"
8 #include "env.h"
9 #include "sig.h"
10 #include "open.h"
11 #include "getln.h"
12 #include "case.h"
13 #include "str.h"
14 #include "datetime.h"
15 #include "date822fmt.h"
16 #include "now.h"
17 #include "quote.h"
18 #include "readwrite.h"
19 #include "exit.h"
20 #include "substdio.h"
21 #include "getconf.h"
22 #include "constmap.h"
23 #include "fmt.h"
24 #include "sgetopt.h"
25 #include "byte.h"
26 #include "seek.h"
27 #include "errtxt.h"
28 #include "copy.h"
29 #include "idx.h"
30
31 #define FATAL "ezmlm-request: fatal: "
32 #define INFO "ezmlm-request: info: "
33
34 void die_usage()
35 {
36 strerr_die1x(100,"ezmlm-request: usage: ezmlm-request [-f lists.cfg] dir");
37 }
38
39 void die_nomem()
40 {
41 strerr_die2x(111,FATAL,ERR_NOMEM);
42 }
43
44 void die_badaddr()
45 {
46 strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
47 }
48
49 char strnum[FMT_ULONG];
50
51 void *psql = (void *) 0;
52
53 char *userlocal = (char *) 0;
54 char *userhost = (char *) 0;
55 char *listlocal = (char *) 0;
56 char *listhost = (char *) 0;
57 char *cfname = (char *) 0;
58 char *command = "help";
59 stralloc line = {0};
60 stralloc qline = {0};
61 stralloc usr = {0};
62 stralloc lhost = {0};
63 stralloc subject = {0};
64 stralloc inlocal = {0};
65 stralloc outlocal = {0};
66 stralloc listname = {0};
67 stralloc hostname = {0};
68 stralloc outhost = {0};
69 stralloc headerremove = {0};
70 stralloc mailinglist = {0};
71 stralloc cmds = {0};
72 stralloc from = {0};
73 stralloc to = {0};
74 stralloc charset = {0};
75 char *boundary = "zxcaeedrqcrtrvthbdty"; /* cheap "rnd" MIME boundary */
76 int flagcd = '\0'; /* no encoding by default */
77
78 struct constmap headerremovemap;
79 struct constmap commandmap;
80 int flaggotsub = 0; /* Found a subject */
81 /* cmdstring has all commands seperated by '\'. cmdxlate maps each */
82 /* command alias to the basic command, which is used to construct */
83 /* the command address (positive numbers) or handled by this */
84 /* program (negative numbers). Note: Any command not matched is */
85 /* used to make a command address, so ezmlm request can handle */
86 /* ("transmit") user-added commands. */
87 const char *cmdstring =
88 "system\\help\\" /* 1,2 */
89 "subscribe\\unsubscribe\\index\\" /* 3,4,5 */
90 "info\\list\\query\\" /* 6,7,8 */
91 "sub\\unsub\\remove\\signoff\\" /* 9,10,11,12 */
92 "lists\\which\\" /* 13,14 */
93 "ind\\rev\\review\\recipients\\" /* 15,16,17,18 */
94 "who\\showdist\\" /* 19,20 */
95 "put\\set"; /* 21,22 */
96
97 /* map aliases. -> 0 not recognized. -> 1 recognized will be made */
98 /* help and arguments scrapped. < 0 handled locally. HELP without */
99 /* args also handled locally */
100 /* the last are not supported -> help */
101 const int cmdxlate[] = { 0,1,2,3,4,5,6,7,8,3,4,4,4,-13,-14,5,7,7,7,7,7,
102 1,1 };
103
104 /* If there are no arguments (listlocal = 0) then commands are mapped*/
105 /* through this. This way, help, list, query, ... can mean something */
106 /* here even though they have local funcions at the lists if used */
107 /* with arguments. (Made same lengh as cmdxlate in case of bugs.) */
108 /* Note: This is used ONLY for the global interface */
109 const int noargsxlate[] = { 0,1,-2,3,4,5,-2,-13,-14,9,10,11,12,13,14,15,16,17,
110 18,19,20,21,22 };
111
112 /* these need to be defined as the index of the corresponding */
113 /* commands. They are handled by ezmlm-request. NOTE: Help is >0! */
114 #define EZREQ_LISTS 13
115 #define EZREQ_WHICH 14
116 #define EZREQ_HELP 2
117 #define EZREQ_BAD 1
118
119 substdio sstext;
120 char textbuf[1024];
121 datetime_sec when;
122 struct datetime dt;
123 char date[DATE822FMT];
124
125 struct qmail qq;
126
127 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
128 {
129 qmail_put(&qq,buf,len);
130 return len;
131 }
132
133 char qqbuf[1];
134 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
135
136 char inbuf[1024];
137 substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
138 substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
139
140 substdio ssout;
141 char outbuf[1];
142
143 stralloc mydtline = {0};
144
145 void transferenc()
146 {
147 if (flagcd) {
148 qmail_puts(&qq,"\n--");
149 qmail_puts(&qq,boundary);
150 qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
151 qmail_puts(&qq,charset.s);
152 qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
153 if (flagcd == 'Q')
154 qmail_puts(&qq,"quoted-printable\n\n");
155 else
156 qmail_puts(&qq,"base64\n\n");
157 }
158 }
159
160 int code_qput(s,n)
161 char *s;
162 unsigned int n;
163 {
164 if (!flagcd)
165 qmail_put(&qq,s,n);
166 else {
167 if (flagcd == 'B')
168 encodeB(s,n,&qline,0,FATAL);
169 else
170 encodeQ(s,n,&qline,FATAL);
171 qmail_put(&qq,qline.s,qline.len);
172 }
173 return 0; /* always succeeds */
174 }
175
176 /* Checks the argument. Only us-ascii letters, numbers, ".+-_" are ok. */
177 /* NOTE: For addresses this is more restrictive than rfc821/822. */
178 void checkarg(s)
179 char *s;
180 {
181 register char *cp;
182 register char ch;
183 cp = s;
184 if (!cp) return; /* undef is ok */
185 while ((ch = *cp++)) {
186 if (ch >= 'a' && ch <= 'z')
187 continue; /* lc letters */
188 if (ch >= '0' && ch <='9') /* digits */
189 continue;
190 if (ch == '.' || ch == '-' || ch == '_' || ch == '+')
191 continue; /* ok chars */
192 if (ch >= 'A' && ch <= 'Z') continue; /* UC LETTERS */
193 strerr_die4x(100,ERR_NOT_CLEAN,": \"",s,"\"");
194 }
195 return;
196 }
197
198 /* parses line poited to by cp into sz:s as per: */
199 /* 1. listlocal-command-userlocal=userhost@listhost */
200 /* 2. command userlocal@userhost */
201 /* 3. command userlocal@userhost listlocal@listhost */
202 /* 4. command listlocal@listhost */
203 /* 5. command listlocal[@listhost] userlocal@userhost */
204 /* 6. which [userlocal@userhost] */
205 /* The first 3 are valid only if !cfname, i.e. -request operation and */
206 /* listlocal and listhost are always set to outlocal@outhost. Options */
207 /* 4-5 are for the global address (cfname is set). Here listhost is */
208 /* taken from the first list in *cfname matching listlocal, or set to */
209 /* outhost, if not specified. If specified, it's accepted if it matches */
210 /* a list in *cfname and silently set to outhost otherwise. Pointers to */
211 /* unspecified parts are set to NULL in this routine to be dealt with */
212 /* elsewhere. "Which" special argument order (6) is fixed elsewhere. */
213 /* If listhost is not given, "@outhost" is added. Absence of 'userhost' */
214 /* is accepted to allow commands that take arguments that are not */
215 /* addresses (e.g. -get12-34). */
216
217 void parseline(cp)
218 char *cp;
219
220 {
221 register char *cp1, *cp2;
222 char *cp3;
223
224 cp1 = cp;
225 while (*cp1) { /* make tabs into spaces */
226 if (*cp1 == '\t') *cp1 = ' ';
227 ++cp1;
228 }
229 /* NOTE: outlocal has '\0' added! */
230 if (outlocal.len < str_len(cp) && cp[outlocal.len -1] == '-' &&
231 case_starts(cp,outlocal.s)) { /* normal ezmlm cmd */
232 command = cp + outlocal.len; /* after the '-' */
233 listlocal = outlocal.s;
234 listhost = outhost.s;
235 cp1 = command;
236 while (*cp1 && *cp1 != '-') ++cp1; /* find next '-' */
237 if (*cp1) {
238 *cp1 = '\0';
239 userlocal = ++cp1; /* after '-' */
240 cp1 = cp1 + str_rchr(cp1,'@'); /* @ _or_ end */
241 *cp1 = '\0'; /* last '=' in userlocal */
242 cp1 = userlocal + str_rchr(userlocal,'=');
243 if (*cp1) { /* found '=' */
244 *cp1 = '\0'; /* zap */
245 userhost = cp1 + 1; /* char after '=' */
246 }
247 }
248 } else { /* '@' before ' ' means complete cmd */
249 if (str_chr(cp,'@') < str_chr(cp,' ')) /* addr where inlocal failed */
250 strerr_die2x(100,FATAL,ERR_REQ_LOCAL);
251 /* to match */
252 command = cp;
253 cp1 = cp + str_chr(cp,' ');
254 if (*cp1) {
255 *cp1++ = '\0';
256 while (*cp1 && *cp1 == ' ') ++cp1; /* skip spaces */
257 }
258 cp2 = 0;
259 if (*cp1) { /* argument */
260 cp2 = cp1 + str_chr(cp1,' ');
261 cp3 = cp2;
262 while (*cp2 && *cp2 == ' ') ++cp2; /* skip spaces */
263 *cp3 = '\0';
264
265 if (!*cp2)
266 cp2 = 0;
267 else {
268 cp3 = cp2 + str_chr(cp2,' ');
269 *cp3 = '\0';
270 }
271 } else
272 cp1 = 0;
273
274 if (!cfname && !cp2) { /* the single arg is user if we serve a */
275 cp2 = cp1; /* list. It's list if we serve "domo@" */
276 cp1 = 0;
277 }
278 if (cp2) {
279 userlocal = cp2;
280 cp2 += str_chr(cp2,'@');
281 if (*cp2) {
282 *cp2++ = '\0';
283 userhost = cp2;
284 }
285 }
286 if (cp1) {
287 listlocal = cp1;
288 cp1 += str_chr(cp1,'@');
289 if (*cp1) {
290 *cp1++ = '\0';
291 listhost = cp1;
292 }
293 }
294 }
295 checkarg(command); /* better safe than sorry */
296 checkarg(userlocal); checkarg(userhost);
297 checkarg(listlocal); checkarg(listhost);
298 }
299
300 void main(argc,argv)
301 int argc;
302 char **argv;
303 {
304 char *dir;
305 char *local;
306 char *action;
307 char *def;
308 char *sender;
309 char *psz;
310 char *err;
311 int cmdidx;
312 int flagsub;
313 int flagok;
314 int flagnosubject;
315 int match;
316 int flaginheader;
317 int flagbadfield;
318 int flagmultipart = 0;
319 int fd;
320 int opt;
321 unsigned int pos,pos1,len,last;
322
323 (void)umask(022);
324 sig_pipeignore();
325
326 while ((opt = getopt(argc,argv,"f:F:vV")) != opteof)
327 switch(opt) {
328 case 'F':
329 case 'f': if (optarg) cfname = optarg; break;
330 case 'v':
331 case 'V': strerr_die2x(0,"ezmlm-request version: ",EZIDX_VERSION);
332 default:
333 die_usage();
334 }
335
336 dir = argv[optind];
337 if (!dir) die_usage();
338
339 if (chdir(dir) == -1)
340 strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
341
342 /* do minimum to identify request for this program in case */
343 /* it's invoked in line with e.g. ezmlm-manage */
344
345 def = env_get("DEFAULT");
346 if (def) { /* qmail>=1.02 */
347 action = def;
348 } else if (cfname) { /* older qmail OR just list-mdomo */
349 local = env_get("LOCAL");
350 if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
351 len = str_len(local);
352 if (len >= 8 && !case_diffb(local + len - 8,8,"-return-")) {
353 action = "return-"; /* our bounce with qmail<1.02 */
354 } else
355 action = ""; /* list-mdomo-xxx won't work for older lists */
356 } else { /* older qmail versions */
357 local = env_get("LOCAL");
358 if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
359 getconf_line(&inlocal,"inlocal",1,FATAL,dir);
360 if (inlocal.len > str_len(local)) die_badaddr();
361 if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
362 action = local + inlocal.len;
363 if (*action)
364 if (*(action++) != '-') die_badaddr(); /* check anyway */
365 }
366 /* at this point action = "request" or "request-..." for std use; */
367 /* "" for majordomo@ */
368 if (!cfname) { /* expect request */
369 if (case_starts(action,ACTION_REQUEST))
370 action += str_len(ACTION_REQUEST);
371 else if (case_starts(action,ALT_REQUEST))
372 action += str_len(ALT_REQUEST);
373 else
374 _exit(0); /* not for us */
375 }
376 getconf_line(&outlocal,"outlocal",1,FATAL,dir);
377 getconf_line(&outhost,"outhost",1,FATAL,dir);
378
379 if (!stralloc_copy(&listname,&outlocal)) die_nomem();
380 if (!stralloc_copy(&hostname,&outhost)) die_nomem();
381 if (!stralloc_0(&outlocal)) die_nomem();
382 if (!stralloc_0(&outhost)) die_nomem();
383
384 sender = env_get("SENDER");
385 if (!sender) strerr_die2x(99,INFO,ERR_NOSENDER);
386 if (!*sender)
387 strerr_die2x(99,INFO,ERR_BOUNCE);
388 if (!sender[str_chr(sender,'@')])
389 strerr_die2x(99,INFO,ERR_ANONYMOUS);
390 if (str_equal(sender,"#@[]"))
391 strerr_die2x(99,INFO,ERR_BOUNCE);
392
393 getconf(&headerremove,"headerremove",1,FATAL,dir);
394 constmap_init(&headerremovemap,headerremove.s,headerremove.len,0);
395
396 if (!stralloc_copys(&mydtline,
397 "Delivered-To: request processor for ")) die_nomem();
398 if (!stralloc_cats(&mydtline,outlocal.s)) die_nomem();
399 if (!stralloc_cats(&mydtline,"@")) die_nomem();
400 if (!stralloc_cats(&mydtline,outhost.s)) die_nomem();
401 if (!stralloc_cats(&mydtline,"\n")) die_nomem();
402
403 flagnosubject = 1;
404 if (action[0]) { /* mainly to allow ezmlm-lists or ezmlm-which with */
405 flagnosubject = 0; /* a command address rather than a complete msg */
406 command = action;
407 if (str_start(action,"return")) /* kill bounces */
408 strerr_die2x(0,INFO,ERR_BOUNCE);
409 pos = 1 + str_chr(action + 1,'-');
410 if (action[pos]) { /* start of target */
411 action[pos] = '\0';
412 userlocal = action + pos + 1;
413 pos = str_rchr(userlocal,'='); /* the "pseudo-@" */
414 if (userlocal[pos]) {
415 userlocal[pos] = '\0';
416 userhost = userlocal + pos + 1;
417 }
418 }
419 } else {
420 for (;;) { /* Get Subject: */
421 if (getln(&ssin,&line,&match,'\n') == -1)
422 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
423 if (line.len == 1)
424 break;
425 if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
426 flagsub = 0;
427
428 if (case_startb(line.s,line.len,"mailing-list:"))
429 strerr_die2x(100,FATAL,ERR_MAILING_LIST);
430 else if (case_startb(line.s,line.len,"Subject:")) {
431 flaggotsub = 1;
432 pos = 8;
433 last = line.len - 2; /* skip terminal '\n' */
434 while (line.s[last] == ' ' || line.s[last] == '\t') --last;
435 while (pos <= last &&
436 (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
437 if (!stralloc_copyb(&subject,line.s+pos,last-pos+1)) die_nomem();
438 } else if (case_startb(line.s,line.len,"content-type:")) {
439 pos = 13; last = line.len - 2; /* not cont-line - ok */
440 while (pos <= last &&
441 (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
442 if (case_startb(line.s+pos,line.len - pos,"multipart/"))
443 flagmultipart = 1;
444 } else if (line.len == mydtline.len)
445 if (!byte_diff(line.s,line.len,mydtline.s))
446 strerr_die2x(100,FATAL,ERR_LOOPING);
447 } else if (flagsub) { /* Continuation line */
448 pos = 1;
449 len = line.len - 2; /* skip terminal '\n' */
450 while (line.s[len] == ' ' || line.s[len] == '\t') --len;
451 while (pos < len &&
452 (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
453 if (!stralloc_append(&subject," ")) die_nomem();
454 if (!stralloc_copy(&subject,line.s+pos,len-pos+1)) die_nomem();
455 }
456 if (!match)
457 break;
458 }
459 if (!cfname) { /* listserv@/majordomo@ ignore */
460 register char ch;
461 if (!stralloc_0(&subject)) die_nomem();
462 ch = *subject.s; /* valid commands/list names start w letter */
463 if ((ch <= 'z' && ch >= 'a') || (ch <= 'Z' && ch >= 'A')) {
464 parseline(subject.s);
465 flagnosubject = 0;
466 }
467 }
468 if (cfname || flagnosubject) {
469 for (;;) { /* parse body */
470 if (getln(&ssin,&line,&match,'\n') == -1)
471 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
472 if (!match) break;
473 if (line.len == 1 && flagmultipart != 2) continue;
474 /* lazy MIME cludge assumes first '--...' is start border */
475 /* which is virtually always true */
476 if (flagmultipart == 1) { /* skip to first border */
477 if (*line.s != '-' || line.s[1] != '-') continue;
478 flagmultipart = 2;
479 continue;
480 } else if (flagmultipart == 2) { /* skip content info */
481 if (line.len != 1) continue;
482 flagmultipart = 3; /* may be part within part */
483 continue; /* and blank line */
484 } else if (flagmultipart == 3) {
485 if (*line.s == '-' && line.s[1] == '-') {
486 flagmultipart = 2; /* part within part */
487 continue;
488 }
489 }
490 {
491 register char ch;
492 ch = *line.s;
493 if (line.len == 1 ||
494 !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
495 continue; /* skip if not letter pos 1 */
496 }
497 /* Here we have a body line with something */
498 if (!stralloc_copy(&subject,&line)) die_nomem(); /* save it */
499 subject.s[subject.len-1] = '\0';
500 parseline(subject.s);
501 break;
502 }
503 }
504 }
505 /* Do command substitution */
506 if (!stralloc_copys(&cmds,cmdstring)) die_nomem();
507 if (!stralloc_0(&cmds)) die_nomem();
508 psz = cmds.s;
509 while (*psz) {
510 if (*psz == '\\') *psz = '\0';
511 ++psz;
512 }
513 if (!constmap_init(&commandmap,cmds.s,cmds.len,0)) die_nomem();
514 cmdidx = cmdxlate[constmap_index(&commandmap,command,str_len(command))];
515 if (cmdidx == EZREQ_BAD) { /* recognized, but not supported -> help */
516 listlocal = 0; /* needed 'cause arguments are who-knows-what */
517 listhost = 0;
518 userlocal = 0;
519 userhost = 0;
520 cmdidx = EZREQ_HELP;
521 }
522 if (cfname && !listlocal && !userlocal && cmdidx > 0)
523 cmdidx = noargsxlate[cmdidx]; /* some done differently if no args */
524
525 /* =0 not found. This is treated as a list command! */
526 if (cmdidx < 0 && !cfname) {
527 cmdidx = EZREQ_HELP;
528 }
529 if (qmail_open(&qq,(stralloc *) 0) == -1)
530 strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
531
532 if (cmdidx >= 0) {
533 /* Things handled elsewhere. We do want to handle a simple HELP */
534 /* without arguments for e.g. majordomo@ from our own help file */
535
536 if (!stralloc_copys(&from,sender)) die_nomem();
537 if (!stralloc_0(&from)) die_nomem();
538 if (!listlocal) {
539 if (cfname)
540 strerr_die1x(100,ERR_REQ_LISTNAME);
541 else
542 listlocal = outlocal.s; /* This is at the -request address */
543 }
544 /* if !cfname listhost is made outhost. If cfname, listhost=outhost */
545 /* is ok. listhost=0 => first match in config. Other listhost is ok */
546 /* only if match is found. Otherwise it's set to outhost. */
547
548 if (!cfname || (listhost && !case_diffs(listhost,outhost.s)))
549 listhost = outhost.s;
550 else { /* Check listhost against config file */
551 pos = str_len(listlocal);
552 fd = open_read(cfname);
553 if (fd == -1)
554 strerr_die4sys(111,FATAL,ERR_OPEN,cfname,": ");
555 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
556 flagok = 0; /* got listhost match */
557 for (;;) {
558 if (getln(&sstext,&line,&match,'\n') == -1)
559 strerr_die3sys(111,FATAL,ERR_READ,cfname);
560 if (!match)
561 break;
562 if (line.len <= 1 || line.s[0] == '#')
563 continue;
564 if ((pos < line.len) && (line.s[pos] == '@') &&
565 !byte_diff(line.s,pos,listlocal)) {
566 last = byte_chr(line.s,line.len,':');
567 if (!stralloc_copyb(&lhost,line.s+pos+1,last-pos-1)) die_nomem();
568 if (!stralloc_0(&lhost)) die_nomem();
569 if (listhost) {
570 if (!case_diffs(listhost,lhost.s)) {
571 flagok = 1;
572 break; /* host did match */
573 } else
574 continue; /* host didn't match */
575 } else { /* none given - grab first */
576 listhost = lhost.s;
577 flagok = 1;
578 break;
579 }
580 }
581 }
582 if (!flagok)
583 listhost = outhost.s;
584 close(fd);
585 }
586 if (!listhost)
587 listhost = outhost.s;
588 if (!userlocal) {
589 if (!stralloc_copys(&usr,sender)) die_nomem();
590 if (!stralloc_0(&usr)) die_nomem();
591 userlocal = usr.s;
592 userhost = usr.s + byte_rchr(usr.s,usr.len-1,'@');
593 if (!*userhost)
594 userhost = 0;
595 else {
596 *userhost = '\0';
597 ++userhost;
598 }
599 }
600
601 if (!stralloc_copys(&to,listlocal)) die_nomem();
602 if (!stralloc_cats(&to,"-")) die_nomem();
603 if (cmdidx) { /* recognized - substitute */
604 if (!stralloc_cats(&to,constmap_get(&commandmap,cmdidx)))
605 die_nomem();
606 } else /* not recognized - use as is */
607 if (!stralloc_cats(&to,command)) die_nomem();
608
609 if (!stralloc_cats(&to,"-")) die_nomem();
610 if (!stralloc_cats(&to,userlocal)) die_nomem();
611 if (userhost) { /* doesn't exist for e.g. -get */
612 if (!stralloc_cats(&to,"=")) die_nomem();
613 if (!stralloc_cats(&to,userhost)) die_nomem();
614 }
615 if (!stralloc_cats(&to,"@")) die_nomem();
616 if (!stralloc_cats(&to,listhost)) die_nomem();
617 if (!stralloc_0(&to)) die_nomem();
618
619 qmail_put(&qq,mydtline.s,mydtline.len);
620
621 flaginheader = 1;
622 flagbadfield = 0;
623
624 if (seek_begin(0) == -1)
625 strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
626 substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
627
628 for (;;) {
629 if (getln(&ssin,&line,&match,'\n') == -1)
630 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
631
632 if (flaginheader && match) {
633 if (line.len == 1)
634 flaginheader = 0;
635 if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
636 flagbadfield = 0;
637 if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
638 flagbadfield = 1;
639 }
640 }
641 if (!(flaginheader && flagbadfield))
642 qmail_put(&qq,line.s,line.len);
643 if (!match)
644 break;
645 }
646 } else { /* commands we deal with */
647 cmdidx = - cmdidx; /* now positive */
648 if (cmdidx == EZREQ_WHICH) { /* arg is user, not list */
649 userlocal = listlocal; listlocal = 0;
650 userhost = listhost; listhost = 0;
651 }
652 if (!stralloc_copys(&from,outlocal.s)) die_nomem();
653 if (!stralloc_cats(&from,"-return-@")) die_nomem();
654 if (!stralloc_cats(&from,outhost.s)) die_nomem();
655 if (!stralloc_0(&from)) die_nomem();
656
657 if (userlocal) {
658 if (!stralloc_copys(&to,userlocal)) die_nomem();
659 if (!stralloc_cats(&to,"@")) die_nomem();
660 if (userhost) {
661 if (!stralloc_cats(&to,userhost)) die_nomem();
662 } else {
663 if (!stralloc_cats(&to,outhost.s)) die_nomem();
664 }
665 } else
666 if (!stralloc_copys(&to,sender)) die_nomem();
667 if (!stralloc_0(&to)) die_nomem();
668
669 /* now we need to look for charset and set flagcd appropriately */
670
671 if (getconf_line(&charset,"charset",0,FATAL,dir)) {
672 if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
673 if (charset.s[charset.len - 1] == 'B' ||
674 charset.s[charset.len - 1] == 'Q') {
675 flagcd = charset.s[charset.len - 1];
676 charset.s[charset.len - 2] = '\0';
677 }
678 }
679 } else
680 if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
681 if (!stralloc_0(&charset)) die_nomem();
682 set_cpoutlocal(&listname); /* necessary in case there are <#l#> */
683 set_cpouthost(&hostname); /* necessary in case there are <#h#> */
684 /* we don't want to be send to a list*/
685 qmail_puts(&qq,"Mailing-List: ezmlm-request");
686 if (getconf(&line,"listid",0,FATAL)) {
687 qmail_puts(&qq,"List-ID: ");
688 qmail_put(&qq,line.s,line.len);
689 }
690 qmail_puts(&qq,"\nDate: ");
691 when = now();
692 datetime_tai(&dt,when);
693 qmail_put(&qq,date,date822fmt(date,&dt));
694 qmail_puts(&qq,"Message-ID: <");
695 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when)))
696 die_nomem();
697 if (!stralloc_append(&line,".")) die_nomem();
698 if (!stralloc_catb(&line,strnum,
699 fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
700 if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
701 if (!stralloc_cats(&line,outhost.s)) die_nomem();
702 if (!stralloc_0(&line)) die_nomem();
703 qmail_puts(&qq,line.s);
704 qmail_puts(&qq,">\nFrom: ");
705 if (!quote2(&line,outlocal.s)) die_nomem();
706 qmail_put(&qq,line.s,line.len);
707 if (cmdidx == EZREQ_HELP)
708 qmail_puts(&qq,"-return-@");
709 else
710 qmail_puts(&qq,"-help@");
711 qmail_puts(&qq,outhost.s);
712 qmail_puts(&qq,"\n");
713 qmail_put(&qq,mydtline.s,mydtline.len);
714 qmail_puts(&qq,"To: ");
715 if (!quote2(&line,to.s)) die_nomem();
716 qmail_put(&qq,line.s,line.len);
717 qmail_puts(&qq,"\n");
718 qmail_puts(&qq,"MIME-Version: 1.0\n");
719 if (flagcd) {
720 qmail_puts(&qq,"Content-Type: multipart/mixed; charset=");
721 qmail_puts(&qq,charset.s);
722 qmail_puts(&qq,";\n\tboundary=");
723 qmail_puts(&qq,boundary);
724 } else {
725 qmail_puts(&qq,"Content-type: text/plain; charset=");
726 qmail_puts(&qq,charset.s);
727 }
728 qmail_puts(&qq,"\nSubject: ");
729 if (!quote2(&line,outlocal.s)) die_nomem();
730 qmail_put(&qq,line.s,line.len);
731 qmail_puts(&qq,TXT_RESULTS);
732 transferenc();
733 copy(&qq,"text/top",flagcd,FATAL);
734 if (cmdidx == EZREQ_LISTS || cmdidx == EZREQ_WHICH) {
735 switch (cmdidx) {
736 case EZREQ_LISTS:
737 code_qput("LISTS:",6);
738 break;
739 case EZREQ_WHICH:
740 code_qput("WHICH (",7);
741 code_qput(to.s,to.len - 1);
742 code_qput("):\n\n",4);
743 break;
744 default: break;
745 }
746 fd = open_read(cfname);
747 if (fd == -1)
748 strerr_die4sys(111,FATAL,ERR_OPEN,cfname,": ");
749 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
750 for (;;) {
751 if (getln(&sstext,&line,&match,'\n') == -1)
752 strerr_die3sys(111,FATAL,ERR_READ,cfname);
753 if (!match)
754 break;
755 if (line.len <= 1 || line.s[0] == '#')
756 continue;
757 if (!stralloc_0(&line)) die_nomem();
758 pos = str_chr(line.s,':');
759 if (!line.s[pos])
760 break;
761 line.s[pos] = '\0';
762 ++pos;
763 pos1 = pos + str_chr(line.s + pos,':');
764 if (line.s[pos1]) {
765 line.s[pos1] = '\0';
766 ++pos1;
767 } else
768 pos1 = 0;
769
770 switch (cmdidx) {
771 case EZREQ_LISTS:
772 code_qput("\n\n\t",3);
773 code_qput(line.s,pos-1);
774 code_qput("\n",1);
775 if (pos1) {
776 code_qput(line.s+pos1,line.len-2-pos1);
777 }
778 break;
779 case EZREQ_WHICH:
780 if (issub(line.s+pos,to.s,(char *) 0,FATAL)) {
781 code_qput(line.s,pos-1);
782 code_qput("\n",1);
783 }
784 closesql(); /* likely different dbs for different lists */
785 break;
786 }
787 }
788 code_qput("\n",1);
789 close(fd);
790 } else
791 copy(&qq,"text/help",flagcd,FATAL);
792
793 copy(&qq,"text/bottom",flagcd,FATAL);
794 if (flagcd) {
795 if (flagcd == 'B') {
796 encodeB("",0,&line,2,FATAL); /* flush */
797 qmail_put(&qq,line.s,line.len);
798 }
799 qmail_puts(&qq,"\n--");
800 qmail_puts(&qq,boundary);
801 qmail_puts(&qq,"\nContent-Type: message/rfc822");
802 qmail_puts(&qq,
803 "\nContent-Disposition: inline; filename=request.msg\n\n");
804 }
805 qmail_puts(&qq,"Return-Path: <");
806 if (!quote2(&line,sender)) die_nomem();
807 qmail_put(&qq,line.s,line.len);
808 qmail_puts(&qq,">\n");
809 if (seek_begin(0) == -1)
810 strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
811 if (substdio_copy(&ssqq,&ssin2) != 0)
812 strerr_die2sys(111,FATAL,ERR_READ_INPUT);
813 if (flagcd) {
814 qmail_puts(&qq,"\n--");
815 qmail_puts(&qq,boundary);
816 qmail_puts(&qq,"--\n");
817 }
818 }
819 qmail_from(&qq,from.s);
820 qmail_to(&qq,to.s);
821 if (*(err = qmail_close(&qq)) != '\0')
822 strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
823
824 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
825 strerr_die3x(99,INFO, "qp ",strnum);
826 }