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