chiark / gitweb /
Import ezmlm 0.53
[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 }