Upstream qmail 1.01
[qmail] / qlist.c
CommitLineData
2117e02e
MW
1#include "sig.h"
2#include "readwrite.h"
3#include "substdio.h"
4#include "stralloc.h"
5#include "subfd.h"
6#include "getln.h"
7#include "alloc.h"
8#include "str.h"
9#include "env.h"
10#include "hfield.h"
11#include "case.h"
12#include "token822.h"
13#include "error.h"
14#include "gen_alloc.h"
15#include "gen_allocdefs.h"
16#include "headerbody.h"
17#include "exit.h"
18#include "open.h"
19#include "lock.h"
20#include "qmail.h"
21
22#define ADDRLIMIT 100
23
24void die() { _exit(100); }
25void die_temp() { _exit(111); }
26void die_nomem() {
27 substdio_putsflush(subfderr,"qlist: fatal: out of memory\n"); die_temp(); }
28void die_fork() {
29 substdio_putsflush(subfderr,"qlist: fatal: unable to fork\n"); die_temp(); }
30void die_nolock() {
31 substdio_putsflush(subfderr,"qlist: fatal: unable to open lock file\n"); die_temp(); }
32void die_boing() {
33 substdio_putsflush(subfderr,"qlist: fatal: I don't reply to bounces\n"); die(); }
34void die_badaddr() {
35 substdio_putsflush(subfderr,"qlist: fatal: sorry, I'm not allowed to use that address\n"); die(); }
36void die_qqperm() {
37 substdio_putsflush(subfderr,"qlist: fatal: permanent qmail-queue error\n"); die(); }
38void die_qqtemp() {
39 substdio_putsflush(subfderr,"qlist: fatal: temporary qmail-queue error\n"); die_temp(); }
40void die_usage() {
41 substdio_putsflush(subfderr,
42 "qlist: usage: qlist user-list@host user-list-request@host .qmail-list .qmail-list-request .qtemp-list owner [moreinfo]\n"); die(); }
43void die_read() {
44 if (errno == error_nomem) die_nomem();
45 substdio_putsflush(subfderr,"qlist: fatal: read error\n"); die_temp(); }
46void doordie(sa,r) stralloc *sa; int r; {
47 if (r == 1) return; if (r == -1) die_nomem();
48 substdio_putsflush(subfderr,"qlist: fatal: unable to parse this: ");
49 substdio_putflush(subfderr,sa->s,sa->len); die(); }
50
51int subjectaction = 0;
52int numcommands;
53
54int fdlock;
55
56struct qmail qqt;
57
58char *target;
59char *listathost;
60char *requestathost;
61char *qmaillist;
62char *qmailrequest;
63char *qtemplist;
64char *owner;
65char *moreinfo;
66
67char *dtline;
68char *returnpath;
69stralloc safrom = {0};
70stralloc sart = {0};
71
72int rwfrom(addr) token822_alloc *addr; { token822_reverse(addr);
73 if (token822_unquote(&safrom,addr) != 1) die_nomem();
74 token822_reverse(addr); return 1; }
75int rwrt(addr) token822_alloc *addr; { token822_reverse(addr);
76 if (token822_unquote(&sart,addr) != 1) die_nomem();
77 token822_reverse(addr); return 1; }
78
79GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
80GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
81static stralloc sauninit = {0}; saa savedh = {0};
82void savedh_append(h) stralloc *h; {
83 if (!saa_readyplus(&savedh,1)) die_nomem(); savedh.sa[savedh.len] = sauninit;
84 if (!stralloc_copy(savedh.sa + savedh.len,h)) die_nomem(); ++savedh.len; }
85void savedh_print() { int i; for (i = 0;i < savedh.len;++i)
86 qmail_put(&qqt,savedh.sa[i].s,savedh.sa[i].len); }
87
88void finishheader()
89{
90 int i;
91
92 if (sart.s)
93 { if (!stralloc_0(&sart)) die_nomem(); target = sart.s; }
94 else if (safrom.s)
95 { if (!stralloc_0(&safrom)) die_nomem(); target = safrom.s; }
96 else
97 target = returnpath;
98
99 for (i = 0;target[i];++i)
100 if (target[i] == '\n')
101 die_badaddr();
102 if (i > ADDRLIMIT) die_badaddr();
103 if (str_equal(target,"")) die_boing();
104 if (str_equal(target,"#@[]")) die_boing();
105
106 if (qmail_open(&qqt) == -1) die_fork();
107
108 qmail_puts(&qqt,dtline);
109 savedh_print();
110
111 qmail_puts(&qqt,"\n***** Text inserted by ");
112 qmail_puts(&qqt,requestathost);
113 qmail_puts(&qqt,"\n\
114*\n\
115* Hi! This is the qlist program. I'm handling subscriptions for the\n\
116* ");
117 qmail_puts(&qqt,listathost);
118 qmail_puts(&qqt," mailing list.\n\
119*\n");
120 if (moreinfo)
121 {
122 qmail_puts(&qqt,"* ");
123 qmail_puts(&qqt,moreinfo);
124 qmail_puts(&qqt,"\n*\n");
125 }
126 qmail_puts(&qqt,"* My human owner is ");
127 qmail_puts(&qqt,owner);
128 qmail_puts(&qqt,".\n\
129*\n\
130* To the recipient: This message was sent to me on your behalf. (Your\n\
131* address was listed in the Reply-To or From field.) For security,\n\
132* I'm forwarding this message to you, along with my notes.\n\
133*\n\
134* Anyway, to subscribe, send me an empty message. To unsubscribe, send me\n\
135* a message with the word UNSUBSCRIBE at the beginning of a line. Remember,\n\
136* my address is ");
137 qmail_puts(&qqt,requestathost);
138 qmail_puts(&qqt,".\n\
139*\n\
140* Now I'll look for requests inside this message...\n\
141*\n\
142*****\n");
143}
144
145substdio subin; char subinbuf[SUBSTDIO_INSIZE];
146substdio subout; char suboutbuf[SUBSTDIO_OUTSIZE];
147stralloc subline = {0};
148void subscribe(flagadd)
149int flagadd;
150{
151 int fdin;
152 int fdout;
153 int match;
154 int flagwasthere;
155
156 ++numcommands;
157
158 if (lock_ex(fdlock) == -1) goto bad;
159 fdin = open_read(qmaillist);
160 if (fdin == -1) goto badlock;
161 fdout = open_trunc(qtemplist);
162 if (fdout == -1) goto badinlock;
163 if (chmod(qtemplist,0700) == -1) goto badoutinlock;
164
165 flagwasthere = 0;
166
167 substdio_fdbuf(&subin,read,fdin,subinbuf,sizeof(subinbuf));
168 substdio_fdbuf(&subout,write,fdout,suboutbuf,sizeof(suboutbuf));
169 for (;;)
170 {
171 if (getln(&subin,&subline,&match,'\n') == -1) goto badoutinlock;
172 if (!match) break; /* goodbye partial lines */
173 if (subline.len == str_len(target) + 2)
174 if (!str_diffn(subline.s + 1,target,subline.len - 2))
175 if (subline.s[0] == '&')
176 {
177 flagwasthere = 1;
178 if (!flagadd)
179 continue;
180 }
181 if (substdio_put(&subout,subline.s,subline.len) == -1) goto badoutinlock;
182 }
183
184 if (flagadd && !flagwasthere)
185 {
186 if (substdio_puts(&subout,"&") == -1) goto badoutinlock;
187 if (substdio_puts(&subout,target) == -1) goto badoutinlock;
188 if (substdio_puts(&subout,"\n") == -1) goto badoutinlock;
189 }
190 if (substdio_flush(&subout) == -1) goto badoutinlock;
191
192 close(fdout);
193 close(fdin);
194 if (rename(qtemplist,qmaillist) == -1) goto badlock;
195 if (chmod(qmaillist,0500) == -1) goto badlock;
196
197 lock_un(fdlock);
198
199 qmail_puts(&qqt,"***** Text inserted by ");
200 qmail_puts(&qqt,requestathost);
201 qmail_puts(&qqt,"\n*\n* ");
202 if (flagadd)
203 if (flagwasthere)
204 {
205 qmail_puts(&qqt,"Acknowledgment: ");
206 qmail_puts(&qqt,target);
207 qmail_puts(&qqt," was already a subscriber.\n");
208 }
209 else
210 {
211 qmail_puts(&qqt,"Acknowledgment: ");
212 qmail_puts(&qqt,target);
213 qmail_puts(&qqt," is now a subscriber.\n");
214 }
215 else
216 if (flagwasthere)
217 {
218 qmail_puts(&qqt,"Acknowledgment: ");
219 qmail_puts(&qqt,target);
220 qmail_puts(&qqt," is no longer a subscriber.\n");
221 }
222 else
223 {
224 qmail_puts(&qqt,"Hmmm, I don't see ");
225 qmail_puts(&qqt,target);
226 qmail_puts(&qqt," on the subscription list.\n* I'll let my owner know.\n");
227 }
228 qmail_puts(&qqt,"*\n*****\n");
229 return;
230
231badoutinlock: close(fdout);
232badinlock: close(fdin);
233badlock: lock_un(fdlock);
234bad:
235 qmail_puts(&qqt,"***** Text inserted by ");
236 qmail_puts(&qqt,requestathost);
237 qmail_puts(&qqt,"\n*\n\
238* Oh no! Trouble making the new list. I'll let my owner know.\n\
239*\n\
240*****\n");
241}
242
243void dobody(h) stralloc *h;
244{
245 qmail_put(&qqt,h->s,h->len);
246 if (case_starts(h->s,"subs")) subscribe(1);
247 if (case_starts(h->s,"unsu")) subscribe(0);
248}
249
250stralloc hfbuf = {0};
251token822_alloc hfin = {0};
252token822_alloc hfrewrite = {0};
253token822_alloc hfaddr = {0};
254
255void doheaderfield(h)
256stralloc *h;
257{
258 char *x;
259 switch(hfield_known(h->s,h->len))
260 {
261 case H_CONTENTLENGTH: /* SVR4 silliness */
262 case H_CONTENTTYPE:
263 case H_CONTENTTRANSFERENCODING: /* A-bombs 4.2.1.5.2 is idiotic */
264 return;
265 case H_FROM:
266 doordie(h,token822_parse(&hfin,h,&hfbuf));
267 doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwfrom));
268 break;
269 case H_REPLYTO:
270 doordie(h,token822_parse(&hfin,h,&hfbuf));
271 doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwrt));
272 break;
273 case H_SUBJECT:
274 x = h->s + hfield_skipname(h->s,h->len);
275 if (!case_diffb(x,4,"subs")) subjectaction = 1;
276 if (!case_diffb(x,4,"unsu")) subjectaction = 2;
277 break;
278 }
279 savedh_append(h);
280}
281
282void main(argc,argv)
283int argc;
284char **argv;
285{
286 sig_pipeignore();
287
288 if (!(listathost = argv[1])) die_usage();
289 if (!(requestathost = argv[2])) die_usage();
290 if (!(qmaillist = argv[3])) die_usage();
291 if (!(qmailrequest = argv[4])) die_usage();
292 if (!(qtemplist = argv[5])) die_usage();
293 if (!(owner = argv[6])) die_usage();
294 moreinfo = argv[7];
295 if (!(returnpath = env_get("NEWSENDER"))) die_usage();
296 if (!(dtline = env_get("DTLINE"))) die_usage();
297
298 fdlock = open_append(qmailrequest);
299 if (fdlock == -1) die_nolock();
300
301 numcommands = 0;
302 if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1) die_read();
303 if (!numcommands)
304 {
305 qmail_puts(&qqt,"***** Text inserted by ");
306 qmail_puts(&qqt,requestathost);
307 qmail_puts(&qqt,"\n*\n* ");
308 if (subjectaction)
309 {
310 qmail_puts(&qqt,"\
311Hmmm, no commands? Let me check the Subject line...\n*\n*****\n");
312 subscribe(subjectaction == 1);
313 }
314 else
315 {
316 qmail_puts(&qqt,"\
317I didn't see any commands. I presume this is a subscription request.\n\
318*\n*****\n");
319 subscribe(1);
320 }
321 }
322
323 qmail_from(&qqt,returnpath);
324 qmail_to(&qqt,owner);
325 qmail_to(&qqt,target);
326
327 switch(qmail_close(&qqt))
328 {
329 case 0: _exit(0);
330 case QMAIL_TOOLONG: die_qqperm();
331 default: die_qqtemp();
332 }
333}