chiark / gitweb /
Debianization and various other fixes.
[ezmlm] / ezmlm-split.c
1 /*$Id: ezmlm-split.c,v 1.6 1999/05/12 22:17:54 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include "error.h"
7 #include "stralloc.h"
8 #include "str.h"
9 #include "env.h"
10 #include "sig.h"
11 #include "slurp.h"
12 #include "getconf.h"
13 #include "strerr.h"
14 #include "byte.h"
15 #include "getln.h"
16 #include "case.h"
17 #include "qmail.h"
18 #include "substdio.h"
19 #include "readwrite.h"
20 #include "quote.h"
21 #include "now.h"
22 #include "uint32.h"
23 #include "fmt.h"
24 #include "errtxt.h"
25 #include "idx.h"
26
27 #define FATAL "ezmlm-split: fatal: "
28 #define INFO "ezmlm-split: info: "
29
30 int flagdo = 1;         /* default is manager function */
31
32 char *sender;
33 char *split;
34 stralloc outhost = {0};
35 stralloc inlocal = {0};
36 stralloc outlocal = {0};
37 stralloc target = {0};
38 stralloc lctarget = {0};
39 stralloc line = {0};
40 stralloc domain = {0};
41 stralloc name = {0};
42 stralloc from = {0};
43 stralloc to = {0};
44 char strnum[FMT_ULONG];
45 unsigned long lineno;
46 int flagfound;
47
48 void die_usage() {
49   strerr_die1x(100,"ezmlm-split: usage: ezmlm-split [-dD] dir [splitfile]"); }
50
51 void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
52
53 void die_badaddr()
54 {
55   strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
56 }
57
58 void die_syntax()
59 {
60   strnum[fmt_ulong(strnum,lineno)] = '\0';
61   strerr_die6x(111,FATAL,split," syntax error line ",strnum,": ",line.s);
62 }
63
64 char spbuf[1024];       /* should normally hold entire file */
65 substdio sssp;
66
67 struct qmail qq;
68 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
69 {
70   qmail_put(&qq,buf,len);
71   return (int) len;
72 }
73 char qqbuf[1];
74 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
75
76 char outbuf[1];
77 substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,(int) sizeof(outbuf));
78
79 char inbuf[1024];
80 substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
81
82 int findname()
83 /* returns 1 if a matching line was found, 0 otherwise. name will contain */
84 /* the correct list address in either case */
85 {
86   char *cpat,*cp,*cpname,*cp1,*cp2,*cplast;
87   unsigned long u;
88   uint32 h;
89   unsigned char hash,hash_hi,hash_lo;
90   unsigned int pos,pos_name,pos_hi;
91   char ch;
92   int fd,match;
93
94   /* make case insensitive hash */
95   flagfound = 0;                        /* default */
96   cpname = "";                          /* default */
97   if (!stralloc_copy(&lctarget,&target)) die_nomem();
98   case_lowerb(lctarget.s,lctarget.len -1);
99   h = 5381;
100   cp = lctarget.s;
101   while ((ch = *cp++)) {
102     h = (h + (h << 5)) ^ (uint32) ch;
103   }
104   hash = (h % 53);
105
106   /* make domain pointer */
107   cpat = lctarget.s + str_chr(lctarget.s,'@');
108   if (!*cpat)
109     strerr_die4x(100,FATAL,ERR_ADDR_AT,": ",target.s);
110   cplast = cpat + str_len(cpat) - 1;
111   if (*cplast == '.') --cplast;         /* annonying special case */
112   cp1 = cpat + byte_rchr(cpat,cplast - cpat, '.');
113   if (cp1 != cplast) {                  /* got one '.' */
114     if (!stralloc_copyb(&domain,cp1 + 1, cplast - cp1)) die_nomem();
115     cp2 = cpat + byte_rchr(cpat, cp1 - cpat,'.');
116     if (cp2 == cp1) cp2 = cpat;
117     ++cp2;
118     if (!stralloc_append(&domain,".")) die_nomem();
119     if (!stralloc_catb(&domain,cp2, cp1 - cp2)) die_nomem();
120   } else                                /* no '.' */
121     if (!stralloc_copyb(&domain,cpat + 1,cplast - cpat)) die_nomem();
122   if (!stralloc_0(&domain)) die_nomem();
123
124   if ((fd = open_read(split)) == -1)
125     strerr_die4sys(111,FATAL,ERR_OPEN,split,": ");
126   substdio_fdbuf(&sssp,read,fd,spbuf,(int) sizeof(spbuf));
127   lineno = 0;
128   for (;;) {    /* dom:hash_lo:hash_hi:listaddress */
129     if (getln(&sssp,&line,&match,'\n') == -1)
130       strerr_die4sys(111,FATAL,ERR_READ,split,": ");
131      lineno++;
132     if (!match)
133       break;
134     if (line.s[0] == '#') continue;     /* comment */
135     line.s[line.len - 1] = '\0';        /* no need to allow \0 in lines */
136     if (!line.s[pos = str_chr(line.s,':')])
137       continue;                         /* usually blank line */
138     line.s[pos] = '\0';
139     if (pos == 0 ||                     /* no domain */
140           (case_starts(domain.s,line.s))) {     /* or matching domain */
141         if (!line.s[++pos]) die_syntax();
142         pos_hi = pos + str_chr(line.s + pos,':');
143         if (!line.s[pos_hi]) die_syntax();
144         pos_hi++;
145         (void) scan_ulong(line.s + pos, &u);    /* scan_uint() not in ezmlm */
146         hash_lo = (unsigned char) u;
147         (void) scan_ulong(line.s + pos_hi, &u);
148         hash_hi = (unsigned char) u;
149         pos_name = pos_hi + str_chr(line.s + pos_hi,':');
150         if (pos_hi == pos_name) hash_hi = 52L;  /* default hi = 52 */
151         if (line.s[pos_name]) pos_name++;
152         if (hash > hash_hi || hash < hash_lo) continue; /* not us */
153         cpname = line.s + pos_name;
154         while (*cpname &&                               /* isolate name */
155             (*cpname == ' ' || *cpname == '\t')) cpname++;
156         pos = line.len - 2;
157         while (pos && (line.s[pos] == '\n' || line.s[pos] == ' ' ||
158                 line.s[pos] == '\t')) line.s[pos--] = '\0';
159         break;
160     }
161   }
162   close(fd);
163
164   if (*cpname) {
165     if (!stralloc_copys(&name,cpname)) die_nomem();
166     if (byte_chr(name.s,name.len,'@') == name.len) {    /* local sublist */
167       if (!stralloc_append(&name,"@")) die_nomem();
168       if (!stralloc_cat(&name,&outhost)) die_nomem();
169     }
170     if (!stralloc_0(&name)) die_nomem();
171     return 1;
172   } else {                      /* match without name or no match =>this list */
173     if (!stralloc_copy(&name,&outlocal)) die_nomem();
174     if (!stralloc_append(&name,"@")) die_nomem();
175     if (!stralloc_cat(&name,&outhost)) die_nomem();
176     if (!stralloc_0(&name)) die_nomem();
177     return 0;
178   }
179 }
180
181 void main(argc,argv)
182 int argc;
183 char **argv;
184 {
185   char *dir;
186   char *local;
187   char *action;
188   char *def;
189   char *dtline;
190   char *nhost;
191   char *err;
192   unsigned int i;
193   int match;
194   int optind = 1;
195
196   sig_pipeignore();
197
198   dir = argv[optind++];
199   if (!dir) die_usage();
200   if (dir[0] == '-') {
201     if (dir[1] == 'd') flagdo = 1;
202     else if (dir[1] == 'D') flagdo = 0;
203     else die_usage();
204     if (!(dir = argv[optind++])) die_usage();
205   }
206   if (!(split = argv[optind]))
207     split = "split";
208
209   if (chdir(dir) == -1)
210     strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
211
212   getconf_line(&outhost,"outhost",1,FATAL,dir);
213   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
214
215   if (flagdo) {
216     sender = env_get("SENDER");
217     if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER);
218     if (!*sender)
219       strerr_die2x(100,FATAL,ERR_BOUNCE);
220     if (!sender[str_chr(sender,'@')])
221       strerr_die2x(100,FATAL,ERR_ANONYMOUS);
222     if (str_equal(sender,"#@[]"))
223       strerr_die2x(100,FATAL,ERR_BOUNCE);
224
225     def = env_get("DEFAULT");
226     if (def) {
227       action = def;
228     } else {
229       local = env_get("LOCAL");
230       if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
231       getconf_line(&inlocal,"inlocal",1,FATAL,dir);
232       if (inlocal.len > str_len(local)) die_badaddr();
233       if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
234       action = local + inlocal.len + 1;
235     }
236     if (!stralloc_copys(&target,sender)) die_nomem();
237     if (action[0]) {
238       i = str_chr(action,'-');
239       if (action[i]) {
240         action[i] = '\0';
241         if (!stralloc_copys(&target,action + i + 1)) die_nomem();
242         i = byte_rchr(target.s,target.len,'=');
243         if (i < target.len)
244           target.s[i] = '@';
245       }
246     }
247     if (!stralloc_0(&target)) die_nomem();
248
249     if (case_diffs(action,ACTION_SUBSCRIBE) &&
250       case_diffs(action,ALT_SUBSCRIBE) &&
251       case_diffs(action,ACTION_UNSUBSCRIBE) &&
252       case_diffs(action,ALT_UNSUBSCRIBE))
253     _exit(0);                   /* not for us */
254
255     if (findname()) {
256                                 /* new sender */
257       if (!stralloc_copy(&from,&outlocal)) die_nomem();
258       if (!stralloc_cats(&from,"-return-@")) die_nomem();
259       if (!stralloc_cat(&from,&outhost)) die_nomem();
260       if (!stralloc_0(&from)) die_nomem();
261       if (name.s[i = str_rchr(name.s,'@')]) {           /* name must have '@'*/
262         nhost = name.s + i;
263         *(nhost++) = '\0';
264       }
265       if (!stralloc_copys(&to,name.s)) die_nomem();     /* local */
266       if (!stralloc_append(&to,"-")) die_nomem();       /* - */
267       if (!stralloc_cats(&to,action)) die_nomem();      /* subscribe */
268       if (!stralloc_append(&to,"-")) die_nomem();       /* - */
269       if (target.s[i = str_rchr(target.s,'@')])
270         target.s[i] = '=';
271       if (!stralloc_cats(&to,target.s)) die_nomem();    /* target */
272       if (!stralloc_append(&to,"@")) die_nomem();       /* - */
273       if (!stralloc_cats(&to,nhost)) die_nomem();       /* host */
274       if (!stralloc_0(&to)) die_nomem();
275       dtline = env_get("DTLINE");
276       if (!dtline) strerr_die2x(100,FATAL,ERR_NODTLINE);
277
278       if (qmail_open(&qq,(stralloc *) 0) == -1)
279         strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
280       qmail_puts(&qq,dtline);                           /* delivered-to */
281       if (substdio_copy(&ssqq,&ssin) != 0)
282         strerr_die2sys(111,FATAL,ERR_READ_INPUT);
283       qmail_from(&qq,from.s);
284       qmail_to(&qq,to.s);
285
286       if (*(err = qmail_close(&qq)) != '\0')
287         strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1);
288
289       strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
290       strerr_die3x(99,INFO,"qp ",strnum);
291     }
292     _exit(0);
293   } else {
294
295     for (;;) {
296       if (getln(&ssin,&line,&match,'\n') == -1)
297           strerr_die2sys(111,FATAL,ERR_READ_INPUT);
298       if (!match) break;
299       if (line.len == 1) continue;      /* ignore blank lines */
300       if (line.s[0] == '#') continue;   /* ignore comments */
301       if (!stralloc_copy(&target,&line)) die_nomem();
302       target.s[target.len - 1] = '\0';
303       (void) findname();
304       if (!stralloc_cats(&name,": ")) die_nomem();
305       if (!stralloc_cats(&name,target.s)) die_nomem();
306       if (!stralloc_append(&name,"\n")) die_nomem();
307       if (substdio_put(&ssout,name.s,name.len) == -1)
308         strerr_die2sys(111,ERR_WRITE,"output: ");
309     }
310     if (substdio_flush(&ssout) == -1)
311       strerr_die2sys(111,ERR_FLUSH,"output: ");
312     _exit(0);
313   }
314 }
315
316