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");