1 #include "stralloc.h"
2 #include "subfd.h"
3 #include "strerr.h"
4 #include "error.h"
5 #include "qmail.h"
6 #include "env.h"
7 #include "lock.h"
8 #include "sig.h"
9 #include "open.h"
10 #include "getln.h"
11 #include "case.h"
12 #include "scan.h"
13 #include "str.h"
14 #include "fmt.h"
15 #include "readwrite.h"
16 #include "exit.h"
17 #include "substdio.h"
18 #include "getconf.h"
19 #include "constmap.h"
20 
21 #define FATAL "ezmlm-send: fatal: "
22 
die_usage()23 void die_usage()
24 {
25   strerr_die1x(100,"ezmlm-send: usage: ezmlm-send dir");
26 }
die_nomem()27 void die_nomem()
28 {
29   strerr_die2x(111,FATAL,"out of memory");
30 }
31 
32 char strnum[FMT_ULONG];
33 
34 stralloc fnadir = {0};
35 stralloc fnaf = {0};
36 stralloc fnsub = {0};
37 stralloc line = {0};
38 
39 int flagarchived;
40 int fdarchive;
41 substdio ssarchive;
42 char archivebuf[1024];
43 
44 int flagsublist;
45 stralloc sublist = {0};
46 stralloc mailinglist = {0};
47 stralloc outlocal = {0};
48 stralloc outhost = {0};
49 stralloc headerremove = {0};
50 struct constmap headerremovemap;
51 stralloc headeradd = {0};
52 
53 struct qmail qq;
54 substdio ssin;
55 char inbuf[1024];
56 substdio ssout;
57 char outbuf[1];
58 
mywrite(fd,buf,len)59 int mywrite(fd,buf,len)
60 int fd;
61 char *buf;
62 unsigned int len;
63 {
64   qmail_put(&qq,buf,len);
65   return len;
66 }
67 
die_archive()68 void die_archive()
69 {
70   strerr_die4sys(111,FATAL,"unable to write to ",fnaf.s,": ");
71 }
die_numnew()72 void die_numnew()
73 {
74   strerr_die2sys(111,FATAL,"unable to create numnew: ");
75 }
76 
put(buf,len)77 void put(buf,len) char *buf; int len;
78 {
79   qmail_put(&qq,buf,len);
80   if (flagarchived)
81     if (substdio_put(&ssarchive,buf,len) == -1) die_archive();
82 }
83 
puts(buf)84 void puts(buf) char *buf;
85 {
86   qmail_puts(&qq,buf);
87   if (flagarchived)
88     if (substdio_puts(&ssarchive,buf) == -1) die_archive();
89 }
90 
sublistmatch(sender)91 int sublistmatch(sender)
92 char *sender;
93 {
94   int i;
95   int j;
96 
97   j = str_len(sender);
98   if (j < sublist.len) return 0;
99 
100   i = byte_rchr(sublist.s,sublist.len,'@');
101   if (i == sublist.len) return 1;
102 
103   if (byte_diff(sublist.s,i,sender)) return 0;
104   if (case_diffb(sublist.s + i,sublist.len - i,sender + j - (sublist.len - i)))
105     return 0;
106 
107   return 1;
108 }
109 
110 substdio ssnumnew;
111 char numnewbuf[16];
112 unsigned long msgnum;
113 stralloc num = {0};
114 
115 char buf0[256];
116 substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
117 
numwrite()118 void numwrite()
119 {
120   int fd;
121 
122   fd = open_trunc("numnew");
123   if (fd == -1) die_numnew();
124   substdio_fdbuf(&ssnumnew,write,fd,numnewbuf,sizeof(numnewbuf));
125   if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,msgnum)) == -1)
126     die_numnew();
127   if (substdio_puts(&ssnumnew,"\n") == -1) die_numnew();
128   if (substdio_flush(&ssnumnew) == -1) die_numnew();
129   if (fsync(fd) == -1) die_numnew();
130   if (close(fd) == -1) die_numnew(); /* NFS stupidity */
131   if (rename("numnew","num") == -1)
132     strerr_die2sys(111,FATAL,"unable to move numnew to num: ");
133 }
134 
135 stralloc mydtline = {0};
136 
main(argc,argv)137 void main(argc,argv)
138 int argc;
139 char **argv;
140 {
141   int fd;
142   char *dir;
143   int fdlock;
144   char *sender;
145   int flagmlwasthere;
146   int match;
147   int i;
148   char ch;
149   int flaginheader;
150   int flagbadfield;
151 
152   umask(022);
153   sig_pipeignore();
154 
155   dir = argv[1];
156   if (!dir) die_usage();
157 
158   sender = env_get("SENDER");
159 
160   if (chdir(dir) == -1)
161     strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
162 
163   fdlock = open_append("lock");
164   if (fdlock == -1)
165     strerr_die4sys(111,FATAL,"unable to open ",dir,"/lock: ");
166   if (lock_ex(fdlock) == -1)
167     strerr_die4sys(111,FATAL,"unable to obtain ",dir,"/lock: ");
168 
169   if (qmail_open(&qq) == -1)
170     strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
171 
172   flagarchived = getconf_line(&line,"archived",0,FATAL,dir);
173 
174   getconf_line(&num,"num",1,FATAL,dir);
175   if (!stralloc_0(&num)) die_nomem();
176   scan_ulong(num.s,&msgnum);
177   ++msgnum;
178 
179   getconf_line(&outhost,"outhost",1,FATAL,dir);
180   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
181   getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
182   flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir);
183 
184   getconf(&headerremove,"headerremove",1,FATAL,dir);
185   constmap_init(&headerremovemap,headerremove.s,headerremove.len,0);
186 
187   getconf(&headeradd,"headeradd",1,FATAL,dir);
188   for (i = 0;i < headeradd.len;++i)
189     if (!headeradd.s[i])
190       headeradd.s[i] = '\n';
191 
192   if (!stralloc_copys(&mydtline,"Delivered-To: mailing list ")) die_nomem();
193   if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
194   if (!stralloc_cats(&mydtline,"@")) die_nomem();
195   if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
196   if (!stralloc_cats(&mydtline,"\n")) die_nomem();
197 
198   if (sender) {
199     if (!*sender)
200       strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)");
201     if (str_equal(sender,"#@[]"))
202       strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)");
203     if (flagsublist)
204       if (!sublistmatch(sender))
205         strerr_die2x(100,FATAL,"this message is not from my parent list (#5.7.2)");
206   }
207 
208   if (flagarchived) {
209     if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
210     if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,msgnum / 100))) die_nomem();
211     if (!stralloc_copy(&fnaf,&fnadir)) die_nomem();
212     if (!stralloc_cats(&fnaf,"/")) die_nomem();
213     if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,(unsigned int) (msgnum % 100),2))) die_nomem();
214     if (!stralloc_0(&fnadir)) die_nomem();
215     if (!stralloc_0(&fnaf)) die_nomem();
216 
217     if (mkdir(fnadir.s,0755) == -1)
218       if (errno != error_exist)
219 	strerr_die4sys(111,FATAL,"unable to create ",fnadir.s,": ");
220     fdarchive = open_trunc(fnaf.s);
221     if (fdarchive == -1)
222       strerr_die4sys(111,FATAL,"unable to write ",fnaf.s,": ");
223 
224     substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf));
225   }
226 
227   if (!flagsublist) {
228     puts("Mailing-List: ");
229     put(mailinglist.s,mailinglist.len);
230     puts("\n");
231   }
232   put(headeradd.s,headeradd.len);
233   put(mydtline.s,mydtline.len);
234 
235   flagmlwasthere = 0;
236   flaginheader = 1;
237   flagbadfield = 0;
238 
239   for (;;) {
240     if (getln(&ss0,&line,&match,'\n') == -1)
241       strerr_die2sys(111,FATAL,"unable to read input: ");
242 
243     if (flaginheader && match) {
244       if (line.len == 1)
245 	flaginheader = 0;
246       if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
247 	flagbadfield = 0;
248 	if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
249 	  flagbadfield = 1;
250 	if (case_startb(line.s,line.len,"mailing-list:"))
251 	  flagmlwasthere = 1;
252 	if (line.len == mydtline.len)
253 	  if (!byte_diff(line.s,line.len,mydtline.s))
254             strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)");
255       }
256     }
257 
258     if (!(flaginheader && flagbadfield))
259       put(line.s,line.len);
260 
261     if (!match)
262       break;
263   }
264 
265   if (flagsublist)
266     if (!flagmlwasthere)
267       strerr_die2x(100,FATAL,"sublist messages must have Mailing-List (#5.7.2)");
268   if (!flagsublist)
269     if (flagmlwasthere)
270       strerr_die2x(100,FATAL,"message already has Mailing-List (#5.7.2)");
271 
272   if (flagarchived) {
273     if (substdio_flush(&ssarchive) == -1) die_archive();
274     if (fsync(fdarchive) == -1) die_archive();
275     if (fchmod(fdarchive,0744) == -1) die_archive();
276     if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */
277   }
278 
279   numwrite();
280 
281   if (!stralloc_copy(&line,&outlocal)) die_nomem();
282   if (!stralloc_cats(&line,"-return-")) die_nomem();
283   if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem();
284   if (!stralloc_cats(&line,"-@")) die_nomem();
285   if (!stralloc_cat(&line,&outhost)) die_nomem();
286   if (!stralloc_cats(&line,"-@[]")) die_nomem();
287   if (!stralloc_0(&line)) die_nomem();
288 
289   qmail_from(&qq,line.s);
290 
291   for (i = 0;i < 53;++i) {
292     ch = 64 + i;
293     if (!stralloc_copys(&fnsub,"subscribers/")) die_nomem();
294     if (!stralloc_catb(&fnsub,&ch,1)) strerr_die2x(111,FATAL,"out of memory");
295     if (!stralloc_0(&fnsub)) strerr_die2x(111,FATAL,"out of memory");
296     fd = open_read(fnsub.s);
297     if (fd == -1) {
298       if (errno != error_noent)
299 	strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": ");
300     }
301     else {
302       substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
303       substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf));
304       if (substdio_copy(&ssout,&ssin) != 0)
305 	strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": ");
306       close(fd);
307     }
308   }
309 
310   switch(qmail_close(&qq)) {
311     case 0:
312       strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
313       strerr_die2x(0,"ezmlm-send: info: qp ",strnum);
314     default:
315       --msgnum;
316       numwrite();
317       strerr_die2x(111,FATAL,"temporary qmail-queue error");
318   }
319 }
320