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