Commit | Line | Data |
---|---|---|
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 | ||
24 | void die() { _exit(100); } | |
25 | void die_temp() { _exit(111); } | |
26 | void die_nomem() { | |
27 | substdio_putsflush(subfderr,"qlist: fatal: out of memory\n"); die_temp(); } | |
28 | void die_fork() { | |
29 | substdio_putsflush(subfderr,"qlist: fatal: unable to fork\n"); die_temp(); } | |
30 | void die_nolock() { | |
31 | substdio_putsflush(subfderr,"qlist: fatal: unable to open lock file\n"); die_temp(); } | |
32 | void die_boing() { | |
33 | substdio_putsflush(subfderr,"qlist: fatal: I don't reply to bounces\n"); die(); } | |
34 | void die_badaddr() { | |
35 | substdio_putsflush(subfderr,"qlist: fatal: sorry, I'm not allowed to use that address\n"); die(); } | |
36 | void die_qqperm() { | |
37 | substdio_putsflush(subfderr,"qlist: fatal: permanent qmail-queue error\n"); die(); } | |
38 | void die_qqtemp() { | |
39 | substdio_putsflush(subfderr,"qlist: fatal: temporary qmail-queue error\n"); die_temp(); } | |
40 | void 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(); } | |
43 | void die_read() { | |
44 | if (errno == error_nomem) die_nomem(); | |
45 | substdio_putsflush(subfderr,"qlist: fatal: read error\n"); die_temp(); } | |
46 | void 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 | ||
51 | int subjectaction = 0; | |
52 | int numcommands; | |
53 | ||
54 | int fdlock; | |
55 | ||
56 | struct qmail qqt; | |
57 | ||
58 | char *target; | |
59 | char *listathost; | |
60 | char *requestathost; | |
61 | char *qmaillist; | |
62 | char *qmailrequest; | |
63 | char *qtemplist; | |
64 | char *owner; | |
65 | char *moreinfo; | |
66 | ||
67 | char *dtline; | |
68 | char *returnpath; | |
69 | stralloc safrom = {0}; | |
70 | stralloc sart = {0}; | |
71 | ||
72 | int rwfrom(addr) token822_alloc *addr; { token822_reverse(addr); | |
73 | if (token822_unquote(&safrom,addr) != 1) die_nomem(); | |
74 | token822_reverse(addr); return 1; } | |
75 | int rwrt(addr) token822_alloc *addr; { token822_reverse(addr); | |
76 | if (token822_unquote(&sart,addr) != 1) die_nomem(); | |
77 | token822_reverse(addr); return 1; } | |
78 | ||
79 | GEN_ALLOC_typedef(saa,stralloc,sa,len,a) | |
80 | GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) | |
81 | static stralloc sauninit = {0}; saa savedh = {0}; | |
82 | void 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; } | |
85 | void savedh_print() { int i; for (i = 0;i < savedh.len;++i) | |
86 | qmail_put(&qqt,savedh.sa[i].s,savedh.sa[i].len); } | |
87 | ||
88 | void 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 | ||
145 | substdio subin; char subinbuf[SUBSTDIO_INSIZE]; | |
146 | substdio subout; char suboutbuf[SUBSTDIO_OUTSIZE]; | |
147 | stralloc subline = {0}; | |
148 | void subscribe(flagadd) | |
149 | int 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 | ||
231 | badoutinlock: close(fdout); | |
232 | badinlock: close(fdin); | |
233 | badlock: lock_un(fdlock); | |
234 | bad: | |
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 | ||
243 | void 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 | ||
250 | stralloc hfbuf = {0}; | |
251 | token822_alloc hfin = {0}; | |
252 | token822_alloc hfrewrite = {0}; | |
253 | token822_alloc hfaddr = {0}; | |
254 | ||
255 | void doheaderfield(h) | |
256 | stralloc *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 | ||
282 | void main(argc,argv) | |
283 | int argc; | |
284 | char **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,"\ | |
311 | Hmmm, no commands? Let me check the Subject line...\n*\n*****\n"); | |
312 | subscribe(subjectaction == 1); | |
313 | } | |
314 | else | |
315 | { | |
316 | qmail_puts(&qqt,"\ | |
317 | I 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 | } |