chiark / gitweb /
Debianization for 1.01-2
[qmail] / qlist.c
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 }