ezmlmx 0.68
ezmlmx
Loading...
Searching...
No Matches
ezmlm-return.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <stdio.h>
4#include <unistd.h>
5#include "direntry.h"
6#include "stralloc.h"
7#include "str.h"
8#include "env.h"
9#include "sig.h"
10#include "getconf.h"
11#include "byte.h"
12#include "case.h"
13#include "open.h"
14#include "scan.h"
15#include "lock.h"
16#include "readclose.h"
17#include "getln.h"
18#include "buffer.h"
19#include "error.h"
20#include "quote.h"
21#include "readwrite.h"
22#include "fmt.h"
23#include "now.h"
24#include "cookie.h"
25#include "subscribe.h"
26#include "errtxt.h"
27#include "idx.h"
28#include "logmsg.h"
29#include "lockfile.h"
30
31#define WHO "ezmlm-return"
32
38
39static void die_usage() { logmsg(WHO,100,USAGE,"ezmlm-return [-dD] dir"); }
40static void die_nomem() { logmsg(WHO,111,FATAL,ERR_NOMEM); }
41static void die_badaddr() { logmsg(WHO,100,FATAL,ERR_BAD_RETURN_ADDRESS); }
42static void die_trash() { logmsg(WHO,99,WARN,"trash address"); }
43
44char outbuf[1024];
45buffer bo;
46char inbuf[1024];
47buffer bi;
48
49char strnum[FMT_ULONG];
50char hash[COOKIE];
52char *hashp = (char *) 0;
53unsigned long cookiedate;
54unsigned long addrno = 0L;
55unsigned long addrno1 = 0L;
56stralloc fndir = {0};
57stralloc fndate = {0};
58stralloc fndatenew = {0};
59stralloc fnhash = {0};
60stralloc fnhashnew = {0};
61
62stralloc quoted = {0};
63stralloc ddir = {0};
64char *sender;
65char *dir;
66char *workdir;
67
68void die_hashnew() { logmsg(WHO,111,FATAL,B(ERR_WRITE,fnhashnew.s)); }
69void die_datenew() { logmsg(WHO,111,FATAL,B(ERR_WRITE,fndatenew.s)); }
70void die_msgin() { logmsg(WHO,111,FATAL,ERR_READ_INPUT); }
71
72void makedir(char *s)
73{
74 if (mkdir(s,0755) == -1)
75 if (errno != EEXIST)
76 logmsg(WHO,111,FATAL,B(ERR_CREATE,s));
77}
78
79void dowit(char *addr,unsigned long when,stralloc *bounce)
80{
81 int fd;
82 unsigned int wpos;
83 unsigned long wdir,wfile;
84
85 if (!issub(workdir,addr,(char *) 0)) return;
86
87 if (!stralloc_copys(&fndate,workdir)) die_nomem();
88 if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
89 if (!stralloc_0(&fndate)) die_nomem();
90 fndate.s[fndate.len - 1] = '/'; /* replace '\0' */
91 wpos = fndate.len - 1;
92 wdir = when / 10000;
93 wfile = when - 10000 * wdir;
94 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,wdir))) die_nomem();
95 if (!stralloc_0(&fndate)) die_nomem();
96 makedir(fndate.s);
97 --fndate.len; /* remove terminal '\0' */
98 if (!stralloc_cats(&fndate,"/w")) die_nomem();
99 wpos = fndate.len - 1;
100 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,wfile))) die_nomem();
101 if (!stralloc_cats(&fndate,".")) die_nomem();
102 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
103 die_nomem();
104 if (!stralloc_0(&fndate)) die_nomem();
105 if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
106 fndatenew.s[wpos] = 'W';
107
108 fd = open_trunc(fndatenew.s);
109 if (fd == -1) die_datenew();
110 buffer_init(&bo,buffer_unixwrite,fd,outbuf,sizeof(outbuf));
111 if (buffer_puts(&bo,addr) == -1) die_datenew();
112 if (buffer_put(&bo,"",1) == -1) die_datenew();
113 if (buffer_puts(&bo,"Return-Path: <") == -1) die_datenew();
114 if (!quote2(&quoted,sender)) die_nomem();
115 if (buffer_put(&bo,quoted.s,quoted.len) == -1) die_datenew();
116 if (buffer_puts(&bo,">\n") == -1) die_datenew();
117 if (buffer_put(&bo,bounce->s,bounce->len) == -1) die_datenew();
118 if (buffer_flush(&bo) == -1) die_datenew();
119 if (fsync(fd) == -1) die_datenew();
120 if (close(fd) == -1) die_datenew(); /* NFS stupidity */
121
122 if (rename(fndatenew.s,fndate.s) == -1)
123 logmsg(WHO,111,FATAL,B(ERR_MOVE,fndatenew.s," to ",fndate.s));
124}
125
126void doit(const char *addr,unsigned long msgnum,unsigned long when,const stralloc *bounce)
127{
128 int fd;
129 int fdnew;
130 unsigned int pos;
131 unsigned long ddir,dfile;
132
133 if (!issub(workdir,addr,(char *) 0)) return;
134
135 if (!stralloc_copys(&fndate,workdir)) die_nomem();
136 if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
137 if (!stralloc_0(&fndate)) die_nomem();
138 makedir(fndate.s);
139 fndate.s[fndate.len-1] = '/'; /* replace terminal '\0' */
140 ddir = when / 10000;
141 dfile = when - 10000 * ddir;
142 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,ddir))) die_nomem();
143 if (!stralloc_copy(&fndir,&fndate)) die_nomem();
144 if (!stralloc_0(&fndir)) die_nomem(); /* make later if necessary (new addr)*/
145 if (!stralloc_cats(&fndate,"/d")) die_nomem();
146 pos = fndate.len - 2;
147 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,dfile))) die_nomem();
148 if (!stralloc_cats(&fndate,".")) die_nomem();
149 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
150 die_nomem();
151 if (addrno) { /* so that pre-VERP bounces make a d... file per address */
152 /* for the first one we use the std-style fname */
153 if (!stralloc_cats(&fndate,".")) die_nomem();
154 if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,addrno))) die_nomem();
155 }
156 addrno++; /* get ready for next */
157 if (!stralloc_0(&fndate)) die_nomem();
158 if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
159 fndatenew.s[pos] = '_'; /* fndate = bounce/d/nnnn/dmmmmmm */
160 /* fndatenew = bounce/d/nnnn_dmmmmmm */
161
162 fd = open_trunc(fndatenew.s);
163 if (fd == -1) die_datenew();
164 buffer_init(&bo,buffer_unixwrite,fd,outbuf,sizeof(outbuf));
165 if (buffer_puts(&bo,addr) == -1) die_datenew();
166 if (buffer_put(&bo,"",1) == -1) die_datenew();
167 if (buffer_puts(&bo,"Return-Path: <") == -1) die_datenew();
168 if (!quote2(&quoted,sender)) die_nomem();
169 if (buffer_put(&bo,quoted.s,quoted.len) == -1) die_datenew();
170 if (buffer_puts(&bo,">\n") == -1) die_datenew();
171 if (buffer_put(&bo,bounce->s,bounce->len) == -1) die_datenew();
172 if (buffer_flush(&bo) == -1) die_datenew();
173 if (fsync(fd) == -1) die_datenew();
174 if (close(fd) == -1) die_datenew(); /* NFS stupidity */
175
176 cookie(hash,"",0,"",addr,"");
177 if (!stralloc_copys(&fnhash,workdir)) die_nomem();
178 if (!stralloc_cats(&fnhash,"/bounce/h")) die_nomem();
179 if (!stralloc_0(&fnhash)) die_nomem();
180 makedir(fnhash.s);
181 fnhash.s[fnhash.len - 1] = '/'; /* replace terminal '\0' */
182 if (!stralloc_catb(&fnhash,hash,1)) die_nomem();
183 if (!stralloc_0(&fnhash)) die_nomem();
184 makedir(fnhash.s);
185 --fnhash.len; /* remove terminal '\0' */
186 if (!stralloc_cats(&fnhash,"/h")) die_nomem();
187 pos = fnhash.len - 1;
188 if (!stralloc_catb(&fnhash,hash+1,COOKIE-1)) die_nomem();
189 if (!stralloc_0(&fnhash)) die_nomem();
190 if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
191 fnhashnew.s[pos] = 'H';
192
193 fdnew = open_trunc(fnhashnew.s);
194 if (fdnew == -1) die_hashnew();
195 buffer_init(&bo,buffer_unixwrite,fdnew,outbuf,sizeof(outbuf));
196
197 fd = open_read(fnhash.s);
198 if (fd == -1) {
199 if (errno != ENOENT)
200 logmsg(WHO,111,FATAL,B(ERR_READ,fnhash.s));
201 makedir(fndir.s);
202 if (rename(fndatenew.s,fndate.s) == -1)
203 logmsg(WHO,111,FATAL,B(ERR_MOVE,fndatenew.s," to ",fndate.s));
204 }
205 else {
206 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
207 switch (buffer_copy(&bo,&bi)) {
208 case -2: die_msgin();
209 case -3: die_hashnew();
210 }
211 close(fd);
212 if (unlink(fndatenew.s) == -1)
213 logmsg(WHO,111,FATAL,B(ERR_DELETE,fndatenew.s));
214 }
215 if (buffer_puts(&bo," ") == -1) die_hashnew();
216 if (buffer_put(&bo,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
217 if (buffer_puts(&bo,"\n") == -1) die_hashnew();
218 if (buffer_flush(&bo) == -1) die_hashnew();
219 if (fsync(fdnew) == -1) die_hashnew();
220 if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
221
222 if (rename(fnhashnew.s,fnhash.s) == -1)
223 logmsg(WHO,111,FATAL,B(ERR_MOVE,fnhashnew.s," to ",fnhash.s));
224}
225
226stralloc bounce = {0};
227stralloc line = {0};
228stralloc header = {0};
229stralloc intro = {0};
230stralloc failure = {0};
231stralloc paragraph = {0};
235
236stralloc key = {0};
237char msginbuf[1024];
238buffer bm;
239
240int main(int argc,char **argv)
241{
242 char *action;
243 const char *ret;
244 char *cp;
245 unsigned long msgnum;
246 unsigned long cookiedate;
247 unsigned long when;
248 unsigned long listno = 0L;
249 int match;
250 unsigned int i;
251 int flagdig = 0;
252 int flagmaster = 0;
253 int flagreceipt = 0;
254 int fdlock;
255 char ch;
256
257 umask(022);
258 sig_pipeignore();
259 when = (unsigned long) now();
260
261 dir = argv[1];
262 if (!dir) die_usage();
263 if (*dir == '-') { /* for normal use */
264 if (dir[1] == 'd') {
265 flagdig = 1;
266 } else if (dir[1] == 'D') {
267 flagdig = 0;
268 } else
269 die_usage();
270 dir = argv[2];
271 if (!dir) die_usage();
272 }
273
274 sender = env_get("SENDER");
275 if (!sender) logmsg(WHO,100,FATAL,ERR_NOSENDER);
276 if (!*sender) logmsg(WHO,0,INFO,ERR_BOUNCE);
277 action = env_get("DEFAULT");
278 if (!action || !*action) logmsg(WHO,100,FATAL,ERR_NODEFAULT);
279
280 if (chdir(dir) == -1)
281 logmsg(WHO,111,FATAL,B(ERR_SWITCH,dir));
282
283 switch (openreadclose("key",&key,32)) {
284 case -1: logmsg(WHO,111,FATAL,B(ERR_READ,dir,"/key: "));
285 case 0: logmsg(WHO,100,FATAL,B(dir,"/key",ERR_NOEXIST));
286 }
287 workdir = dir;
288
289 if (str_start(action,"receipt-")) {
290 flagreceipt = 1;
291 action += 8;
292 }
293 ch = *action; /* -d -digest, -m -master, -g -getmaster */
294 if (ch && action[1] == '-') {
295 switch (ch) {
296 case 'g': flagmaster = 1; flagdig = 1; action += 2; break;
297 case 'm': flagmaster = 1; action += 2; break;
298 default: break;
299 }
300 }
301 if (flagdig) {
302 if (!stralloc_copys(&ddir,dir)) die_nomem();
303 if (!stralloc_cats(&ddir,"/digest")) die_nomem();
304 if (!stralloc_0(&ddir)) die_nomem();
305 workdir = ddir.s;
306 }
307
308 if (!*action) die_trash();
309
310 if (flagreceipt || flagmaster) /* check cookie */
311 if (str_chr(action,'-') == COOKIE) {
312 action[COOKIE] = '\0';
313 hashp = action;
314 action += COOKIE + 1;
315 }
316
317 if (!flagreceipt) {
318 if (!flagmaster && str_start(action,"probe-")) {
319 action += 6;
320 action += scan_ulong(action,&cookiedate);
321 if (now() - cookiedate > 3000000) die_trash();
322 if (*action++ != '.') die_trash();
323 i = str_chr(action,'-');
324 if (i != COOKIE) die_trash();
325 byte_copy(hashcopy,COOKIE,action);
326 action += COOKIE;
327 if (*action++ != '-') die_trash();
328 i = str_rchr(action,'=');
329 if (!stralloc_copyb(&line,action,i)) die_nomem();
330 if (action[i]) {
331 if (!stralloc_cats(&line,"@")) die_nomem();
332 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
333 }
334 if (!stralloc_0(&line)) die_nomem();
335 strnum[fmt_ulong(strnum,cookiedate)] = 0;
336 cookie(hash,key.s,key.len,strnum,line.s,"P");
337 if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
338
339 subscribe(workdir,line.s,0,"","-probe",1,-1,(char *) 0);
340 _exit(99);
341 }
342
343 if (!stralloc_copys(&line,workdir)) die_nomem();
344 if (!stralloc_cats(&line,"/lockbounce")) die_nomem();
345 if (!stralloc_0(&line)) die_nomem();
346
347 fdlock = lockfile(line.s);
348
349 if (!flagmaster && str_start(action,"warn-")) {
350 action += 5;
351 action += scan_ulong(action,&cookiedate);
352 if (now() - cookiedate > 3000000) die_trash();
353 if (*action++ != '.') die_trash();
354 i = str_chr(action,'-');
355 if (i != COOKIE) die_trash();
356 byte_copy(hashcopy,COOKIE,action);
357 action += COOKIE;
358 if (*action++ != '-') die_trash();
359 i = str_rchr(action,'=');
360 if (!stralloc_copyb(&line,action,i)) die_nomem();
361 if (action[i]) {
362 if (!stralloc_cats(&line,"@")) die_nomem();
363 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
364 }
365 if (!stralloc_0(&line)) die_nomem();
366 strnum[fmt_ulong(strnum,cookiedate)] = 0;
367 cookie(hash,key.s,key.len,strnum,line.s,"W");
368 if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
369
370 if (readclose(0,&bounce,1024) == -1) die_msgin();
371 dowit(line.s,when,&bounce);
372 _exit(99);
373 }
374 }
375 action += scan_ulong(action,&msgnum);
376 if (*action++ != '-') die_badaddr();
377 cp = action;
378 if (*action >= '0' && *action <= '9') { /* listno */
379 action += scan_ulong(action,&listno);
380 listno++; /* logging is 1-53, not 0-52 */
381 }
382
383 if (hashp) { /* scrap bad cookies */
384 if ((ret = checktag(workdir,msgnum,0L,"x",(char *) 0,hashp))) {
385 if (*ret) logmsg(WHO,111,FATAL,ret);
386 else die_trash();
387 } else if (flagreceipt) {
388 if (!(ret = loginfo(dir,msgnum,listno,0L,5))) {
389 closesql();
390 logmsg(WHO,99,WARN,B("receipt:",cp," [",hashp,"]"));
391 }
392 if (*ret) logmsg(WHO,111,FATAL,ret);
393 else logmsg(WHO,0,INFO,ERR_DONE);
394 } else if (*action) { /* post VERP master bounce */
395 if ((ret = loginfo(dir,msgnum,listno,0L,-1))) {
396 closesql();
397 logmsg(WHO,0,WARN,B("bounce [",hashp,"]"));
398 }
399 if (*ret) logmsg(WHO,111,FATAL,ret);
400 else logmsg(WHO,99,INFO,ERR_DONE);
401 }
402 } else if (flagreceipt || flagmaster)
403 die_badaddr();
404
405 if (*action) {
406 i = str_rchr(action,'=');
407 if (!stralloc_copyb(&line,action,i)) die_nomem();
408 if (action[i]) {
409 if (!stralloc_cats(&line,"@")) die_nomem();
410 if (!stralloc_cats(&line,action + i + 1)) die_nomem();
411 }
412 if (!stralloc_0(&line)) die_nomem();
413 if (readclose(0,&bounce,1024) == -1) die_msgin();
414 doit(line.s,msgnum,when,&bounce);
415 _exit(99);
416 }
417
418 /* pre-VERP bounce, in QSBMF format. Receipts are never pre-VERP */
419
420 buffer_init(&bm,buffer_unixread,0,msginbuf,sizeof(msginbuf));
421
422 flaghaveheader = 0;
423 flaghaveintro = 0;
424
425 for (;;) {
426 if (!stralloc_copys(&paragraph,"")) die_nomem();
427 for (;;) {
428 if (getln(&bm,&line,&match,'\n') == -1) die_msgin();
429 if (!match) die_trash();
430 if (!stralloc_cat(&paragraph,&line)) die_nomem();
431 if (line.len <= 1) break;
432 }
433
434 if (!flaghaveheader) {
435 if (!stralloc_copy(&header,&paragraph)) die_nomem();
436 flaghaveheader = 1;
437 continue;
438 }
439
440 if (!flaghaveintro) {
441 if (paragraph.s[0] == '-' && paragraph.s[1] == '-')
442 continue; /* skip MIME boundary if it exists */
443 if (paragraph.len < 15) die_trash();
444 if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
445 if (!stralloc_copy(&intro,&paragraph)) die_nomem();
446 flaghaveintro = 1;
447 continue;
448 }
449
450 if (paragraph.s[0] == '-')
451 break;
452
453 if (paragraph.s[0] == '<') {
454 if (!stralloc_copy(&failure,&paragraph)) die_nomem();
455
456 if (!stralloc_copy(&bounce,&header)) die_nomem();
457 if (!stralloc_cat(&bounce,&intro)) die_nomem();
458 if (!stralloc_cat(&bounce,&failure)) die_nomem();
459
460 i = byte_chr(failure.s,failure.len,'\n');
461 if (i < 3) die_trash();
462
463 if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
464 if (byte_chr(line.s,line.len,'\0') == line.len) {
465 if (!stralloc_0(&line)) die_nomem();
466 if (flagmaster) { /* bounced msg slave! */
467 if ((i = str_rchr(line.s,'@')) >= 5) { /* 00_52@host */
468 line.s[i] = '\0'; /* 00_52 */
469 scan_ulong(line.s + i - 5,&listno);
470 if ((ret = loginfo(dir,msgnum,listno + 1,0L,-1)) && *ret) // FIXME
471 logmsg(WHO,111,FATAL,ret);
472 logmsg(WHO,0,WARN,B("bounce ",line.s + i - 5));
474 }
475 } else
476 doit(line.s,msgnum,when,&bounce);
477 }
478 }
479 }
480 closesql();
481
483 logmsg(WHO,0,WARN,B("[",hashp,"]"));
484 else
485 _exit(99);
486
487// return 0;
488}
datetime_sec now(void)
Definition now.c:5
#define COOKIE
Definition cookie.h:4
int issub()
Returns (char *) to match if userhost is in the subscriber database dbname, 0 otherwise....
int quote2(stralloc *sa, const char *s)
Definition quote.c:65
Error messages. If you translate these, I would urge you to keep the English version as well....
#define ERR_NOMEM
Definition errtxt.h:14
#define ERR_MOVE
Definition errtxt.h:29
#define ERR_NOEXIST
Definition errtxt.h:40
#define ERR_READ_INPUT
Definition errtxt.h:26
#define ERR_DONE
Definition errtxt.h:156
#define ERR_DELETE
Definition errtxt.h:25
#define ERR_READ
Definition errtxt.h:18
#define ERR_CREATE
Definition errtxt.h:28
#define ERR_NODEFAULT
Definition errtxt.h:35
#define ERR_SWITCH
Definition errtxt.h:42
#define ERR_WRITE
Definition errtxt.h:17
#define ERR_NOSENDER
Definition errtxt.h:37
#define ERR_BOUNCE
Definition errtxt.h:38
#define ERR_BAD_RETURN_ADDRESS
Definition errtxt.h:51
int lockfile(const char *)
Definition lockfile.c:15
const char * loginfo(const char *dir, unsigned long msgnum, unsigned long, unsigned long subs, int done)
Definition loginfo.c:12
void closesql(void)
close connection to SQL server, if open
Definition opensql.c:21
int subscribe(const char *dir, const char *username, int flagadd, const char *from, const char *event, int flagmysql, int forcehash, const char *table_override)
Definition subscribe.c:76
void die_nomem()
Definition getconf.c:17
#define WHO
Definition author.c:1
char * workdir
Definition ezmlm-get.c:129
stralloc listno
Definition ezmlm-get.c:78
stralloc ddir
Definition ezmlm-get.c:116
char inbuf[1024]
char outbuf[1024]
char * dir
buffer bi
buffer bo
int flagdig
int main()
Definition ezmlm-weed.c:69
void cookie(char *hash, const char *key, unsigned int keylen, const char *date, const char *addr, const char *action)
Definition cookie.c:14
const char * checktag(const char *dir, unsigned long num, unsigned long listno, const char *action, const char *seed, const char *hash)
Definition checktag.c:27
stralloc fndate
stralloc failure
stralloc bounce
unsigned long addrno
stralloc header
stralloc intro
stralloc fndatenew
stralloc paragraph
stralloc fndir
const char * cp
Definition ezmlm-cron.c:76
stralloc addr
Definition ezmlm-cron.c:45
int fdlock
Definition ezmlm-cron.c:71
int fd
Definition ezmlm-cgi.c:141
datetime_sec when
Definition ezmlm-cgi.c:173
int match
Definition ezmlm-cgi.c:140
void die_datenew()
void makedir(char *s)
void doit(const char *addr, unsigned long msgnum, unsigned long when, const stralloc *bounce)
unsigned long cookiedate
int flagmasterbounce
void die_msgin()
char hashcopy[COOKIE]
char * hashp
int flaghaveheader
void die_hashnew()
stralloc fnhashnew
char msginbuf[1024]
unsigned long addrno1
buffer bm
void dowit(char *addr, unsigned long when, stralloc *bounce)
stralloc fnhash
int flaghaveintro
unsigned long msgnum
stralloc quoted
Definition ezmlm-clean.c:90
stralloc action
Definition ezmlm-store.c:84
const char * logmsg(const char *dir, unsigned long num, unsigned long listno, unsigned long subs, int done)
Definition loginfo.c:32