1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 #include "error.h"
5 #include "case.h"
6 #include "stralloc.h"
7 #include "str.h"
8 #include "env.h"
9 #include "sender.h"
10 #include "error.h"
11 #include "sig.h"
12 #include "fork.h"
13 #include "wait.h"
14 #include "strerr.h"
15 #include "byte.h"
16 #include "getln.h"
17 #include "qmail.h"
18 #include "substdio.h"
19 #include "readwrite.h"
20 #include "seek.h"
21 #include "quote.h"
22 #include "datetime.h"
23 #include "now.h"
24 #include "fmt.h"
25 #include "getconfopt.h"
26 #include "cookie.h"
27 #include "messages.h"
28 #include "copy.h"
29 #include "open.h"
30 #include "lock.h"
31 #include "wrap.h"
32 #include "die.h"
33 #include "idx.h"
34 #include "mime.h"
35 #include "config.h"
36 #include "auto_version.h"
37
38 static int flagmime = MOD_MIME; /* default is message as attachment */
39
40 const char FATAL[] = "ezmlm-confirm: fatal: ";
41 const char INFO[] = "ezmlm-confirm: info: ";
42 const char USAGE[] =
43 "ezmlm-confirm: usage: ezmlm-confirm [-cCmMrRvV] dir [/path/ezmlm-send]";
44
45 static const char *replyto = (char *) 0;
46 static stralloc sendopt = {0};
47
48 static struct option options[] = {
49 OPT_COPY_FLAG(sendopt,'c'),
50 OPT_COPY_FLAG(sendopt,'C'),
51 OPT_COPY_FLAG(sendopt,'r'),
52 OPT_COPY_FLAG(sendopt,'R'),
53 OPT_FLAG(flagmime,'m',1,0),
54 OPT_FLAG(flagmime,'M',0,0),
55 OPT_CSTR(replyto,'t',0),
56 OPT_CSTR(replyto,'T',0),
57 OPT_END
58 };
59
60 static stralloc to = {0};
61 static datetime_sec when;
62 static stralloc line = {0};
63
checkfile(const char * fn,stralloc * fnmsg)64 static int checkfile(const char *fn,stralloc *fnmsg)
65 /* looks for DIR/mod/unconfirmed/fn. */
66 /* Returns: */
67 /* 1 found in unconfirmed */
68 /* 0 not found */
69 /* Handles errors. */
70 /* ALSO: if found, fnmsg contains the o-terminated*/
71 /* file name. */
72 {
73 struct stat st;
74 if (!stralloc_copys(fnmsg,"mod/unconfirmed/")) die_nomem();
75 if (!stralloc_cats(fnmsg,fn)) die_nomem();
76 if (!stralloc_0(fnmsg)) die_nomem();
77 if (stat(fnmsg->s,&st) == 0)
78 return 1;
79 if (errno != error_noent)
80 strerr_die2sys(111,FATAL,MSG1(ERR_STAT,fnmsg->s));
81 return 0;
82 }
83
maketo(void)84 static void maketo(void)
85 /* expects line to be a return-path line. If it is and the format is valid */
86 /* to is set to to the sender. Otherwise, to is left untouched. Assuming */
87 /* to is empty to start with, it will remain empty if no sender is found. */
88 {
89 unsigned int x, y;
90
91 if (case_startb(line.s,line.len,"return-path:")) {
92 x = 12 + byte_chr(line.s + 12,line.len-12,'<');
93 if (x != line.len) {
94 y = byte_rchr(line.s + x,line.len-x,'>');
95 if (y + x != line.len) {
96 if (!stralloc_copyb(&to,line.s+x+1,y-1)) die_nomem();
97 if (!stralloc_0(&to)) die_nomem();
98 } /* no return path-> no addressee. A NUL in the sender */
99 } /* is no worse than a faked sender, so no problem */
100 }
101 }
102
main(int argc,char ** argv)103 int main(int argc, char **argv)
104 {
105 const char *sender;
106 const char *def;
107 const char *local;
108 const char *action;
109 int fd;
110 int match;
111 unsigned int start,confnum;
112 int child;
113 int opt;
114 const char *cp;
115
116 char hash[COOKIE];
117 stralloc fnbase = {0};
118 stralloc fnmsg = {0};
119
120 const char *dir;
121
122 substdio sstext;
123 char textbuf[1024];
124
125
126 (void) umask(022);
127 sig_pipeignore();
128 when = now();
129
130 if (!stralloc_copys(&sendopt,"-")) die_nomem();
131 opt = getconfopt(argc,argv,options,1,&dir);
132
133 sender = get_sender();
134 if (!sender) die_sender();
135 local = env_get("LOCAL");
136 if (!local) strerr_die2x(100,FATAL,MSG(ERR_NOLOCAL));
137 def = env_get("DEFAULT");
138 if (!def) strerr_die2x(100,FATAL,MSG(ERR_NODEFAULT));
139
140 if (!*sender)
141 strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
142 if (!sender[str_chr(sender,'@')])
143 strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS));
144 if (str_equal(sender,"#@[]"))
145 strerr_die2x(100,FATAL,MSG(ERR_BOUNCE));
146
147 /* local should be >= def, but who knows ... */
148 cp = local + str_len(local) - str_len(def) - 2;
149 if (cp < local) die_badformat();
150 action = local + byte_rchr(local,cp - local,'-');
151 if (action == cp) die_badformat();
152 action++;
153
154 if (!action[0]) die_badformat();
155 if (!str_start(action,ACTION_CONFIRM) && !str_start(action,ACTION_DISCARD))
156 die_badformat();
157 start = str_chr(action,'-');
158 if (!action[start]) die_badformat();
159 confnum = 1 + start + str_chr(action + start + 1,'.');
160 if (!action[confnum]) die_badformat();
161 confnum += 1 + str_chr(action + confnum + 1,'.');
162 if (!action[confnum]) die_badformat();
163 if (!stralloc_copyb(&fnbase,action+start+1,confnum-start-1)) die_nomem();
164 if (!stralloc_0(&fnbase)) die_nomem();
165 cookie(hash,key.s,key.len,fnbase.s,"","a");
166 if (byte_diff(hash,COOKIE,action+confnum+1))
167 die_badformat();
168
169 lockfile("mod/confirmlock");
170
171 if (!checkfile(fnbase.s,&fnmsg)) strerr_die2x(100,FATAL,MSG(ERR_MOD_TIMEOUT));
172 /* Here, we have an existing filename in fnbase with the complete path */
173 /* from the current dir in fnmsg. */
174
175 if (str_start(action,ACTION_DISCARD)) {
176 unlink(fnmsg.s);
177 strerr_die1x(0,"ezmlm-confirm: info: post rejected");
178 } else if (str_start(action,ACTION_CONFIRM)) {
179 fd = open_read(fnmsg.s);
180 if (fd == -1) {
181 if (errno !=error_noent)
182 strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fnmsg.s));
183 else /* shouldn't happen since we've got lock */
184 strerr_die3x(100,FATAL,fnmsg.s,MSG(ERR_MOD_TIMEOUT));
185 }
186
187 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
188 /* read "Return-Path:" line */
189 if (getln(&sstext,&line,&match,'\n') == -1 || !match)
190 strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT));
191 maketo(); /* extract SENDER to "to" */
192 env_put2("SENDER",to.s); /* set SENDER */
193 if (seek_begin(fd) == -1) /* rewind, since we read an entire buffer */
194 strerr_die2sys(111,FATAL,MSG1(ERR_SEEK,fnmsg.s));
195
196 if ((child = wrap_fork()) == 0) {
197 close(0);
198 dup(fd); /* make fnmsg.s stdin */
199 if (argc > opt + 1)
200 wrap_execvp((const char **)argv + opt);
201 else if (argc > opt)
202 wrap_execsh(argv[opt]);
203 else
204 wrap_execbin("/ezmlm-send", &sendopt, dir);
205 }
206 /* parent */
207 close(fd);
208 wrap_exitcode(child);
209
210 unlink(fnmsg.s);
211 }
212 _exit(0);
213 }
214