5748891fe49e3e4345253b516fe222793b78eea6
[ezmlm] / ezmlm-send.c
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
23 void die_usage()
24 {
25 strerr_die1x(100,"ezmlm-send: usage: ezmlm-send dir");
26 }
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
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
68 void die_archive()
69 {
70 strerr_die4sys(111,FATAL,"unable to write to ",fnaf.s,": ");
71 }
72 void die_numnew()
73 {
74 strerr_die2sys(111,FATAL,"unable to create numnew: ");
75 }
76
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
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
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
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
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 }