1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 #include "error.h"
5 #include "stralloc.h"
6 #include "str.h"
7 #include "env.h"
8 #include "sender.h"
9 #include "sig.h"
10 #include "open.h"
11 #include "scan.h"
12 #include "strerr.h"
13 #include "byte.h"
14 #include "getln.h"
15 #include "case.h"
16 #include "qmail.h"
17 #include "getconfopt.h"
18 #include "subfd.h"
19 #include "substdio.h"
20 #include "readwrite.h"
21 #include "quote.h"
22 #include "now.h"
23 #include "uint32.h"
24 #include "subhash.h"
25 #include "fmt.h"
26 #include "messages.h"
27 #include "die.h"
28 #include "config.h"
29 #include "idx.h"
30 #include "auto_version.h"
31
32 const char FATAL[] = "ezmlm-split: fatal: ";
33 const char INFO[] = "ezmlm-split: info: ";
34 const char USAGE[] =
35 "ezmlm-split: usage: ezmlm-split [-dD] dir [splitfile]";
36
37 static int flagdo = 1; /* default is manager function */
38
39 static struct option options[] = {
40 OPT_FLAG(flagdo,'d',1,0),
41 OPT_FLAG(flagdo,'D',0,0),
42 OPT_END
43 };
44
45 static const char *sender;
46 static const char *split;
47 static stralloc target = {0};
48 static stralloc lctarget = {0};
49 static stralloc line = {0};
50 static stralloc domain = {0};
51 static stralloc name = {0};
52 static stralloc from = {0};
53 static stralloc to = {0};
54 static unsigned long lineno;
55 static int flagfound;
56
die_syntax(void)57 static void die_syntax(void)
58 {
59 char strnum[FMT_ULONG];
60 strnum[fmt_ulong(strnum,lineno)] = '\0';
61 strerr_die6x(111,FATAL,split," syntax error line ",strnum,": ",line.s);
62 }
63
64 static char spbuf[1024]; /* should normally hold entire file */
65 static substdio sssp;
66
67 struct qmail qq;
68
findname(void)69 static int findname(void)
70 /* returns 1 if a matching line was found, 0 otherwise. name will contain */
71 /* the correct list address in either case */
72 {
73 char *cpat,*cp1,*cp2,*cplast;
74 const char *cpname;
75 unsigned long u;
76 unsigned char hash,hash_hi,hash_lo;
77 unsigned int pos,pos_name,pos_hi;
78 int fd,match;
79
80 /* make case insensitive hash */
81 flagfound = 0; /* default */
82 cpname = ""; /* default */
83 if (!stralloc_copy(&lctarget,&target)) die_nomem();
84 case_lowerb(lctarget.s,lctarget.len -1);
85 hash = subhashs(lctarget.s);
86
87 /* make domain pointer */
88 cpat = lctarget.s + str_chr(lctarget.s,'@');
89 if (!*cpat)
90 strerr_die4x(100,FATAL,MSG(ERR_ADDR_AT),": ",target.s);
91 cplast = cpat + str_len(cpat) - 1;
92 if (*cplast == '.') --cplast; /* annonying special case */
93 cp1 = cpat + byte_rchr(cpat,cplast - cpat, '.');
94 if (cp1 != cplast) { /* got one '.' */
95 if (!stralloc_copyb(&domain,cp1 + 1, cplast - cp1)) die_nomem();
96 cp2 = cpat + byte_rchr(cpat, cp1 - cpat,'.');
97 if (cp2 == cp1) cp2 = cpat;
98 ++cp2;
99 if (!stralloc_append(&domain,'.')) die_nomem();
100 if (!stralloc_catb(&domain,cp2, cp1 - cp2)) die_nomem();
101 } else /* no '.' */
102 if (!stralloc_copyb(&domain,cpat + 1,cplast - cpat)) die_nomem();
103 if (!stralloc_0(&domain)) die_nomem();
104
105 if ((fd = open_read(split)) == -1) {
106 if (errno == error_noent)
107 _exit(0);
108 else
109 strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,split));
110 }
111 substdio_fdbuf(&sssp,read,fd,spbuf,(int) sizeof(spbuf));
112 lineno = 0;
113 for (;;) { /* dom:hash_lo:hash_hi:listaddress */
114 if (getln(&sssp,&line,&match,'\n') == -1)
115 strerr_die2sys(111,FATAL,MSG1(ERR_READ,split));
116 lineno++;
117 if (!match)
118 break;
119 if (line.s[0] == '#') continue; /* comment */
120 line.s[line.len - 1] = '\0'; /* no need to allow \0 in lines */
121 if (!line.s[pos = str_chr(line.s,':')])
122 continue; /* usually blank line */
123 line.s[pos] = '\0';
124 if (pos == 0 || /* no domain */
125 (case_starts(domain.s,line.s))) { /* or matching domain */
126 if (!line.s[++pos]) die_syntax();
127 pos_hi = pos + str_chr(line.s + pos,':');
128 if (!line.s[pos_hi]) die_syntax();
129 pos_hi++;
130 (void) scan_ulong(line.s + pos, &u); /* scan_uint() not in ezmlm */
131 hash_lo = (unsigned char) u;
132 (void) scan_ulong(line.s + pos_hi, &u);
133 hash_hi = (unsigned char) u;
134 pos_name = pos_hi + str_chr(line.s + pos_hi,':');
135 if (pos_hi == pos_name) hash_hi = 52L; /* default hi = 52 */
136 if (line.s[pos_name]) pos_name++;
137 if (hash > hash_hi || hash < hash_lo) continue; /* not us */
138 cpname = line.s + pos_name;
139 while (*cpname && /* isolate name */
140 (*cpname == ' ' || *cpname == '\t')) cpname++;
141 pos = line.len - 2;
142 while (pos && (line.s[pos] == '\n' || line.s[pos] == ' ' ||
143 line.s[pos] == '\t')) line.s[pos--] = '\0';
144 break;
145 }
146 }
147 close(fd);
148
149 if (*cpname) {
150 if (!stralloc_copys(&name,cpname)) die_nomem();
151 if (byte_chr(name.s,name.len,'@') == name.len) { /* local sublist */
152 if (!stralloc_append(&name,'@')) die_nomem();
153 if (!stralloc_cat(&name,&outhost)) die_nomem();
154 }
155 if (!stralloc_0(&name)) die_nomem();
156 return 1;
157 } else { /* match without name or no match =>this list */
158 if (!stralloc_copy(&name,&outlocal)) die_nomem();
159 if (!stralloc_append(&name,'@')) die_nomem();
160 if (!stralloc_cat(&name,&outhost)) die_nomem();
161 if (!stralloc_0(&name)) die_nomem();
162 return 0;
163 }
164 }
165
main(int argc,char ** argv)166 int main(int argc,char **argv)
167 {
168 char strnum[FMT_ULONG];
169 char *action;
170 char *dtline;
171 char *nhost;
172 const char *err;
173 unsigned int i;
174 int match;
175 int opt;
176
177 sig_pipeignore();
178
179 opt = getconfopt(argc,argv,options,1,0);
180 if (!(split = argv[opt]))
181 split = "split";
182
183 if (flagdo) {
184 sender = get_sender();
185 if (!sender) die_sender();
186 if (!*sender)
187 strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
188 if (!sender[str_chr(sender,'@')])
189 strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS));
190 if (str_equal(sender,"#@[]"))
191 strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
192
193 action = env_get("DEFAULT");
194 if (!action) strerr_die2x(100,FATAL,MSG(ERR_NODEFAULT));
195 if (!stralloc_copys(&target,sender)) die_nomem();
196 if (action[0]) {
197 i = str_chr(action,'-');
198 if (action[i]) {
199 action[i] = '\0';
200 if (!stralloc_copys(&target,action + i + 1)) die_nomem();
201 i = byte_rchr(target.s,target.len,'=');
202 if (i < target.len)
203 target.s[i] = '@';
204 }
205 }
206 if (!stralloc_0(&target)) die_nomem();
207
208 if (case_diffs(action,ACTION_SUBSCRIBE) &&
209 case_diffs(action,ALT_SUBSCRIBE) &&
210 case_diffs(action,ACTION_UNSUBSCRIBE) &&
211 case_diffs(action,ALT_UNSUBSCRIBE))
212 _exit(0); /* not for us */
213
214 if (findname()) {
215 /* new sender */
216 if (!stralloc_copy(&from,&outlocal)) die_nomem();
217 if (!stralloc_cats(&from,"-return-@")) die_nomem();
218 if (!stralloc_cat(&from,&outhost)) die_nomem();
219 if (!stralloc_0(&from)) die_nomem();
220 nhost = name.s + str_rchr(name.s,'@'); /* name must have '@'*/
221 *(nhost++) = '\0';
222 if (!stralloc_copys(&to,name.s)) die_nomem(); /* local */
223 if (!stralloc_append(&to,'-')) die_nomem(); /* - */
224 if (!stralloc_cats(&to,action)) die_nomem(); /* subscribe */
225 if (!stralloc_append(&to,'-')) die_nomem(); /* - */
226 if (target.s[i = str_rchr(target.s,'@')])
227 target.s[i] = '=';
228 if (!stralloc_cats(&to,target.s)) die_nomem(); /* target */
229 if (!stralloc_append(&to,'@')) die_nomem(); /* - */
230 if (!stralloc_cats(&to,nhost)) die_nomem(); /* host */
231 if (!stralloc_0(&to)) die_nomem();
232 dtline = env_get("DTLINE");
233 if (!dtline) strerr_die2x(100,FATAL,MSG(ERR_NODTLINE));
234
235 if (qmail_open(&qq) == -1)
236 strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE));
237 qmail_puts(&qq,dtline); /* delivered-to */
238 if (qmail_copy(&qq,subfdin,0) != 0)
239 strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
240 qmail_from(&qq,from.s);
241 qmail_to(&qq,to.s);
242
243 if (*(err = qmail_close(&qq)) != '\0')
244 strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1);
245
246 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
247 strerr_die3x(99,INFO,"qp ",strnum);
248 }
249 _exit(0);
250 } else {
251
252 for (;;) {
253 if (getln(subfdin,&line,&match,'\n') == -1)
254 strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
255 if (!match) break;
256 if (line.len == 1) continue; /* ignore blank lines */
257 if (line.s[0] == '#') continue; /* ignore comments */
258 if (!stralloc_copy(&target,&line)) die_nomem();
259 target.s[target.len - 1] = '\0';
260 (void) findname();
261 if (!stralloc_cats(&name,": ")) die_nomem();
262 if (!stralloc_cats(&name,target.s)) die_nomem();
263 if (!stralloc_append(&name,'\n')) die_nomem();
264 if (substdio_put(subfdout,name.s,name.len) == -1)
265 strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT));
266 }
267 if (substdio_flush(subfdout) == -1)
268 strerr_die2sys(111,FATAL,MSG(ERR_FLUSH_STDOUT));
269 _exit(0);
270 }
271 (void)argc;
272 }
273