09d2b3e1cac5f0042d167dc350a8f9b123bd35ee
[ezmlm] / ezmlm-make.c
1 #include <sys/types.h>
2 #include <sys/time.h>
3 #include "sgetopt.h"
4 #include "stralloc.h"
5 #include "strerr.h"
6 #include "exit.h"
7 #include "readwrite.h"
8 #include "open.h"
9 #include "substdio.h"
10 #include "str.h"
11 #include "auto_bin.h"
12
13 #define FATAL "ezmlm-make: fatal: "
14
15 void die_usage()
16 {
17 strerr_die1x(100,"ezmlm-make: usage: ezmlm-make [ -aApP ] dir dot local host");
18 }
19 void die_relative()
20 {
21 strerr_die2x(100,FATAL,"dir must start with slash");
22 }
23 void die_newline()
24 {
25 strerr_die2x(100,FATAL,"newlines not allowed");
26 }
27 void die_quote()
28 {
29 strerr_die2x(100,FATAL,"quotes not allowed");
30 }
31 void die_nomem()
32 {
33 strerr_die2x(111,FATAL,"out of memory");
34 }
35
36 stralloc key = {0};
37 struct timeval tv;
38
39 void keyadd(u)
40 unsigned long u;
41 {
42 char ch;
43 ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
44 ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
45 ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
46 ch = u; if (!stralloc_append(&key,&ch)) die_nomem();
47 }
48
49 void keyaddtime()
50 {
51 gettimeofday(&tv,(struct timezone *) 0);
52 keyadd(tv.tv_usec);
53 }
54
55 char *dir;
56 char *dot;
57 char *local;
58 char *host;
59
60 stralloc dotplus = {0};
61 stralloc dirplus = {0};
62
63 void dirplusmake(slash)
64 char *slash;
65 {
66 if (!stralloc_copys(&dirplus,dir)) die_nomem();
67 if (!stralloc_cats(&dirplus,slash)) die_nomem();
68 if (!stralloc_0(&dirplus)) die_nomem();
69 }
70
71 void linkdotdir(dash,slash)
72 char *dash;
73 char *slash;
74 {
75 if (!stralloc_copys(&dotplus,dot)) die_nomem();
76 if (!stralloc_cats(&dotplus,dash)) die_nomem();
77 if (!stralloc_0(&dotplus)) die_nomem();
78 dirplusmake(slash);
79 if (symlink(dirplus.s,dotplus.s) == -1)
80 strerr_die4sys(111,FATAL,"unable to create ",dotplus.s,": ");
81 keyaddtime();
82 }
83
84 void dcreate(slash)
85 char *slash;
86 {
87 dirplusmake(slash);
88 if (mkdir(dirplus.s,0755) == -1)
89 strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
90 keyaddtime();
91 }
92
93 substdio ss;
94 char ssbuf[SUBSTDIO_OUTSIZE];
95
96 void fopen(slash)
97 char *slash;
98 {
99 int fd;
100
101 dirplusmake(slash);
102 fd = open_trunc(dirplus.s);
103 if (fd == -1)
104 strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
105
106 substdio_fdbuf(&ss,write,fd,ssbuf,sizeof(ssbuf));
107 }
108
109 void fput(buf,len)
110 char *buf;
111 unsigned int len;
112 {
113 if (substdio_bput(&ss,buf,len) == -1)
114 strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
115 }
116 void fputs(buf)
117 char *buf;
118 {
119 if (substdio_bputs(&ss,buf) == -1)
120 strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
121 }
122
123 void fclose()
124 {
125 if (substdio_flush(&ss) == -1)
126 strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
127 if (fsync(ss.fd) == -1)
128 strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
129 if (close(ss.fd) == -1) /* NFS stupidity */
130 strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
131 keyaddtime();
132 }
133
134 void main(argc,argv)
135 int argc;
136 char **argv;
137 {
138 int opt;
139 int flagarchived;
140 int flagpublic;
141
142 keyadd(getpid());
143 keyadd(getppid());
144 keyadd(getuid());
145 keyadd(getgid());
146 gettimeofday(&tv,(struct timezone *) 0);
147 keyadd(tv.tv_sec);
148
149 umask(077);
150
151 flagarchived = 1;
152 flagpublic = 1;
153
154 while ((opt = getopt(argc,argv,"aApP")) != opteof)
155 switch(opt) {
156 case 'a': flagarchived = 1; break;
157 case 'A': flagarchived = 0; break;
158 case 'p': flagpublic = 1; break;
159 case 'P': flagpublic = 0; break;
160 default:
161 die_usage();
162 }
163 argv += optind;
164
165 if (!(dir = *argv++)) die_usage();
166 if (!(dot = *argv++)) die_usage();
167 if (!(local = *argv++)) die_usage();
168 if (!(host = *argv++)) die_usage();
169
170 if (dir[0] != '/') die_relative();
171 if (dir[str_chr(dir,'\'')]) die_quote();
172 if (dir[str_chr(dir,'\n')]) die_newline();
173 if (local[str_chr(local,'\n')]) die_newline();
174 if (host[str_chr(host,'\n')]) die_newline();
175
176 dcreate("");
177 dcreate("/archive");
178 dcreate("/subscribers");
179 dcreate("/bounce");
180 dcreate("/text");
181
182
183 linkdotdir("-owner","/owner");
184 linkdotdir("-default","/manager");
185 linkdotdir("-return-default","/bouncer");
186 linkdotdir("","/editor");
187
188 fopen("/lock"); fclose();
189 fopen("/lockbounce"); fclose();
190 if (flagpublic) {
191 fopen("/public"); fclose();
192 }
193 if (flagarchived) {
194 fopen("/archived"); fclose();
195 }
196 fopen("/num"); fputs("0\n"); fclose();
197 fopen("/inhost"); fputs(host); fputs("\n"); fclose();
198 fopen("/outhost"); fputs(host); fputs("\n"); fclose();
199 fopen("/inlocal"); fputs(local); fputs("\n"); fclose();
200 fopen("/outlocal"); fputs(local); fputs("\n"); fclose();
201
202 fopen("/mailinglist");
203 fputs("contact ");
204 fputs(local); fputs("-help@"); fputs(host); fputs("; run by ezmlm\n");
205 fclose();
206
207 fopen("/owner");
208 fputs(dir); fputs("/Mailbox\n");
209 fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
210 fputs("' || exit 0\n");
211 fclose();
212
213 fopen("/manager");
214 fputs("|"); fputs(auto_bin); fputs("/ezmlm-manage '"); fputs(dir); fputs("'\n");
215 fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
216 fputs("' || exit 0\n");
217 fclose();
218
219 fopen("/editor");
220 fputs("|"); fputs(auto_bin); fputs("/ezmlm-reject\n");
221 fputs("|"); fputs(auto_bin); fputs("/ezmlm-send '"); fputs(dir); fputs("'\n");
222 fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
223 fputs("' || exit 0\n");
224 fclose();
225
226 fopen("/bouncer");
227 fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
228 fputs("' || exit 0\n");
229 fputs("|"); fputs(auto_bin); fputs("/ezmlm-weed\n");
230 fputs("|"); fputs(auto_bin); fputs("/ezmlm-return '"); fputs(dir); fputs("'\n");
231 fclose();
232
233 fopen("/headerremove");
234 fputs("\
235 return-path\n\
236 return-receipt-to\n\
237 content-length\n\
238 ");
239 fclose();
240
241 fopen("/headeradd");
242 fclose();
243
244
245 fopen("/text/top");
246 fputs("Hi! This is the ezmlm program. I'm managing the\n");
247 fputs(local); fputs("@"); fputs(host); fputs(" mailing list.\n\n");
248 fclose();
249
250 fopen("/text/bottom");
251 fputs("\n--- Here are the ezmlm command addresses.\n\
252 \n\
253 I can handle administrative requests automatically.\n\
254 Just send an empty note to any of these addresses:\n\n <");
255 fputs(local); fputs("-subscribe@"); fputs(host); fputs(">:\n");
256 fputs(" Receive future messages sent to the mailing list.\n\n <");
257 fputs(local); fputs("-unsubscribe@"); fputs(host); fputs(">:\n");
258 fputs(" Stop receiving messages.\n\n <");
259 fputs(local); fputs("-get.12345@"); fputs(host); fputs(">:\n");
260 fputs(" Retrieve a copy of message 12345 from the archive.\n\
261 \n\
262 DO NOT SEND ADMINISTRATIVE REQUESTS TO THE MAILING LIST!\n\
263 If you do, I won't see them, and subscribers will yell at you.\n\
264 \n\
265 To specify God@heaven.af.mil as your subscription address, send mail\n\
266 to <");
267 fputs(local); fputs("-subscribe-God=heaven.af.mil@"); fputs(host);
268 fputs(">.\n\
269 I'll send a confirmation message to that address; when you receive that\n\
270 message, simply reply to it to complete your subscription.\n\
271 \n");
272 fputs("\n--- Below this line is a copy of the request I received.\n\n");
273 fclose();
274
275 fopen("/text/sub-confirm");
276 fputs("To confirm that you would like\n\
277 \n\
278 !A\n\
279 \n\
280 added to this mailing list, please send an empty reply to this address:\n\
281 \n\
282 !R\n\
283 \n\
284 Your mailer should have a Reply feature that uses this address automatically.\n\
285 \n\
286 This confirmation serves two purposes. First, it verifies that I am able\n\
287 to get mail through to you. Second, it protects you in case someone\n\
288 forges a subscription request in your name.\n\
289 \n");
290 fclose();
291
292 fopen("/text/unsub-confirm");
293 fputs("To confirm that you would like\n\
294 \n\
295 !A\n\
296 \n\
297 removed from this mailing list, please send an empty reply to this address:\n\
298 \n\
299 !R\n\
300 \n\
301 Your mailer should have a Reply feature that uses this address automatically.\n\
302 \n\
303 I haven't checked whether your address is currently on the mailing list.\n\
304 To see what address you used to subscribe, look at the messages you are\n\
305 receiving from the mailing list. Each message has your address hidden\n\
306 inside its return path; for example, God@heaven.af.mil receives messages\n\
307 with return path ...-God=heaven.af.mil.\n\
308 \n");
309 fclose();
310
311 fopen("/text/sub-ok");
312 fputs("Acknowledgment: I have added the address\n\
313 \n\
314 !A\n\
315 \n\
316 to this mailing list.\n\
317 \n");
318 fclose();
319
320 fopen("/text/unsub-ok");
321 fputs("Acknowledgment: I have removed the address\n\
322 \n\
323 !A\n\
324 \n\
325 from this mailing list.\n\
326 \n");
327 fclose();
328
329 fopen("/text/sub-nop");
330 fputs("Acknowledgment: The address\n\
331 \n\
332 !A\n\
333 \n\
334 is on this mailing list.\n\
335 \n");
336 fclose();
337
338 fopen("/text/unsub-nop");
339 fputs("Acknowledgment: The address\n\
340 \n\
341 !A\n\
342 \n\
343 is not on this mailing list.\n\
344 \n");
345 fclose();
346
347 fopen("/text/sub-bad");
348 fputs("Oops, that confirmation number appears to be invalid.\n\
349 \n\
350 The most common reason for invalid numbers is expiration. I have to\n\
351 receive confirmation of each request within ten days.\n\
352 \n\
353 I've set up a new confirmation number. To confirm that you would like\n\
354 \n\
355 !A\n\
356 \n\
357 added to this mailing list, please send an empty reply to this address:\n\
358 \n\
359 !R\n\
360 \n\
361 Sorry for the trouble.\n\
362 \n");
363 fclose();
364
365 fopen("/text/unsub-bad");
366 fputs("Oops, that confirmation number appears to be invalid.\n\
367 \n\
368 The most common reason for invalid numbers is expiration. I have to\n\
369 receive confirmation of each request within ten days.\n\
370 \n\
371 I've set up a new confirmation number. To confirm that you would like\n\
372 \n\
373 !A\n\
374 \n\
375 removed from this mailing list, please send an empty reply to this address:\n\
376 \n\
377 !R\n\
378 \n\
379 Sorry for the trouble.\n\
380 \n");
381 fclose();
382
383 fopen("/text/get-bad");
384 fputs("Sorry, I don't see that message.\n\n");
385 fclose();
386
387 fopen("/text/bounce-bottom");
388 fputs("\n\
389 --- Below this line is a copy of the bounce message I received.\n\n");
390 fclose();
391
392 fopen("/text/bounce-warn");
393 fputs("\n\
394 Messages to you seem to have been bouncing. I've attached a copy of\n\
395 the first bounce message I received.\n\
396 \n\
397 If this message bounces too, I will send you a probe. If the probe bounces,\n\
398 I will remove your address from the mailing list, without further notice.\n\
399 \n");
400 fclose();
401
402 fopen("/text/bounce-probe");
403 fputs("\n\
404 Messages to you seem to have been bouncing. I sent you a warning\n\
405 message, but it bounced. I've attached a copy of the bounce message.\n\
406 \n\
407 This is a probe to check whether your address is reachable. If this\n\
408 probe bounces, I will remove your address from the mailing list, without\n\
409 further notice.\n\
410 \n");
411 fclose();
412
413 fopen("/text/bounce-num");
414 fputs("\n\
415 I've kept a list of which messages bounced from your address. Copies of\n\
416 these messages may be in the archive. To get message 12345 from the\n\
417 archive, send an empty note to ");
418 fputs(local); fputs("-get.12345@"); fputs(host); fputs(".\n\
419 Here are the message numbers:\n\
420 \n");
421 fclose();
422
423 fopen("/text/help");
424 fputs("\
425 This is a generic help message. The message I received wasn't sent to\n\
426 any of my command addresses.\n\
427 \n");
428 fclose();
429
430 fopen("/key");
431 fput(key.s,key.len);
432 fclose();
433
434 _exit(0);
435 }