Commit | Line | Data |
---|---|---|
f8beb284 MW |
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 | } |