Commit | Line | Data |
---|---|---|
f8beb284 MW |
1 | /*$Id: ezmlm-manage.c,v 1.86 1999/12/23 02:43:55 lindberg Exp $*/ |
2 | /*$Name: ezmlm-idx-040 $*/ | |
3 | ||
5b62e993 MW |
4 | #include <sys/types.h> |
5 | #include <sys/stat.h> | |
6 | #include "error.h" | |
7 | #include "stralloc.h" | |
8 | #include "str.h" | |
9 | #include "env.h" | |
10 | #include "sig.h" | |
11 | #include "slurp.h" | |
12 | #include "getconf.h" | |
13 | #include "strerr.h" | |
14 | #include "byte.h" | |
15 | #include "getln.h" | |
16 | #include "case.h" | |
17 | #include "qmail.h" | |
18 | #include "substdio.h" | |
19 | #include "readwrite.h" | |
20 | #include "seek.h" | |
21 | #include "quote.h" | |
22 | #include "datetime.h" | |
23 | #include "now.h" | |
24 | #include "date822fmt.h" | |
25 | #include "fmt.h" | |
26 | #include "subscribe.h" | |
27 | #include "cookie.h" | |
f8beb284 MW |
28 | #include "sgetopt.h" |
29 | #include "copy.h" | |
30 | #include "errtxt.h" | |
31 | #include "idx.h" | |
5b62e993 MW |
32 | |
33 | #define FATAL "ezmlm-manage: fatal: " | |
f8beb284 MW |
34 | #define INFO "ezmlm-manage: info: " |
35 | ||
36 | int flagverbose = 0; /* default: Owner not informed about subdb changes */ | |
37 | /* 1 => notified for failed unsub, 2 => for all */ | |
38 | int flagnotify = 1; /* notify subscriber of completed events. 0 also */ | |
39 | /* suppresses all subscriber communication for */ | |
40 | /* [un]sub if -U/-S is used */ | |
41 | int flagbottom = 1; /* default: copy request & admin info to message */ | |
42 | int flaglist = 0; /* default: do not reply to -list */ | |
43 | int flagget = 1; /* default: service -get requests */ | |
44 | int flagsubconf = 1; /* default: require user-confirm for subscribe */ | |
45 | int flagunsubconf = 1; /* default: require user-confirm for unsubscribe */ | |
46 | int flagunsubismod = 0; /* default: do not require moderator approval to */ | |
47 | /* unsubscribe from moderated list */ | |
48 | int flagedit = 0; /* default: text file edit not allowed */ | |
49 | int flagstorefrom = 1; /* default: store from: line for subscribes */ | |
50 | char flagcd = '\0'; /* default: do not use _Q_uoted printable or _B_ase64 */ | |
51 | char encin = '\0'; /* encoding of incoming message */ | |
52 | int flagdig = 0; /* request is not for digest list */ | |
53 | ||
54 | static const char hex[]="0123456789ABCDEF"; | |
55 | char urlstr[] = "%00"; /* to build a url-encoded version of a char */ | |
56 | ||
57 | int act = AC_NONE; /* desired action */ | |
58 | unsigned int actlen = 0;/* str_len of above */ | |
59 | char *dir; | |
60 | char *workdir; | |
61 | char *sender; | |
62 | void *psql = (void *) 0; | |
63 | ||
64 | void die_usage() { | |
65 | strerr_die1x(100,"ezmlm-manage: usage: ezmlm-manage " | |
66 | "[-bBcCdDeEfFlLmMnNqQsSuUvV] dir"); } | |
67 | ||
68 | void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); } | |
69 | ||
5b62e993 MW |
70 | void die_badaddr() |
71 | { | |
f8beb284 MW |
72 | strerr_die2x(100,FATAL,ERR_BAD_ADDRESS); |
73 | } | |
74 | ||
75 | void die_cookie() | |
76 | { | |
77 | strerr_die2x(100,FATAL,ERR_MOD_COOKIE); | |
5b62e993 MW |
78 | } |
79 | ||
80 | stralloc inhost = {0}; | |
81 | stralloc outhost = {0}; | |
82 | stralloc inlocal = {0}; | |
83 | stralloc outlocal = {0}; | |
84 | stralloc key = {0}; | |
85 | stralloc mailinglist = {0}; | |
f8beb284 MW |
86 | stralloc mydtline = {0}; |
87 | stralloc target = {0}; | |
88 | stralloc verptarget = {0}; | |
89 | stralloc confirm = {0}; | |
90 | stralloc line = {0}; | |
91 | stralloc qline = {0}; | |
92 | stralloc quoted = {0}; | |
93 | stralloc moddir = {0}; | |
94 | stralloc ddir = {0}; | |
95 | stralloc modsub = {0}; | |
96 | stralloc remote = {0}; | |
97 | stralloc from = {0}; | |
98 | stralloc to = {0}; | |
99 | stralloc owner = {0}; | |
100 | stralloc fromline = {0}; | |
101 | stralloc text = {0}; | |
102 | stralloc fnedit = {0}; | |
103 | stralloc fneditn = {0}; | |
104 | stralloc charset = {0}; | |
5b62e993 MW |
105 | |
106 | datetime_sec when; | |
107 | struct datetime dt; | |
f8beb284 MW |
108 | int match; |
109 | unsigned int max; | |
5b62e993 MW |
110 | |
111 | char strnum[FMT_ULONG]; | |
112 | char date[DATE822FMT]; | |
113 | char hash[COOKIE]; | |
f8beb284 | 114 | char boundary[COOKIE]; |
5b62e993 | 115 | datetime_sec hashdate; |
5b62e993 | 116 | |
f8beb284 MW |
117 | char inbuf[1024]; |
118 | substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf)); | |
119 | substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf)); | |
120 | ||
121 | substdio sstext; /* editing texts and reading "from" */ | |
122 | char textbuf[512]; | |
123 | ||
124 | substdio ssfrom; /* writing "from" */ | |
125 | char frombuf[512]; | |
126 | ||
127 | int fdlock; | |
128 | ||
129 | void lock() | |
130 | { | |
131 | fdlock = open_append("lock"); | |
132 | if (fdlock == -1) | |
133 | strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: "); | |
134 | if (lock_ex(fdlock) == -1) | |
135 | strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: "); | |
136 | } | |
137 | ||
138 | void unlock() | |
139 | { | |
140 | close(fdlock); | |
141 | } | |
142 | ||
143 | void make_verptarget() | |
144 | /* puts target with '=' instead of last '@' into stralloc verptarget */ | |
145 | /* and does set_cpverptarget */ | |
146 | { | |
147 | unsigned int i; | |
148 | ||
149 | i = str_rchr(target.s,'@'); | |
150 | if (!stralloc_copyb(&verptarget,target.s,i)) die_nomem(); | |
151 | if (target.s[i]) { | |
152 | if (!stralloc_append(&verptarget,"=")) die_nomem(); | |
153 | if (!stralloc_cats(&verptarget,target.s + i + 1)) die_nomem(); | |
154 | } | |
155 | if (!stralloc_0(&verptarget)) die_nomem(); | |
156 | set_cpverptarget(verptarget.s); | |
157 | } | |
158 | ||
159 | void store_from(frl,adr) | |
160 | /* rewrites the from file removing all that is older than 1000000 secs */ | |
161 | /* and add the curent from line (frl). Forget it if there is none there.*/ | |
162 | /* NOTE: This is used only for subscribes to moderated lists! */ | |
163 | stralloc *frl; /* from line */ | |
164 | char *adr; | |
165 | { | |
166 | int fdin; | |
167 | int fdout; | |
168 | unsigned long linetime; | |
169 | ||
170 | if (!flagstorefrom || !frl->len) return; /* nothing to store */ | |
171 | lock(); | |
172 | if ((fdout = open_trunc("fromn")) == -1) | |
173 | strerr_die3sys(111,FATAL,ERR_OPEN,"fromn: "); | |
174 | substdio_fdbuf(&ssfrom,write,fdout,frombuf,(int) sizeof(frombuf)); | |
175 | if ((fdin = open_read("from")) == -1) { | |
176 | if (errno != error_noent) | |
177 | strerr_die3sys(111,FATAL,ERR_OPEN,"from: "); | |
178 | } else { | |
179 | substdio_fdbuf(&sstext,read,fdin,textbuf,(int) sizeof(textbuf)); | |
180 | for (;;) { | |
181 | if (getln(&sstext,&line,&match,'\n') == -1) | |
182 | strerr_die3sys(111,FATAL,ERR_READ,"from: "); | |
183 | if (!match) break; | |
184 | (void) scan_ulong(line.s,&linetime); | |
185 | if (linetime + 1000000 > when && linetime <= when) | |
186 | if (substdio_bput(&ssfrom,line.s,line.len)) | |
187 | strerr_die3sys(111,FATAL,ERR_WRITE,"fromn: "); | |
188 | } | |
189 | close(fdin); | |
190 | } /* build new entry */ | |
191 | if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,when))) die_nomem(); | |
192 | if (!stralloc_append(&line," ")) die_nomem(); | |
193 | if (!stralloc_cats(&line,adr)) die_nomem(); | |
194 | if (!stralloc_0(&line)) die_nomem(); | |
195 | if (!stralloc_catb(&line,frl->s,frl->len)) die_nomem(); | |
196 | if (!stralloc_append(&line,"\n")) die_nomem(); | |
197 | if (substdio_bput(&ssfrom,line.s,line.len) == -1) | |
198 | strerr_die3sys(111,FATAL,ERR_WRITE,"fromn: "); | |
199 | if (substdio_flush(&ssfrom) == -1) | |
200 | strerr_die3sys(111,FATAL,ERR_WRITE,"fromn: "); | |
201 | if (fsync(fdout) == -1) | |
202 | strerr_die3sys(111,FATAL,ERR_SYNC,"fromn: "); | |
203 | if (close(fdout) == -1) | |
204 | strerr_die3sys(111,FATAL,ERR_CLOSE,"fromn: "); | |
205 | if (rename("fromn","from") == -1) | |
206 | strerr_die3sys(111,FATAL,ERR_MOVE,"from: "); | |
207 | unlock(); | |
208 | } | |
209 | ||
210 | char *get_from(adr,act) | |
211 | /* If we captured a from line, it will be from the subscriber, except */ | |
212 | /* when -S is used when it's usually from the subscriber, but of course */ | |
213 | /* could be from anyone. The matching to stored data is required only */ | |
214 | /* to support moderated lists, and in cases where a new -sc is issued */ | |
215 | /* because an old one was invalid. In this case, we read through the */ | |
216 | /* from file trying to match up a timestamp with that starting in */ | |
217 | /* *(act+3). If the time stamp matches, we compare the target address */ | |
218 | /* itself. act + 3 must be a legal part of the string returns pointer to*/ | |
219 | /* fromline, NULL if not found. Since the execution time from when to */ | |
220 | /* storage may differ, we can't assume that the timestamps are in order.*/ | |
221 | ||
222 | char *adr; /* target address */ | |
223 | char *act; /* action */ | |
224 | { | |
225 | int fd; | |
226 | char *fl; | |
227 | unsigned int pos; | |
228 | unsigned long thistime; | |
229 | unsigned long linetime; | |
230 | ||
231 | if (!flagstorefrom) return 0; | |
232 | if (fromline.len) { /* easy! We got it in this message */ | |
233 | if (!stralloc_0(&fromline)) die_nomem(FATAL); | |
234 | return fromline.s; | |
235 | } /* need to recover it from DIR/from */ | |
236 | fl = 0; | |
237 | (void) scan_ulong(act+3,&thistime); | |
238 | if ((fd = open_read("from")) == -1) | |
239 | if (errno == error_noent) | |
240 | return 0; | |
241 | else | |
242 | strerr_die3x(111,FATAL,ERR_READ,"from: "); | |
243 | substdio_fdbuf(&sstext,read,fd,textbuf,(int) sizeof(textbuf)); | |
244 | for (;;) { | |
245 | if (getln(&sstext,&fromline,&match,'\n') == -1) | |
246 | strerr_die3sys(111,FATAL,ERR_READ,"from: "); | |
247 | if (!match) break; | |
248 | fromline.s[fromline.len - 1] = (char) 0; | |
249 | /* now:time addr\0fromline\0 read all. They can be out of order! */ | |
250 | pos = scan_ulong(fromline.s,&linetime); | |
251 | if (linetime != thistime) continue; | |
252 | if (!str_diff(fromline.s + pos + 1,adr)) { | |
253 | pos = str_len(fromline.s); | |
254 | if (pos < fromline.len) { | |
255 | fl = fromline.s + pos + 1; | |
256 | break; | |
257 | } | |
258 | } | |
259 | } | |
260 | close(fd); | |
261 | return fl; | |
262 | } | |
263 | ||
264 | int hashok(action,ac) | |
5b62e993 | 265 | char *action; |
f8beb284 | 266 | char *ac; |
5b62e993 MW |
267 | { |
268 | char *x; | |
f8beb284 | 269 | datetime_sec u; |
5b62e993 | 270 | |
f8beb284 | 271 | x = action + 3; |
5b62e993 MW |
272 | x += scan_ulong(x,&u); |
273 | hashdate = u; | |
274 | if (hashdate > when) return 0; | |
275 | if (hashdate < when - 1000000) return 0; | |
276 | ||
277 | u = hashdate; | |
f8beb284 MW |
278 | strnum[fmt_ulong(strnum,(unsigned long) u)] = 0; |
279 | cookie(hash,key.s,key.len - flagdig,strnum,target.s,ac); | |
5b62e993 MW |
280 | |
281 | if (*x == '.') ++x; | |
282 | if (str_len(x) != COOKIE) return 0; | |
283 | return byte_equal(hash,COOKIE,x); | |
284 | } | |
285 | ||
286 | struct qmail qq; | |
287 | int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len; | |
288 | { | |
289 | qmail_put(&qq,buf,len); | |
290 | return len; | |
291 | } | |
f8beb284 | 292 | |
5b62e993 | 293 | char qqbuf[1]; |
f8beb284 | 294 | substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf)); |
5b62e993 | 295 | |
f8beb284 MW |
296 | int code_qput(s,n) |
297 | char *s; | |
298 | unsigned int n; | |
299 | { | |
300 | if (!flagcd) | |
301 | qmail_put(&qq,s,n); | |
302 | else { | |
303 | if (flagcd == 'B') | |
304 | encodeB(s,n,&qline,0,FATAL); | |
305 | else | |
306 | encodeQ(s,n,&qline,FATAL); | |
307 | qmail_put(&qq,qline.s,qline.len); | |
308 | } | |
309 | return 0; /* always succeeds */ | |
310 | } | |
5b62e993 | 311 | |
f8beb284 MW |
312 | int subto(s,l) |
313 | char *s; | |
314 | unsigned int l; | |
315 | { | |
316 | qmail_put(&qq,"T",1); | |
317 | qmail_put(&qq,s,l); | |
318 | qmail_put(&qq,"",1); | |
319 | return (int) l; | |
320 | } | |
5b62e993 | 321 | |
f8beb284 MW |
322 | int code_subto(s,l) |
323 | char *s; | |
324 | unsigned int l; | |
5b62e993 | 325 | { |
f8beb284 MW |
326 | code_qput(s,l); |
327 | code_qput("\n",1); | |
328 | return (int) l; | |
329 | } | |
5b62e993 | 330 | |
f8beb284 MW |
331 | int dummy_to(s,l) |
332 | char *s; /* ignored */ | |
333 | unsigned int l; | |
334 | { | |
335 | return (int) l; | |
336 | } | |
5b62e993 | 337 | |
f8beb284 MW |
338 | void transferenc() |
339 | { | |
340 | if (flagcd) { | |
341 | qmail_puts(&qq,"\n--"); | |
342 | qmail_put(&qq,boundary,COOKIE); | |
343 | qmail_puts(&qq,"\nContent-Type: text/plain; charset="); | |
344 | qmail_puts(&qq,charset.s); | |
345 | qmail_puts(&qq,"\nContent-Transfer-Encoding: "); | |
346 | if (flagcd == 'Q') | |
347 | qmail_puts(&qq,"quoted-printable\n\n"); | |
348 | else | |
349 | qmail_puts(&qq,"base64\n\n"); | |
350 | } else | |
351 | qmail_puts(&qq,"\n"); | |
352 | } | |
353 | ||
354 | void to_owner() | |
355 | { | |
356 | if (!stralloc_copy(&owner,&outlocal)) die_nomem(); | |
357 | if (!stralloc_cats(&owner,"-owner@")) die_nomem(); | |
358 | if (!stralloc_cat(&owner,&outhost)) die_nomem(); | |
359 | if (!stralloc_0(&owner)) die_nomem(); | |
360 | qmail_to(&qq,owner.s); | |
361 | } | |
362 | ||
363 | void mod_bottom() | |
364 | { | |
365 | copy(&qq,"text/mod-sub",flagcd,FATAL); | |
366 | copy(&qq,"text/bottom",flagcd,FATAL); | |
367 | code_qput(TXT_SUPPRESSED,str_len(TXT_SUPPRESSED)); | |
368 | if (flagcd) { | |
369 | qmail_puts(&qq,"\n--"); | |
370 | qmail_put(&qq,boundary,COOKIE); | |
371 | qmail_puts(&qq,"--\n"); | |
5b62e993 | 372 | } |
f8beb284 MW |
373 | if (flagcd == 'B') { |
374 | encodeB("",0,&line,2,FATAL); /* flush */ | |
375 | qmail_put(&qq,line.s,line.len); | |
376 | } | |
377 | qmail_from(&qq,from.s); | |
378 | } | |
379 | void msg_headers() | |
380 | /* Writes all the headers up to but not including subject */ | |
381 | { | |
382 | int flaggoodfield; | |
383 | int flagfromline; | |
384 | int flaggetfrom; | |
385 | unsigned int pos; | |
5b62e993 | 386 | |
f8beb284 MW |
387 | qmail_puts(&qq,"Mailing-List: "); |
388 | qmail_put(&qq,mailinglist.s,mailinglist.len); | |
389 | if(getconf_line(&line,"listid",0,FATAL,dir)) { | |
390 | qmail_puts(&qq,"\nList-ID: "); | |
5b62e993 | 391 | qmail_put(&qq,line.s,line.len); |
f8beb284 MW |
392 | } |
393 | if (!quote("ed,&outlocal)) die_nomem(); /* quoted has outlocal */ | |
394 | qmail_puts(&qq,"\nList-Help: <mailto:"); /* General rfc2369 headers */ | |
395 | qmail_put(&qq,quoted.s,quoted.len); | |
396 | qmail_puts(&qq,"-help@"); | |
397 | qmail_put(&qq,outhost.s,outhost.len); | |
398 | qmail_puts(&qq,">\nList-Post: <mailto:"); | |
399 | qmail_put(&qq,quoted.s,quoted.len); | |
400 | qmail_puts(&qq,"@"); | |
401 | qmail_put(&qq,outhost.s,outhost.len); | |
402 | qmail_puts(&qq,">\nList-Subscribe: <mailto:"); | |
403 | qmail_put(&qq,quoted.s,quoted.len); | |
404 | qmail_puts(&qq,"-subscribe@"); | |
405 | qmail_put(&qq,outhost.s,outhost.len); | |
406 | qmail_puts(&qq,">\nDate: "); | |
407 | datetime_tai(&dt,when); | |
408 | qmail_put(&qq,date,date822fmt(date,&dt)); | |
409 | qmail_puts(&qq,"Message-ID: <"); | |
410 | if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when))) | |
411 | die_nomem(); | |
412 | if (!stralloc_append(&line,".")) die_nomem(); | |
413 | if (!stralloc_catb(&line,strnum, | |
414 | fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); | |
415 | if (!stralloc_cats(&line,".ezmlm@")) die_nomem(); | |
416 | if (!stralloc_cat(&line,&outhost)) die_nomem(); | |
417 | if (!stralloc_0(&line)) die_nomem(); | |
418 | qmail_puts(&qq,line.s); | |
419 | /* "unique" MIME boundary as hash of messageid */ | |
420 | cookie(boundary,"",0,"",line.s,""); | |
421 | qmail_puts(&qq,">\nFrom: "); | |
422 | qmail_put(&qq,quoted.s,quoted.len); | |
423 | if (act == AC_HELP) /* differnt "From:" for help to break auto- */ | |
424 | qmail_puts(&qq,"-return-@"); /* responder loops */ | |
425 | else | |
426 | qmail_puts(&qq,"-help@"); | |
427 | qmail_put(&qq,outhost.s,outhost.len); | |
428 | qmail_puts(&qq,"\nTo: "); | |
429 | if (!quote2("ed,target.s)) die_nomem(); | |
430 | qmail_put(&qq,quoted.s,quoted.len); | |
431 | qmail_puts(&qq,"\n"); | |
432 | if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem(); | |
433 | if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); | |
434 | if (!stralloc_cats(&mydtline,"@")) die_nomem(); | |
435 | if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); | |
436 | if (!stralloc_cats(&mydtline,"\n")) die_nomem(); | |
437 | qmail_put(&qq,mydtline.s,mydtline.len); | |
5b62e993 | 438 | |
f8beb284 MW |
439 | flaggoodfield = 0; |
440 | flagfromline = 0; | |
441 | /* do it for -sc, but if the -S flag is used, do it for -subscribe */ | |
442 | flaggetfrom = flagstorefrom && | |
443 | ((act == AC_SC) || ((act == AC_SUBSCRIBE) && !flagsubconf)); | |
444 | for (;;) { | |
445 | if (getln(&ssin,&line,&match,'\n') == -1) | |
446 | strerr_die2sys(111,FATAL,ERR_READ_INPUT); | |
447 | if (!match) break; | |
448 | if (line.len == 1) break; | |
449 | if ((line.s[0] != ' ') && (line.s[0] != '\t')) { | |
450 | flagfromline = 0; | |
451 | flaggoodfield = 0; | |
452 | if (case_startb(line.s,line.len,"mailing-list:")) | |
453 | strerr_die2x(100,FATAL,ERR_MAILING_LIST); | |
454 | if (line.len == mydtline.len) | |
455 | if (byte_equal(line.s,line.len,mydtline.s)) | |
456 | strerr_die2x(100,FATAL,ERR_LOOPING); | |
457 | if (case_startb(line.s,line.len,"delivered-to:")) | |
458 | flaggoodfield = 1; | |
459 | else if (case_startb(line.s,line.len,"received:")) | |
460 | flaggoodfield = 1; | |
461 | else if (case_startb(line.s,line.len,"content-transfer-encoding:")) { | |
462 | pos = 26; | |
463 | while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos; | |
464 | if (case_startb(line.s+pos,line.len-pos,"base64")) | |
465 | encin = 'B'; | |
466 | else if (case_startb(line.s+pos,line.len-pos,"quoted-printable")) | |
467 | encin = 'Q'; | |
468 | } else if (flaggetfrom && case_startb(line.s,line.len,"from:")) { | |
469 | flagfromline = 1; /* for logging subscriber data */ | |
470 | pos = 5; | |
471 | while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos; | |
472 | if (!stralloc_copyb(&fromline,line.s + pos,line.len - pos - 1)) | |
473 | die_nomem(); | |
474 | } | |
475 | } else { | |
476 | if (flagfromline == 1) /* scrap terminal '\n' */ | |
477 | if (!stralloc_catb(&fromline,line.s,line.len - 1)) die_nomem(); | |
478 | } | |
479 | if (flaggoodfield) | |
480 | qmail_put(&qq,line.s,line.len); | |
481 | } | |
482 | qmail_puts(&qq,"MIME-Version: 1.0\n"); | |
483 | if (flagcd) { | |
484 | qmail_puts(&qq,"Content-Type: multipart/mixed; charset="); | |
485 | qmail_puts(&qq,charset.s); | |
486 | qmail_puts(&qq,";\n\tboundary="); | |
487 | qmail_put(&qq,boundary,COOKIE); | |
488 | } else { | |
489 | qmail_puts(&qq,"Content-type: text/plain; charset="); | |
490 | qmail_puts(&qq,charset.s); | |
5b62e993 | 491 | } |
f8beb284 MW |
492 | qmail_puts(&qq,"\n"); |
493 | } | |
5b62e993 | 494 | |
f8beb284 MW |
495 | int geton(action) |
496 | char *action; | |
497 | { | |
498 | char *fl; | |
499 | int r; | |
500 | unsigned int i; | |
501 | unsigned char ch; | |
502 | ||
503 | fl = get_from(target.s,action); /* try to match up */ | |
504 | switch((r = subscribe(workdir,target.s,1,fl,"+",1,-1,(char *) 0,FATAL))) { | |
505 | case 1: | |
506 | qmail_puts(&qq,"List-Unsubscribe: <mailto:"); /*rfc2369 */ | |
507 | qmail_put(&qq,outlocal.s,outlocal.len); | |
508 | qmail_puts(&qq,"-unsubscribe-"); | |
509 | /* url-encode since verptarget is controlled by sender */ | |
510 | /* note &verptarget ends in '\0', hence len - 1! */ | |
511 | for (i = 0; i < verptarget.len - 1; i++) { | |
512 | ch = verptarget.s[i]; | |
513 | if (str_chr("\"?;<>&/:%+#",ch) < 10 || | |
514 | (ch <= ' ') || (ch & 0x80)) { | |
515 | urlstr[1] = hex[ch / 16]; | |
516 | urlstr[2] = hex[ch & 0xf]; | |
517 | qmail_put(&qq,urlstr,3); | |
518 | } else { | |
519 | qmail_put(&qq,verptarget.s + i, 1); | |
520 | } | |
521 | } | |
522 | qmail_puts(&qq,"@"); | |
523 | qmail_put(&qq,outhost.s,outhost.len); /* safe */ | |
524 | qmail_puts(&qq,">\n"); | |
525 | qmail_puts(&qq,TXT_WELCOME); | |
526 | if (!quote("ed,&outlocal)) die_nomem(); | |
527 | qmail_put(&qq,quoted.s,quoted.len); | |
528 | qmail_puts(&qq,"@"); | |
529 | qmail_put(&qq,outhost.s,outhost.len); | |
530 | qmail_puts(&qq,"\n"); | |
531 | transferenc(); | |
532 | if (!stralloc_copy(&confirm,&outlocal)) die_nomem(); | |
533 | if (!stralloc_append(&confirm,"unsubscribe-")) die_nomem(); | |
534 | if (!stralloc_cats(&confirm,verptarget.s)) die_nomem(); | |
535 | if (!stralloc_append(&confirm,"@")) die_nomem(); | |
536 | if (!stralloc_cat(&confirm,&outhost)) die_nomem(); | |
537 | if (!stralloc_0(&confirm)) die_nomem(); | |
538 | set_cpconfirm(confirm.s); /* for !R in copy */ | |
539 | copy(&qq,"text/top",flagcd,FATAL); | |
540 | copy(&qq,"text/sub-ok",flagcd,FATAL); | |
541 | break; | |
542 | default: | |
543 | if (str_start(action,ACTION_TC)) | |
544 | strerr_die2x(0,INFO,ERR_SUB_NOP); | |
545 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
546 | transferenc(); | |
547 | copy(&qq,"text/top",flagcd,FATAL); | |
548 | copy(&qq,"text/sub-nop",flagcd,FATAL); | |
549 | break; | |
550 | } | |
551 | if (flagdig == FLD_DENY || flagdig == FLD_ALLOW) | |
552 | strerr_die3x(0,INFO,ERR_EXTRA_SUB,target.s); | |
553 | return r; | |
5b62e993 MW |
554 | } |
555 | ||
f8beb284 MW |
556 | int getoff(action) |
557 | char *action; | |
558 | { | |
559 | int r; | |
560 | ||
561 | switch((r = subscribe(workdir,target.s,0,"","-",1,-1,(char *) 0,FATAL))) { | |
562 | /* no comment for unsubscribe */ | |
563 | case 1: | |
564 | qmail_puts(&qq,TXT_GOODBYE); | |
565 | if (!quote("ed,&outlocal)) die_nomem(); | |
566 | qmail_put(&qq,quoted.s,quoted.len); | |
567 | qmail_puts(&qq,"@"); | |
568 | qmail_put(&qq,outhost.s,outhost.len); | |
569 | qmail_puts(&qq,"\n\n"); | |
570 | transferenc(); | |
571 | copy(&qq,"text/top",flagcd,FATAL); | |
572 | copy(&qq,"text/unsub-ok",flagcd,FATAL); | |
573 | break; | |
574 | default: | |
575 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
576 | transferenc(); | |
577 | copy(&qq,"text/top",flagcd,FATAL); | |
578 | copy(&qq,"text/unsub-nop",flagcd,FATAL); | |
579 | break; | |
580 | } | |
581 | if (flagdig == FLD_DENY || flagdig == FLD_ALLOW) | |
582 | strerr_die3x(0,INFO,ERR_EXTRA_UNSUB,target.s); | |
583 | return r; | |
584 | } | |
5b62e993 | 585 | |
f8beb284 MW |
586 | void doconfirm(act) |
587 | /* This should only be called with valid act for sub/unsub confirms. If act */ | |
588 | /* is not ACTION_SC or ACTION_TC, it is assumed to be an unsubscribe conf.*/ | |
589 | char *act; /* first letter of desired confirm request only as STRING! */ | |
590 | { | |
591 | unsigned int i; | |
592 | ||
593 | strnum[fmt_ulong(strnum,(unsigned long) when)] = 0; | |
594 | cookie(hash,key.s,key.len-flagdig,strnum,target.s,act); | |
595 | if (!stralloc_copy(&confirm,&outlocal)) die_nomem(); | |
596 | if (!stralloc_append(&confirm,"-")) die_nomem(); | |
597 | if (!stralloc_catb(&confirm,act,1)) die_nomem(); | |
598 | if (!stralloc_cats(&confirm,"c.")) die_nomem(); | |
599 | if (!stralloc_cats(&confirm,strnum)) die_nomem(); | |
600 | if (!stralloc_append(&confirm,".")) die_nomem(); | |
601 | if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem(); | |
602 | if (!stralloc_append(&confirm,"-")) die_nomem(); | |
603 | if (!stralloc_cats(&confirm,verptarget.s)) die_nomem(); | |
604 | if (!stralloc_append(&confirm,"@")) die_nomem(); | |
605 | if (!stralloc_cat(&confirm,&outhost)) die_nomem(); | |
606 | if (!stralloc_0(&confirm)) die_nomem(); | |
607 | set_cpconfirm(confirm.s); /* for copy */ | |
608 | ||
609 | qmail_puts(&qq,"Reply-To: "); | |
610 | if (!quote2("ed,confirm.s)) die_nomem(); | |
611 | qmail_put(&qq,quoted.s,quoted.len); | |
612 | qmail_puts(&qq,"\n"); | |
613 | if (!stralloc_0(&confirm)) die_nomem(); | |
614 | ||
615 | qmail_puts(&qq,"Subject: "); | |
616 | if (*act == ACTION_SC[0] || *act == ACTION_UC[0]) | |
617 | qmail_puts(&qq,TXT_USRCONFIRM); | |
618 | else | |
619 | qmail_puts(&qq,TXT_MODCONFIRM); | |
620 | if (*act == ACTION_SC[0] || *act == ACTION_TC[0]) | |
621 | qmail_puts(&qq,TXT_SUBSCRIBE_TO); | |
622 | else | |
623 | qmail_puts(&qq,TXT_UNSUBSCRIBE_FROM); | |
624 | if (!quote("ed,&outlocal)) die_nomem(); | |
625 | qmail_put(&qq,quoted.s,quoted.len); | |
626 | qmail_puts(&qq,"@"); | |
627 | qmail_put(&qq,outhost.s,outhost.len); | |
628 | qmail_puts(&qq,"\n"); | |
629 | transferenc(); | |
630 | copy(&qq,"text/top",flagcd,FATAL); | |
631 | } | |
632 | ||
633 | void sendtomods() | |
634 | { | |
635 | putsubs(moddir.s,0L,52L,subto,1,FATAL); | |
636 | } | |
637 | ||
638 | void copybottom() | |
639 | { | |
640 | if (flagbottom || act == AC_HELP) { | |
641 | copy(&qq,"text/bottom",flagcd,FATAL); | |
642 | if (flagcd) { | |
643 | if (flagcd == 'B') { | |
644 | encodeB("",0,&line,2,FATAL); /* flush */ | |
645 | qmail_put(&qq,line.s,line.len); | |
646 | } | |
647 | qmail_puts(&qq,"\n--"); | |
648 | qmail_put(&qq,boundary,COOKIE); | |
649 | qmail_puts(&qq,"\nContent-Type: message/rfc822"); | |
650 | qmail_puts(&qq,"\nContent-Disposition: inline; filename=request.msg\n\n"); | |
651 | } | |
652 | qmail_puts(&qq,"Return-Path: <"); | |
653 | if (!quote2("ed,sender)) die_nomem(); | |
654 | qmail_put(&qq,quoted.s,quoted.len); | |
655 | qmail_puts(&qq,">\n"); | |
656 | if (seek_begin(0) == -1) | |
657 | strerr_die2sys(111,FATAL,ERR_SEEK_INPUT); | |
658 | if (substdio_copy(&ssqq,&ssin2) != 0) | |
659 | strerr_die2sys(111,FATAL,ERR_READ_INPUT); | |
660 | if (flagcd) { | |
661 | qmail_puts(&qq,"\n--"); | |
662 | qmail_put(&qq,boundary,COOKIE); | |
663 | qmail_puts(&qq,"--\n"); | |
664 | } | |
665 | } else { | |
666 | if (flagcd == 'B') { | |
667 | encodeB("",0,&line,2,FATAL); /* flush even if no bottom */ | |
668 | qmail_put(&qq,line.s,line.len); | |
669 | } | |
670 | } | |
671 | ||
672 | qmail_from(&qq,from.s); | |
673 | } | |
674 | ||
675 | int main(argc,argv) | |
5b62e993 MW |
676 | int argc; |
677 | char **argv; | |
678 | { | |
5b62e993 | 679 | char *local; |
f8beb284 | 680 | char *def; |
5b62e993 | 681 | char *action; |
f8beb284 MW |
682 | char *x, *y; |
683 | char *fname; | |
684 | char *pmod; | |
685 | char *err; | |
686 | char *cp,*cpfirst,*cplast,*cpnext,*cpafter; | |
687 | int flagmod; | |
688 | int flagremote; | |
689 | int flagpublic; | |
690 | int opt,r; | |
691 | unsigned int i; | |
692 | unsigned int len; | |
5b62e993 | 693 | int fd; |
f8beb284 MW |
694 | int flagdone; |
695 | register char ch; | |
5b62e993 | 696 | |
f8beb284 | 697 | (void) umask(022); |
5b62e993 MW |
698 | sig_pipeignore(); |
699 | when = now(); | |
700 | ||
f8beb284 MW |
701 | while ((opt = getopt(argc,argv,"bBcCdDeEfFlLmMnNqQsSuUvV")) != opteof) |
702 | switch(opt) { | |
703 | case 'b': flagbottom = 1; break; | |
704 | case 'B': flagbottom = 0; break; | |
705 | case 'c': flagget = 1; break; | |
706 | case 'C': flagget = 0; break; | |
707 | case 'd': | |
708 | case 'e': flagedit = 1; break; | |
709 | case 'D': | |
710 | case 'E': flagedit = 0; break; | |
711 | case 'f': flagstorefrom = 1; break; | |
712 | case 'F': flagstorefrom = 0; break; | |
713 | case 'l': flaglist = 1; break; | |
714 | case 'L': flaglist = 0; break; | |
715 | case 'm': flagunsubismod = 1; break; | |
716 | case 'M': flagunsubismod = 0; break; | |
717 | case 'n': flagnotify = 1; break; | |
718 | case 'N': flagnotify = 0; break; | |
719 | case 's': flagsubconf = 1; break; | |
720 | case 'S': flagsubconf = 0; break; | |
721 | case 'q': flagverbose = 0; break; | |
722 | case 'Q': flagverbose++; break; | |
723 | case 'u': flagunsubconf = 1; break; | |
724 | case 'U': flagunsubconf = 0; break; | |
725 | case 'v': | |
726 | case 'V': strerr_die2x(0, | |
727 | "ezmlm-manage version: ezmlm-0.53+",EZIDX_VERSION); | |
728 | default: | |
729 | die_usage(); | |
730 | } | |
731 | ||
732 | dir = argv[optind]; | |
5b62e993 MW |
733 | if (!dir) die_usage(); |
734 | ||
735 | sender = env_get("SENDER"); | |
f8beb284 | 736 | if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER); |
5b62e993 | 737 | local = env_get("LOCAL"); |
f8beb284 MW |
738 | if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL); |
739 | def = env_get("DEFAULT"); | |
5b62e993 MW |
740 | |
741 | if (!*sender) | |
f8beb284 | 742 | strerr_die2x(100,FATAL,ERR_BOUNCE); |
5b62e993 | 743 | if (!sender[str_chr(sender,'@')]) |
f8beb284 | 744 | strerr_die2x(100,FATAL,ERR_ANONYMOUS); |
5b62e993 | 745 | if (str_equal(sender,"#@[]")) |
f8beb284 | 746 | strerr_die2x(100,FATAL,ERR_BOUNCE); |
5b62e993 MW |
747 | |
748 | if (chdir(dir) == -1) | |
f8beb284 | 749 | strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": "); |
5b62e993 MW |
750 | |
751 | switch(slurp("key",&key,32)) { | |
752 | case -1: | |
f8beb284 | 753 | strerr_die4sys(111,FATAL,ERR_READ,dir,"/key: "); |
5b62e993 | 754 | case 0: |
f8beb284 | 755 | strerr_die4x(100,FATAL,dir,"/key",ERR_NOEXIST); |
5b62e993 MW |
756 | } |
757 | getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); | |
5b62e993 MW |
758 | getconf_line(&outhost,"outhost",1,FATAL,dir); |
759 | getconf_line(&outlocal,"outlocal",1,FATAL,dir); | |
f8beb284 MW |
760 | set_cpouthost(&outhost); |
761 | if (getconf_line(&charset,"charset",0,FATAL,dir)) { | |
762 | if (charset.len >= 2 && charset.s[charset.len - 2] == ':') { | |
763 | if (charset.s[charset.len - 1] == 'B' || | |
764 | charset.s[charset.len - 1] == 'Q') { | |
765 | flagcd = charset.s[charset.len - 1]; | |
766 | charset.s[charset.len - 2] = '\0'; | |
767 | } | |
768 | } | |
769 | } else | |
770 | if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem(); | |
771 | if (!stralloc_0(&charset)) die_nomem(); | |
5b62e993 | 772 | |
f8beb284 MW |
773 | if (def) /* qmail-1.02 */ |
774 | action = def; /* .qmail-list-default */ | |
775 | else { /* older version of qmail */ | |
776 | getconf_line(&inlocal,"inlocal",1,FATAL,dir); | |
777 | if (inlocal.len > str_len(local)) die_badaddr(); | |
778 | if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr(); | |
779 | action = local + inlocal.len; | |
780 | if (*(action++) != '-') die_badaddr(); | |
781 | /* has to be '-' to match link. Check anyway */ | |
782 | } | |
5b62e993 | 783 | |
f8beb284 | 784 | if (!stralloc_copys(&ddir,dir)) die_nomem(); |
5b62e993 | 785 | |
f8beb284 MW |
786 | if (case_starts(action,"digest")) { /* digest */ |
787 | action += 6; | |
788 | if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); | |
789 | if (!stralloc_cats(&ddir,"/digest")) die_nomem(); | |
790 | flagdig = FLD_DIGEST; | |
791 | } else if (case_starts(action,ACTION_ALLOW)) { /* allow */ | |
792 | action += str_len(ACTION_ALLOW); | |
793 | if (!stralloc_append(&outlocal,"-")) die_nomem(); | |
794 | if (!stralloc_cats(&outlocal,ACTION_ALLOW)) die_nomem(); | |
795 | if (!stralloc_cats(&ddir,"/allow")) die_nomem(); | |
796 | flagdig = FLD_ALLOW; | |
797 | } else if (case_starts(action,ACTION_DENY)) { /* deny */ | |
798 | action += str_len(ACTION_DENY); | |
799 | if (!stralloc_append(&outlocal,"-")) die_nomem(); | |
800 | if (!stralloc_cats(&outlocal,ACTION_DENY)) die_nomem(); | |
801 | if (!stralloc_cats(&ddir,"/deny")) die_nomem(); | |
802 | flagdig = FLD_DENY; | |
5b62e993 | 803 | } |
f8beb284 MW |
804 | if (flagdig) /* zap '-' after db specifier */ |
805 | if (*(action++) != '-') die_badaddr(); | |
806 | ||
807 | if (!stralloc_0(&ddir)) die_nomem(); | |
808 | workdir = ddir.s; | |
809 | set_cpoutlocal(&outlocal); | |
5b62e993 MW |
810 | |
811 | if (!stralloc_copys(&target,sender)) die_nomem(); | |
812 | if (action[0]) { | |
f8beb284 | 813 | i = str_chr(action,'-'); |
5b62e993 MW |
814 | if (action[i]) { |
815 | action[i] = 0; | |
816 | if (!stralloc_copys(&target,action + i + 1)) die_nomem(); | |
817 | i = byte_rchr(target.s,target.len,'='); | |
818 | if (i < target.len) | |
819 | target.s[i] = '@'; | |
820 | } | |
821 | } | |
822 | if (!stralloc_0(&target)) die_nomem(); | |
f8beb284 MW |
823 | set_cptarget(target.s); /* for copy() */ |
824 | make_verptarget(); | |
5b62e993 | 825 | |
f8beb284 MW |
826 | flagmod = getconf_line(&modsub,"modsub",0,FATAL,dir); |
827 | flagremote = getconf_line(&remote,"remote",0,FATAL,dir); | |
5b62e993 | 828 | |
f8beb284 MW |
829 | if (case_equals(action,ACTION_LISTN) || |
830 | case_equals(action,ALT_LISTN)) | |
831 | act = AC_LISTN; | |
832 | else if (case_equals(action,ACTION_LIST) || | |
833 | case_equals(action,ALT_LIST)) | |
834 | act = AC_LIST; | |
835 | else if (case_starts(action,ACTION_GET) || | |
836 | case_starts(action,ALT_GET)) | |
837 | act = AC_GET; | |
838 | else if (case_equals(action,ACTION_HELP) || | |
839 | case_equals(action,ALT_HELP)) | |
840 | act = AC_HELP; | |
841 | else if (case_starts(action,ACTION_EDIT) || | |
842 | case_starts(action,ALT_EDIT)) | |
843 | act = AC_EDIT; | |
844 | else if (case_starts(action,ACTION_LOG)) | |
845 | { act = AC_LOG; actlen = str_len(ACTION_LOG); } | |
846 | else if (case_starts(action,ALT_LOG)) | |
847 | { act = AC_LOG; actlen = str_len(ALT_LOG); } | |
5b62e993 | 848 | |
f8beb284 MW |
849 | /* NOTE: act is needed in msg_headers(). */ |
850 | /* Yes, this needs to be cleaned up! */ | |
851 | ||
852 | if (flagmod || flagremote) { | |
853 | if (modsub.len && modsub.s[0] == '/') { | |
854 | if (!stralloc_copy(&moddir,&modsub)) die_nomem(); | |
855 | } else if (remote.len && remote.s[0] == '/') { | |
856 | if (!stralloc_copy(&moddir,&remote)) die_nomem(); | |
857 | } else { | |
858 | if (!stralloc_copys(&moddir,dir)) die_nomem(); | |
859 | if (!stralloc_cats(&moddir,"/mod")) die_nomem(); | |
5b62e993 | 860 | } |
f8beb284 MW |
861 | if (!stralloc_0(&moddir)) die_nomem(); |
862 | /* for these the reply is 'secret' and goes to sender */ | |
863 | /* This means that they can be triggered from a SENDER */ | |
864 | /* that is not a mod, but never send to a non-mod */ | |
865 | if (act == AC_NONE || flagdig == FLD_DENY) /* None of the above */ | |
866 | pmod = issub(moddir.s,sender,(char *) 0,FATAL); | |
867 | /* sender = moderator? */ | |
868 | else | |
869 | pmod = issub(moddir.s,target.s,(char *) 0,FATAL); | |
870 | /* target = moderator? */ | |
871 | } else | |
872 | pmod = 0; /* always 0 for non-mod/remote lists */ | |
873 | /* if DIR/public is missing, we still respond*/ | |
874 | /* to requests from moderators for remote */ | |
875 | /* admin and modsub lists. Since pmod */ | |
876 | /* is false for all non-mod lists, only it */ | |
877 | /* needs to be tested. */ | |
878 | if ((flagpublic = slurp("public",&line,1)) == -1) | |
879 | strerr_die4sys(111,FATAL,ERR_READ,dir,"/public: "); | |
880 | if (!flagpublic && !(pmod && flagremote) && | |
881 | !case_equals(action,ACTION_HELP)) | |
882 | strerr_die2x(100,FATAL,ERR_NOT_PUBLIC); | |
5b62e993 | 883 | |
f8beb284 MW |
884 | if (flagdig == FLD_DENY) |
885 | if (!pmod || !flagremote) /* only mods can do */ | |
886 | strerr_die1x(100,ERR_NOT_ALLOWED); | |
887 | ||
888 | if (act == AC_NONE) { /* none of the above */ | |
889 | if (case_equals(action,ACTION_SUBSCRIBE) || | |
890 | case_equals(action,ALT_SUBSCRIBE)) | |
891 | act = AC_SUBSCRIBE; | |
892 | else if (case_equals(action,ACTION_UNSUBSCRIBE) | |
893 | || case_equals(action,ALT_UNSUBSCRIBE)) | |
894 | act = AC_UNSUBSCRIBE; | |
895 | else if (str_start(action,ACTION_SC)) act = AC_SC; | |
5b62e993 | 896 | } |
5b62e993 | 897 | |
f8beb284 MW |
898 | if (!stralloc_copy(&from,&outlocal)) die_nomem(); |
899 | if (!stralloc_cats(&from,"-return-@")) die_nomem(); | |
900 | if (!stralloc_cat(&from,&outhost)) die_nomem(); | |
901 | if (!stralloc_0(&from)) die_nomem(); | |
5b62e993 | 902 | |
f8beb284 MW |
903 | if (qmail_open(&qq,(stralloc *) 0) == -1) |
904 | strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); | |
905 | msg_headers(); | |
5b62e993 | 906 | |
f8beb284 MW |
907 | if (act == AC_SUBSCRIBE) { |
908 | if (pmod && flagremote) { | |
909 | doconfirm(ACTION_TC); | |
910 | copy(&qq,"text/mod-sub-confirm",flagcd,FATAL); | |
911 | copybottom(); | |
912 | qmail_to(&qq,pmod); | |
913 | } else if (flagsubconf) { | |
914 | doconfirm(ACTION_SC); | |
915 | copy(&qq,"text/sub-confirm",flagcd,FATAL); | |
916 | copybottom(); | |
917 | qmail_to(&qq,target.s); | |
918 | } else { /* normal subscribe, no confirm */ | |
919 | r = geton(action); /* should be rarely used. */ | |
920 | copybottom(); | |
921 | if (flagnotify) qmail_to(&qq,target.s); | |
922 | if (r && flagverbose > 1) to_owner(); | |
923 | } | |
5b62e993 | 924 | |
f8beb284 MW |
925 | } else if (act == AC_SC) { |
926 | if (hashok(action,ACTION_SC)) { | |
927 | if (flagmod && !(pmod && str_equal(sender,target.s))) { | |
928 | store_from(&fromline,target.s); /* save from line, if requested */ | |
929 | /* since transaction not complete */ | |
930 | doconfirm(ACTION_TC); | |
931 | copy(&qq,"text/mod-sub-confirm",flagcd,FATAL); | |
932 | copybottom(); | |
933 | sendtomods(); | |
934 | } else { | |
935 | r = geton(action); | |
936 | copybottom(); | |
937 | qmail_to(&qq,target.s); | |
938 | if (r && flagverbose > 1) to_owner(); | |
939 | } | |
940 | } else { | |
941 | doconfirm(ACTION_SC); | |
942 | copy(&qq,"text/sub-bad",flagcd,FATAL); | |
943 | copybottom(); | |
944 | qmail_to(&qq,target.s); | |
5b62e993 | 945 | } |
5b62e993 | 946 | |
f8beb284 MW |
947 | } else if (str_start(action,ACTION_TC)) { |
948 | if (hashok(action,ACTION_TC)) { | |
949 | r = geton(action); | |
950 | mod_bottom(); | |
951 | if (flagnotify) qmail_to(&qq,target.s); /* unless suppressed */ | |
952 | if (r && flagverbose > 1) to_owner(); | |
953 | } else { | |
954 | if (!pmod || !flagremote) /* else anyone can get a good -tc. */ | |
955 | die_cookie(); | |
956 | doconfirm(ACTION_TC); | |
957 | copy(&qq,"text/sub-bad",flagcd,FATAL); | |
958 | copybottom(); | |
959 | qmail_to(&qq,pmod); | |
960 | } | |
961 | ||
962 | } else if (act == AC_UNSUBSCRIBE) { | |
963 | if (flagunsubconf) { | |
964 | if (pmod && flagremote) { | |
965 | doconfirm(ACTION_VC); | |
966 | copy(&qq,"text/mod-unsub-confirm",flagcd,FATAL); | |
967 | copybottom(); | |
968 | qmail_to(&qq,pmod); | |
969 | } else { | |
970 | doconfirm(ACTION_UC); | |
971 | copy(&qq,"text/unsub-confirm",flagcd,FATAL); | |
972 | copybottom(); | |
973 | qmail_to(&qq,target.s); | |
5b62e993 | 974 | } |
f8beb284 MW |
975 | } else if (flagunsubismod && flagmod) { |
976 | doconfirm(ACTION_VC); | |
977 | copy(&qq,"text/mod-unsub-confirm",flagcd,FATAL); | |
978 | copybottom(); | |
979 | sendtomods(); | |
980 | } else { | |
981 | r = getoff(action); | |
982 | copybottom(); | |
983 | if (!r || flagnotify) qmail_to(&qq,target.s); | |
984 | /* tell owner if problems (-Q) or anyway (-QQ) */ | |
985 | if (flagverbose && (!r || flagverbose > 1)) to_owner(); | |
986 | } | |
987 | ||
988 | } else if (str_start(action,ACTION_UC)) { | |
989 | if (hashok(action,ACTION_UC)) { | |
990 | /* unsub is moderated only on moderated list if -m unless the */ | |
991 | /* target == sender == a moderator */ | |
992 | if (flagunsubismod && flagmod) { | |
993 | doconfirm(ACTION_VC); | |
994 | copy(&qq,"text/mod-unsub-confirm",flagcd,FATAL); | |
995 | copybottom(); | |
996 | sendtomods(); | |
997 | } else { | |
998 | r = getoff(action); | |
999 | copybottom(); | |
1000 | if (!r || flagnotify) qmail_to(&qq,target.s); | |
1001 | /* tell owner if problems (-Q) or anyway (-QQ) */ | |
1002 | if (flagverbose && (!r || flagverbose > 1)) to_owner(); | |
5b62e993 | 1003 | } |
f8beb284 MW |
1004 | } else { |
1005 | doconfirm(ACTION_UC); | |
1006 | copy(&qq,"text/unsub-bad",flagcd,FATAL); | |
1007 | copybottom(); | |
1008 | qmail_to(&qq,target.s); | |
1009 | } | |
1010 | ||
1011 | } else if (str_start(action,ACTION_VC)) { | |
1012 | if (hashok(action,ACTION_VC)) { | |
1013 | r = getoff(action); | |
1014 | if (!r && flagmod) | |
1015 | strerr_die2x(0,INFO,ERR_UNSUB_NOP); | |
1016 | mod_bottom(); | |
1017 | if (r) { /* success to target */ | |
1018 | qmail_to(&qq,target.s); | |
1019 | if (flagverbose > 1) to_owner(); | |
1020 | } else /* NOP to sender = admin. Will take */ | |
1021 | qmail_to(&qq,sender); /* care of it. No need to tell owner */ | |
1022 | /* if list is moderated skip - otherwise bad with > 1 mod */ | |
1023 | } else { | |
1024 | if (!pmod || !flagremote) /* else anyone can get a good -vc. */ | |
1025 | die_cookie(); | |
1026 | doconfirm(ACTION_VC); | |
1027 | copy(&qq,"text/unsub-bad",flagcd,FATAL); | |
1028 | copybottom(); | |
1029 | qmail_to(&qq,pmod); | |
1030 | } | |
1031 | ||
1032 | } else if (act == AC_LIST || act == AC_LISTN) { | |
1033 | ||
1034 | if (!flaglist || (!flagmod && !flagremote)) | |
1035 | strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE); | |
1036 | if (!pmod) | |
1037 | strerr_die2x(100,FATAL,ERR_NOT_ALLOWED); | |
1038 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1039 | transferenc(); | |
1040 | copy(&qq,"text/top",flagcd,FATAL); | |
1041 | ||
1042 | if (act == AC_LIST) { | |
1043 | (void) code_qput(TXT_LISTMEMBERS,str_len(TXT_LISTMEMBERS)); | |
1044 | i = putsubs(workdir,0L,52L,code_subto,1,FATAL); | |
1045 | } else /* listn */ | |
1046 | i = putsubs(workdir,0L,52L,dummy_to,1,FATAL); | |
1047 | ||
1048 | (void) code_qput("\n ======> ",11); | |
1049 | (void) code_qput(strnum,fmt_ulong(strnum,i)); | |
1050 | (void) code_qput("\n",1); | |
1051 | copybottom(); | |
1052 | qmail_to(&qq,pmod); | |
1053 | ||
1054 | } else if (act == AC_LOG) { | |
1055 | action += actlen; | |
1056 | if (*action == '.' || *action == '_') ++action; | |
1057 | if (!flaglist || !flagremote) | |
1058 | strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE); | |
1059 | if (!pmod) | |
1060 | strerr_die2x(100,FATAL,ERR_NOT_ALLOWED); | |
1061 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1062 | transferenc(); | |
1063 | searchlog(workdir,action,code_subto,FATAL); | |
1064 | copybottom(); | |
1065 | qmail_to(&qq,pmod); | |
1066 | ||
1067 | } else if (act == AC_EDIT) { | |
1068 | /* only remote admins and only if -e is specified may edit */ | |
1069 | if (!flagedit || !flagremote) | |
1070 | strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE); | |
1071 | if (!pmod) | |
1072 | strerr_die2x(100,FATAL,ERR_NOT_ALLOWED); | |
1073 | len = str_len(ACTION_EDIT); | |
1074 | if (!case_starts(action,ACTION_EDIT)) | |
1075 | len = str_len(ALT_EDIT); | |
1076 | if (action[len]) { /* -edit.file, not just -edit */ | |
1077 | if (action[len] != '.') | |
1078 | strerr_die2x(100,FATAL,ERR_BAD_REQUEST); | |
1079 | if (!stralloc_copys(&fnedit,"text/")) die_nomem(); | |
1080 | if (!stralloc_cats(&fnedit,action+len+1)) die_nomem(); | |
1081 | if (!stralloc_0(&fnedit)) die_nomem(); | |
1082 | case_lowerb(fnedit.s,fnedit.len); | |
1083 | i = 5; /* after the "text/" */ | |
1084 | while ((ch = fnedit.s[i++])) { | |
1085 | if (((ch > 'z') || (ch < 'a')) && (ch != '_')) | |
1086 | strerr_die2x(100,FATAL,ERR_BAD_NAME); | |
1087 | if (ch == '_') fnedit.s[i-1] = '-'; | |
1088 | } | |
1089 | switch(slurp(fnedit.s,&text,1024)) { /* entire file! */ | |
1090 | case -1: | |
1091 | strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fnedit.s,": "); | |
1092 | case 0: | |
1093 | strerr_die5x(100,FATAL,dir,"/",fnedit.s,ERR_NOEXIST); | |
1094 | } | |
1095 | if (!stralloc_copy(&line,&text)) die_nomem(); | |
1096 | { /* get rid of nulls to use cookie */ | |
1097 | register char *s; register unsigned int n; | |
1098 | s = line.s; n = line.len; | |
1099 | while(n--) { if (!*s) *s = '_'; ++s; } | |
1100 | } | |
1101 | if (!stralloc_cat(&line,&fnedit)) die_nomem(); /* including '\0' */ | |
1102 | strnum[fmt_ulong(strnum,(unsigned long) when)] = 0; | |
1103 | cookie(hash,key.s,key.len,strnum,line.s,"-e"); | |
1104 | if (!stralloc_copy(&confirm,&outlocal)) die_nomem(); | |
1105 | if (!stralloc_append(&confirm,"-")) die_nomem(); | |
1106 | if (!stralloc_catb(&confirm,ACTION_ED,LENGTH_ED)) die_nomem(); | |
1107 | if (!stralloc_cats(&confirm,strnum)) die_nomem(); | |
1108 | if (!stralloc_append(&confirm,".")) die_nomem(); | |
1109 | /* action part has been checked for bad chars */ | |
1110 | if (!stralloc_cats(&confirm,action + len + 1)) die_nomem(); | |
1111 | if (!stralloc_append(&confirm,".")) die_nomem(); | |
1112 | if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem(); | |
1113 | if (!stralloc_append(&confirm,"@")) die_nomem(); | |
1114 | if (!stralloc_cat(&confirm,&outhost)) die_nomem(); | |
1115 | if (!stralloc_0(&confirm)) die_nomem(); | |
1116 | set_cpconfirm(confirm.s); | |
1117 | ||
1118 | qmail_puts(&qq,"Reply-To: "); | |
1119 | if (!quote2("ed,confirm.s)) die_nomem(); | |
1120 | qmail_put(&qq,quoted.s,quoted.len); | |
1121 | qmail_puts(&qq,"\n"); | |
1122 | if (!stralloc_0(&confirm)) die_nomem(); | |
1123 | ||
1124 | qmail_puts(&qq,TXT_EDIT_RESPONSE); | |
1125 | qmail_puts(&qq,action+len+1); /* has the '_' not '-' */ | |
1126 | qmail_puts(&qq,TXT_EDIT_FOR); | |
1127 | if (!quote("ed,&outlocal)) die_nomem(); | |
1128 | qmail_put(&qq,quoted.s,quoted.len); | |
1129 | qmail_puts(&qq,"@"); | |
1130 | qmail_put(&qq,outhost.s,outhost.len); | |
1131 | qmail_puts(&qq,"\n"); | |
1132 | transferenc(); | |
1133 | copy(&qq,"text/top",flagcd,FATAL); | |
1134 | copy(&qq,"text/edit-do",flagcd,FATAL); | |
1135 | (void) code_qput(TXT_EDIT_START,str_len(TXT_EDIT_START)); | |
1136 | (void) code_qput("\n",1); | |
1137 | (void) code_qput(text.s,text.len); | |
1138 | (void) code_qput(TXT_EDIT_END,str_len(TXT_EDIT_END)); | |
1139 | (void) code_qput("\n",1); | |
1140 | ||
1141 | } else { /* -edit only, so output list of editable files */ | |
1142 | qmail_puts(&qq,TXT_EDIT_LIST); | |
1143 | transferenc(); | |
1144 | copy(&qq,"text/top",flagcd,FATAL); | |
1145 | copy(&qq,"text/edit-list",flagcd,FATAL); | |
1146 | } | |
1147 | qmail_puts(&qq,"\n\n"); | |
1148 | copybottom(); | |
1149 | qmail_to(&qq,pmod); | |
1150 | ||
1151 | } else if (str_start(action,ACTION_ED)) { | |
1152 | datetime_sec u; | |
1153 | int flaggoodfield; | |
1154 | x = action + LENGTH_ED; | |
1155 | x += scan_ulong(x,&u); | |
1156 | if ((u > when) || (u < when - 100000)) die_cookie(); | |
1157 | if (*x == '.') ++x; | |
1158 | fname = x; | |
1159 | x += str_chr(x,'.'); | |
1160 | if (!*x) die_cookie(); | |
1161 | *x = (char) 0; | |
1162 | ++x; | |
1163 | if (!stralloc_copys(&fnedit,"text/")) die_nomem(); | |
1164 | if (!stralloc_cats(&fnedit,fname)) die_nomem(); | |
1165 | if (!stralloc_0(&fnedit)) die_nomem(); | |
1166 | y = fnedit.s + 5; /* after "text/" */ | |
1167 | while (*++y) { /* Name should be guaranteed by the cookie, */ | |
1168 | /* but better safe than sorry ... */ | |
1169 | if (((*y > 'z') || (*y < 'a')) && (*y != '_')) | |
1170 | strerr_die2x(100,FATAL,ERR_BAD_NAME); | |
1171 | if (*y == '_') *y = '-'; | |
1172 | } | |
1173 | ||
1174 | lock(); /* file must not change while here */ | |
1175 | ||
1176 | switch (slurp(fnedit.s,&text,1024)) { | |
1177 | case -1: | |
1178 | strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fnedit.s,": "); | |
1179 | case 0: | |
1180 | strerr_die5x(100,FATAL,dir,"/",fnedit.s,ERR_NOEXIST); | |
1181 | } | |
1182 | if (!stralloc_copy(&line,&text)) die_nomem(); | |
1183 | { /* get rid of nulls to use cookie */ | |
1184 | register char *s; register unsigned int n; | |
1185 | s = line.s; n = line.len; | |
1186 | while(n--) { if (!*s) *s = '_'; ++s; } | |
1187 | } | |
1188 | if (!stralloc_cat(&line,&fnedit)) die_nomem(); /* including '\0' */ | |
1189 | strnum[fmt_ulong(strnum,(unsigned long) u)] = 0; | |
1190 | cookie(hash,key.s,key.len,strnum,line.s,"-e"); | |
1191 | if (str_len(x) != COOKIE) die_cookie(); | |
1192 | if (byte_diff(hash,COOKIE,x)) die_cookie(); | |
1193 | /* cookie is ok, file exists, lock's on, new file ends in '_' */ | |
1194 | if (!stralloc_copys(&fneditn,fnedit.s)) die_nomem(); | |
1195 | if (!stralloc_append(&fneditn,"_")) die_nomem(); | |
1196 | if (!stralloc_0(&fneditn)) die_nomem(); | |
1197 | fd = open_trunc(fneditn.s); | |
1198 | if (fd == -1) | |
1199 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": "); | |
1200 | substdio_fdbuf(&sstext,write,fd,textbuf,sizeof(textbuf)); | |
1201 | if (!stralloc_copys("ed,"")) die_nomem(); /* clear */ | |
1202 | if (!stralloc_copys(&text,"")) die_nomem(); | |
1203 | ||
1204 | for (;;) { /* get message body */ | |
1205 | if (getln(&ssin,&line,&match,'\n') == -1) | |
1206 | strerr_die2sys(111,FATAL,ERR_READ_INPUT); | |
1207 | if (!match) break; | |
1208 | if (!stralloc_cat(&text,&line)) die_nomem(); | |
1209 | } | |
1210 | if (encin) { /* decode if necessary */ | |
1211 | if (encin == 'B') | |
1212 | decodeB(text.s,text.len,&line,FATAL); | |
1213 | else | |
1214 | decodeQ(text.s,text.len,&line,FATAL); | |
1215 | if (!stralloc_copy(&text,&line)) die_nomem(); | |
1216 | } | |
1217 | cp = text.s; | |
1218 | cpafter = text.s+text.len; | |
1219 | flaggoodfield = 0; | |
1220 | flagdone = 0; | |
1221 | len = 0; | |
1222 | while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) { | |
1223 | i = byte_chr(cp,cpnext-cp,'%'); | |
1224 | if (i != (unsigned int) (cpnext - cp)) { | |
1225 | if (!flaggoodfield) { /* TXT_EDIT_START/END */ | |
1226 | if (case_startb(cp+i,cpnext-cp-i,TXT_EDIT_START)) { | |
1227 | /* start tag. Store users 'quote characters', e.g. '> ' */ | |
1228 | if (!stralloc_copyb("ed,cp,i)) die_nomem(); | |
1229 | flaggoodfield = 1; | |
1230 | cp = cpnext + 1; | |
1231 | cpfirst = cp; | |
1232 | continue; | |
1233 | } | |
1234 | } else | |
1235 | if (case_startb(cp+i,cpnext-cp-i,TXT_EDIT_END)) { | |
1236 | flagdone = 1; | |
1237 | break; | |
1238 | } | |
1239 | } | |
1240 | if (flaggoodfield) { | |
1241 | if ((len += cpnext - cp - quoted.len + 1) > MAXEDIT) | |
1242 | strerr_die1x(100,ERR_EDSIZE); | |
1243 | ||
1244 | if (quoted.len && cpnext-cp >= (int) quoted.len && | |
1245 | !str_diffn(cp,quoted.s,quoted.len)) | |
1246 | cp += quoted.len; /* skip quoting characters */ | |
1247 | cplast = cpnext - 1; | |
1248 | if (*cplast == '\r') /* CRLF -> '\n' for base64 encoding */ | |
1249 | *cplast = '\n'; | |
1250 | else | |
1251 | ++cplast; | |
1252 | if (substdio_put(&sstext,cp,cplast-cp+1) == -1) | |
1253 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": "); | |
1254 | } | |
1255 | cp = cpnext + 1; | |
1256 | } | |
1257 | if (!flagdone) | |
1258 | strerr_die2x(100,FATAL,ERR_NO_MARK); | |
1259 | if (substdio_flush(&sstext) == -1) | |
1260 | strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": "); | |
1261 | if (fsync(fd) == -1) | |
1262 | strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",fneditn.s,": "); | |
1263 | if (fchmod(fd, 0600) == -1) | |
1264 | strerr_die6sys(111,FATAL,ERR_CHMOD,dir,"/",fneditn.s,": "); | |
1265 | if (close(fd) == -1) | |
1266 | strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",fneditn.s,": "); | |
1267 | if (rename(fneditn.s,fnedit.s) == -1) | |
1268 | strerr_die6sys(111,FATAL,ERR_MOVE,dir,"/",fneditn.s,": "); | |
1269 | ||
1270 | unlock(); | |
1271 | qmail_puts(&qq,TXT_EDIT_SUCCESS); | |
1272 | qmail_puts(&qq,fname); | |
1273 | qmail_puts(&qq,TXT_EDIT_FOR); | |
1274 | if (!quote("ed,&outlocal)) die_nomem(); | |
1275 | qmail_put(&qq,quoted.s,quoted.len); | |
1276 | qmail_puts(&qq,"@"); | |
1277 | qmail_put(&qq,outhost.s,outhost.len); | |
1278 | qmail_puts(&qq,"\n"); | |
1279 | transferenc(); | |
1280 | copy(&qq,"text/top",flagcd,FATAL); | |
1281 | copy(&qq,"text/edit-done",flagcd,FATAL); | |
1282 | copybottom(); | |
1283 | qmail_to(&qq,sender); /* not necessarily from mod */ | |
1284 | ||
1285 | } else if (act == AC_GET) { | |
1286 | ||
5b62e993 MW |
1287 | unsigned long u; |
1288 | struct stat st; | |
1289 | char ch; | |
1290 | int r; | |
f8beb284 MW |
1291 | unsigned int pos; |
1292 | ||
1293 | if (!flagget) | |
1294 | strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE); | |
1295 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1296 | transferenc(); | |
1297 | copy(&qq,"text/top",flagcd,FATAL); | |
1298 | ||
1299 | pos = str_len(ACTION_GET); | |
1300 | if (!case_starts(action,ACTION_GET)) | |
1301 | pos = str_len(ALT_GET); | |
5b62e993 | 1302 | |
f8beb284 MW |
1303 | if (action[pos] == '.' || action [pos] == '_') pos++; |
1304 | scan_ulong(action + pos,&u); | |
5b62e993 MW |
1305 | |
1306 | if (!stralloc_copys(&line,"archive/")) die_nomem(); | |
1307 | if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,u / 100))) die_nomem(); | |
1308 | if (!stralloc_cats(&line,"/")) die_nomem(); | |
1309 | if (!stralloc_catb(&line,strnum,fmt_uint0(strnum,(unsigned int) (u % 100),2))) die_nomem(); | |
1310 | if (!stralloc_0(&line)) die_nomem(); | |
1311 | ||
1312 | fd = open_read(line.s); | |
1313 | if (fd == -1) | |
1314 | if (errno != error_noent) | |
f8beb284 | 1315 | strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": "); |
5b62e993 | 1316 | else |
f8beb284 | 1317 | copy(&qq,"text/get-bad",flagcd,FATAL); |
5b62e993 MW |
1318 | else { |
1319 | if (fstat(fd,&st) == -1) | |
f8beb284 | 1320 | copy(&qq,"text/get-bad",flagcd,FATAL); |
5b62e993 | 1321 | else if (!(st.st_mode & 0100)) |
f8beb284 | 1322 | copy(&qq,"text/get-bad",flagcd,FATAL); |
5b62e993 MW |
1323 | else { |
1324 | substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); | |
1325 | qmail_puts(&qq,"> "); | |
1326 | for (;;) { | |
1327 | r = substdio_get(&sstext,&ch,1); | |
f8beb284 | 1328 | if (r == -1) strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); |
5b62e993 MW |
1329 | if (r == 0) break; |
1330 | qmail_put(&qq,&ch,1); | |
1331 | if (ch == '\n') qmail_puts(&qq,"> "); | |
1332 | } | |
1333 | qmail_puts(&qq,"\n"); | |
1334 | } | |
1335 | close(fd); | |
1336 | } | |
f8beb284 MW |
1337 | copybottom(); |
1338 | qmail_to(&qq,target.s); | |
5b62e993 | 1339 | |
f8beb284 MW |
1340 | } else if (case_starts(action,ACTION_QUERY) || |
1341 | case_starts(action,ALT_QUERY)) { | |
1342 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1343 | transferenc(); | |
1344 | copy(&qq,"text/top",flagcd,FATAL); | |
1345 | if (pmod) { /* pmod points to static storage in issub(). Need to do this */ | |
1346 | /* before calling issub() again */ | |
1347 | if (!stralloc_copys(&to,pmod)) die_nomem(); | |
1348 | if (!stralloc_0(&to)) die_nomem(); | |
1349 | } else { | |
1350 | if (!stralloc_copy(&to,&target)) die_nomem(); | |
1351 | } | |
1352 | if (issub(workdir,target.s,(char *) 0,FATAL)) | |
1353 | copy(&qq,"text/sub-nop",flagcd,FATAL); | |
1354 | else | |
1355 | copy(&qq,"text/unsub-nop",flagcd,FATAL); | |
1356 | copybottom(); | |
1357 | qmail_to(&qq,to.s); | |
5b62e993 | 1358 | |
f8beb284 MW |
1359 | } else if (case_starts(action,ACTION_INFO) || |
1360 | case_starts(action,ALT_INFO)) { | |
1361 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1362 | transferenc(); | |
1363 | copy(&qq,"text/top",flagcd,FATAL); | |
1364 | copy(&qq,"text/info",flagcd,FATAL); | |
1365 | copybottom(); | |
1366 | qmail_to(&qq,target.s); | |
5b62e993 | 1367 | |
f8beb284 MW |
1368 | } else if (case_starts(action,ACTION_FAQ) || |
1369 | case_starts(action,ALT_FAQ)) { | |
1370 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1371 | transferenc(); | |
1372 | copy(&qq,"text/top",flagcd,FATAL); | |
1373 | copy(&qq,"text/faq",flagcd,FATAL); | |
1374 | copybottom(); | |
1375 | qmail_to(&qq,target.s); | |
5b62e993 | 1376 | |
f8beb284 MW |
1377 | } else if (pmod && (act == AC_HELP)) { |
1378 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1379 | transferenc(); | |
1380 | copy(&qq,"text/top",flagcd,FATAL); | |
1381 | copy(&qq,"text/mod-help",flagcd,FATAL); | |
1382 | copy(&qq,"text/help",flagcd,FATAL); | |
1383 | copybottom(); | |
1384 | qmail_to(&qq,pmod); | |
5b62e993 | 1385 | |
f8beb284 MW |
1386 | } else { |
1387 | act = AC_HELP; | |
1388 | qmail_puts(&qq,TXT_EZMLM_RESPONSE); | |
1389 | transferenc(); | |
1390 | copy(&qq,"text/top",flagcd,FATAL); | |
1391 | copy(&qq,"text/help",flagcd,FATAL); | |
1392 | copybottom(); | |
1393 | qmail_to(&qq,sender); | |
1394 | } | |
1395 | ||
1396 | if (*(err = qmail_close(&qq)) == '\0') { | |
5b62e993 | 1397 | strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; |
f8beb284 | 1398 | closesql(); |
5b62e993 | 1399 | strerr_die2x(0,"ezmlm-manage: info: qp ",strnum); |
f8beb284 MW |
1400 | } else { |
1401 | closesql(); | |
1402 | strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1); | |
5b62e993 MW |
1403 | } |
1404 | } | |
f8beb284 | 1405 |