Commit | Line | Data |
---|---|---|
f8beb284 MW |
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 |