16 #include "readwrite.h"
21 #include "date822fmt.h"
23 #include "subscribe.h"
26 #define FATAL "ezmlm-manage: fatal: "
27 void die_usage() { strerr_die1x(100,"ezmlm-manage: usage: ezmlm-manage dir"); }
28 void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
31 strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
34 stralloc inhost = {0};
35 stralloc outhost = {0};
36 stralloc inlocal = {0};
37 stralloc outlocal = {0};
39 stralloc mailinglist = {0};
44 char strnum[FMT_ULONG];
45 char date[DATE822FMT];
47 datetime_sec hashdate;
48 stralloc target = {0};
49 stralloc confirm = {0};
51 stralloc quoted = {0};
60 x += scan_ulong(x,&u);
62 if (hashdate > when) return 0;
63 if (hashdate < when - 1000000) return 0;
66 strnum[fmt_ulong(strnum,u)] = 0;
67 cookie(hash,key.s,key.len,strnum,target.s,action + 1);
70 if (str_len(x) != COOKIE) return 0;
71 return byte_equal(hash,COOKIE,x);
75 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
77 qmail_put(&qq,buf,len);
81 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
84 substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
85 substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
98 strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
100 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
102 if (getln(&sstext,&line,&match,'\n') == -1)
103 strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
106 if (line.s[0] == '!') {
107 if (line.s[1] == 'R') {
109 qmail_puts(&qq,confirm.s);
110 qmail_puts(&qq,"\n");
113 if (line.s[1] == 'A') {
115 qmail_puts(&qq,target.s);
116 qmail_puts(&qq,"\n");
121 qmail_put(&qq,line.s,line.len);
130 stralloc mydtline = {0};
153 if (!dir) die_usage();
155 sender = env_get("SENDER");
156 if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
157 local = env_get("LOCAL");
158 if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
159 host = env_get("HOST");
160 if (!host) strerr_die2x(100,FATAL,"HOST not set");
163 strerr_die2x(100,FATAL,"I don't reply to bounce messages (#5.7.2)");
164 if (!sender[str_chr(sender,'@')])
165 strerr_die2x(100,FATAL,"I don't reply to senders without host names (#5.7.2)");
166 if (str_equal(sender,"#@[]"))
167 strerr_die2x(100,FATAL,"I don't reply to bounce messages (#5.7.2)");
169 if (chdir(dir) == -1)
170 strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
172 switch(slurp("key",&key,32)) {
174 strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
176 strerr_die3x(100,FATAL,dir,"/key does not exist");
178 getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
179 getconf_line(&inhost,"inhost",1,FATAL,dir);
180 getconf_line(&inlocal,"inlocal",1,FATAL,dir);
181 getconf_line(&outhost,"outhost",1,FATAL,dir);
182 getconf_line(&outlocal,"outlocal",1,FATAL,dir);
184 if (inhost.len != str_len(host)) die_badaddr();
185 if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
186 if (inlocal.len > str_len(local)) die_badaddr();
187 if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
189 action = local + inlocal.len;
191 switch(slurp("public",&line,1)) {
193 strerr_die4sys(111,FATAL,"unable to read ",dir,"/public: ");
195 strerr_die2x(100,FATAL,"sorry, I've been told to reject all requests (#5.7.2)");
198 if (!stralloc_copys(&target,sender)) die_nomem();
200 i = 1 + str_chr(action + 1,'-');
203 if (!stralloc_copys(&target,action + i + 1)) die_nomem();
204 i = byte_rchr(target.s,target.len,'=');
209 if (!stralloc_0(&target)) die_nomem();
210 if (!stralloc_copys(&confirm,"")) die_nomem();
212 if (qmail_open(&qq) == -1)
213 strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
215 qmail_puts(&qq,"Mailing-List: ");
216 qmail_put(&qq,mailinglist.s,mailinglist.len);
217 qmail_puts(&qq,"\nDate: ");
218 datetime_tai(&dt,when);
219 qmail_put(&qq,date,date822fmt(date,&dt));
220 qmail_puts(&qq,"Message-ID: <");
221 qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) when));
223 qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) getpid()));
224 qmail_puts(&qq,".ezmlm@");
225 qmail_put(&qq,outhost.s,outhost.len);
226 qmail_puts(&qq,">\nFrom: ");
227 if (!quote("ed,&outlocal)) die_nomem();
228 qmail_put(&qq,quoted.s,quoted.len);
229 qmail_puts(&qq,"-help@");
230 qmail_put(&qq,outhost.s,outhost.len);
231 qmail_puts(&qq,"\nTo: ");
232 if (!quote2("ed,target.s)) die_nomem();
233 qmail_put(&qq,quoted.s,quoted.len);
234 qmail_puts(&qq,"\n");
237 if (str_start(action,"-sc.")) flaghashok = hashok(action);
238 if (str_start(action,"-uc.")) flaghashok = hashok(action);
241 if (str_equal(action,"-subscribe")) flagconfirm = 1;
242 if (str_equal(action,"-unsubscribe")) flagconfirm = 1;
243 if (!flaghashok) flagconfirm = 1;
246 strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
247 cookie(hash,key.s,key.len,strnum,target.s,action + 1);
248 if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
249 if (!stralloc_cats(&confirm,"-")) die_nomem();
250 if (!stralloc_catb(&confirm,action + 1,1)) die_nomem();
251 if (!stralloc_cats(&confirm,"c.")) die_nomem();
252 if (!stralloc_cats(&confirm,strnum)) die_nomem();
253 if (!stralloc_cats(&confirm,".")) die_nomem();
254 if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem();
255 if (!stralloc_cats(&confirm,"-")) die_nomem();
256 i = str_rchr(target.s,'@');
257 if (!stralloc_catb(&confirm,target.s,i)) die_nomem();
259 if (!stralloc_cats(&confirm,"=")) die_nomem();
260 if (!stralloc_cats(&confirm,target.s + i + 1)) die_nomem();
262 if (!stralloc_cats(&confirm,"@")) die_nomem();
263 if (!stralloc_cat(&confirm,&outhost)) die_nomem();
264 if (!stralloc_0(&confirm)) die_nomem();
266 qmail_puts(&qq,"Reply-To: ");
267 if (!quote2("ed,confirm.s)) die_nomem();
268 qmail_put(&qq,quoted.s,quoted.len);
269 qmail_puts(&qq,"\n");
271 if (!stralloc_0(&confirm)) die_nomem();
273 qmail_puts(&qq,"Subject: ezmlm response\n");
275 if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem();
276 if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
277 if (!stralloc_cats(&mydtline,"@")) die_nomem();
278 if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
279 if (!stralloc_cats(&mydtline,"\n")) die_nomem();
281 qmail_put(&qq,mydtline.s,mydtline.len);
285 if (getln(&ssin,&line,&match,'\n') == -1)
286 strerr_die2sys(111,FATAL,"unable to read input: ");
288 if (line.len == 1) break;
289 if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
291 if (case_startb(line.s,line.len,"mailing-list:"))
292 strerr_die2x(100,FATAL,"incoming message has Mailing-List (#5.7.2)");
293 if (line.len == mydtline.len)
294 if (byte_equal(line.s,line.len,mydtline.s))
295 strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)");
296 if (case_startb(line.s,line.len,"delivered-to:"))
298 if (case_startb(line.s,line.len,"received:"))
302 qmail_put(&qq,line.s,line.len);
304 if (seek_begin(0) == -1)
305 strerr_die2sys(111,FATAL,"unable to seek input: ");
307 qmail_puts(&qq,"\n");
309 if (str_equal(action,"-subscribe"))
310 copy("text/sub-confirm");
311 else if (str_equal(action,"-unsubscribe"))
312 copy("text/unsub-confirm");
313 else if (str_start(action,"-sc.")) {
315 copy("text/sub-bad");
317 switch(subscribe(target.s,1)) {
318 case -1: strerr_die1(111,FATAL,&subscribe_err);
319 case -2: strerr_die1(100,FATAL,&subscribe_err);
320 case 1: log("+",target.s); copy("text/sub-ok"); break;
321 default: copy("text/sub-nop"); break;
324 else if (str_start(action,"-uc.")) {
326 copy("text/unsub-bad");
328 switch(subscribe(target.s,0)) {
329 case -1: strerr_die1(111,FATAL,&subscribe_err);
330 case -2: strerr_die1(100,FATAL,&subscribe_err);
331 case 1: log("-",target.s); copy("text/unsub-ok"); break;
332 default: copy("text/unsub-nop"); break;
335 else if (str_start(action,"-get.")) {
341 scan_ulong(action + 5,&u);
343 if (!stralloc_copys(&line,"archive/")) die_nomem();
344 if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,u / 100))) die_nomem();
345 if (!stralloc_cats(&line,"/")) die_nomem();
346 if (!stralloc_catb(&line,strnum,fmt_uint0(strnum,(unsigned int) (u % 100),2))) die_nomem();
347 if (!stralloc_0(&line)) die_nomem();
349 fd = open_read(line.s);
351 if (errno != error_noent)
352 strerr_die4sys(111,FATAL,"unable to open ",line.s,": ");
354 copy("text/get-bad");
356 if (fstat(fd,&st) == -1)
357 copy("text/get-bad");
358 else if (!(st.st_mode & 0100))
359 copy("text/get-bad");
361 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
362 qmail_puts(&qq,"> ");
364 r = substdio_get(&sstext,&ch,1);
365 if (r == -1) strerr_die4sys(111,FATAL,"unable to read ",line.s,": ");
367 qmail_put(&qq,&ch,1);
368 if (ch == '\n') qmail_puts(&qq,"> ");
370 qmail_puts(&qq,"\n");
380 qmail_puts(&qq,"Return-Path: <");
381 if (!quote2("ed,sender)) die_nomem();
382 qmail_put(&qq,quoted.s,quoted.len);
383 qmail_puts(&qq,">\n");
384 if (substdio_copy(&ssqq,&ssin2) != 0)
385 strerr_die2sys(111,FATAL,"unable to read input: ");
387 if (!stralloc_copy(&line,&outlocal)) die_nomem();
388 if (!stralloc_cats(&line,"-return-@")) die_nomem();
389 if (!stralloc_cat(&line,&outhost)) die_nomem();
390 if (!stralloc_0(&line)) die_nomem();
391 qmail_from(&qq,line.s);
393 qmail_to(&qq,target.s);
395 switch(qmail_close(&qq)) {
397 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
398 strerr_die2x(0,"ezmlm-manage: info: qp ",strnum);
400 /* don't worry about undoing actions; everything is idempotent */
401 strerr_die2x(111,FATAL,"temporary qmail-queue error");