Commit | Line | Data |
---|---|---|
5b62e993 MW |
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 | } |