1 /*
2 ** Copyright 1998 - 2008 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #include "config.h"
7 #include "funcs.h"
8 #include "deliverdotlock.h"
9 #include "mio.h"
10 #include "formatmbox.h"
11 #include "xconfig.h"
12 #include "varlist.h"
13 #include "pipefds.h"
14 #include "log.h"
15 #include "exittrap.h"
16 #include "maildir.h"
17 #include "filelock.h"
18 #include "setgroupid.h"
19 #include <sys/types.h>
20 #if HAVE_SYS_STAT_H
21 #include <sys/stat.h>
22 #endif
23 #if HAVE_SYS_FILE_H
24 #include <sys/file.h>
25 #endif
26 #include "mywait.h"
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #if HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #if HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #include <errno.h>
37
38 static const char rcsid[]="$Id: deliver.C,v 1.17 2009/06/27 16:32:38 mrsam Exp $";
39
40 ///////////////////////////////////////////////////////////////////////////
41 //
42 // Deliver to a mailbox, a file, or a maildir.
43 //
44 // If there was a delivery error, error message is printed, and we
45 // return -1.
46 //
47 // If delivery was succesfull, 0 is returned.
48 //
49 ///////////////////////////////////////////////////////////////////////////
50
delivery(const char * mailbox)51 int delivery(const char *mailbox)
52 {
53 FormatMbox format_mbox;
54
55 if (format_mbox.HasMsg()) return (0);
56
57 DeliverDotLock dotlock;
58 Buffer b;
59
60 if ( *mailbox == '!' || *mailbox == '|' )
61 {
62 Buffer cmdbuf;
63
64 if (*mailbox == '!')
65 {
66 b="SENDMAIL";
67
68 const char *sendmail=GetVarStr(b);
69
70 cmdbuf=sendmail;
71
72 cmdbuf += " -f '' ";
73 cmdbuf += mailbox+1;
74 }
75 else
76 cmdbuf= mailbox+1;
77
78 cmdbuf += '\0';
79
80 if (VerboseLevel() > 0)
81 merr << "maildrop: Delivering to |" <<
82 (const char *)cmdbuf << "\n";
83
84 PipeFds pipe;
85
86 if (pipe.Pipe())
87 throw "Cannot create pipe.";
88
89 pid_t pid=fork();
90
91 if (pid < 0)
92 throw "Cannot fork.";
93
94 if (pid == 0)
95 {
96 pipe.close1();
97 dup2(pipe.fds[0], 0);
98 pipe.close0();
99
100 try
101 {
102 subshell(cmdbuf);
103 }
104 catch (const char *p)
105 {
106 if (write(2, p, strlen(p)) < 0 ||
107 write(2, "\n", 1) < 0)
108 ; /* ignored */
109 _exit(100);
110 }
111 #if NEED_NONCONST_EXCEPTIONS
112 catch (char *p)
113 {
114 if (write(2, p, strlen(p)) < 0 ||
115 write(2, "\n", 1) < 0)
116 ; /* ignored */
117 _exit(100);
118 }
119 #endif
120 catch (...)
121 {
122 _exit(100);
123 }
124 }
125
126 Mio pipemio;
127
128 pipe.close0();
129 pipemio.fd(pipe.fds[1]);
130 pipe.fds[1]= -1;
131 format_mbox.Init(0);
132
133 int rc=format_mbox.DeliverTo(pipemio);
134 int wait_stat;
135
136 while (wait(&wait_stat) != pid)
137 ;
138
139 if (wait_stat == 0)
140 rc=0;
141
142 log(mailbox, rc || wait_stat, format_mbox);
143
144 {
145 Buffer name, val;
146
147 if (rc) wait_stat= -1;
148 else wait_stat= WIFEXITED(wait_stat)
149 ? WEXITSTATUS(wait_stat):-1;
150
151 val.append( (unsigned long)wait_stat);
152 name="EXITCODE";
153 SetVar(name, val);
154 }
155
156 if (rc) return (-1);
157 }
158 else if (Maildir::IsMaildir(mailbox))
159 {
160 Maildir deliver_maildir;
161 Mio deliver_file;
162
163 if ( deliver_maildir.MaildirOpen(mailbox, deliver_file,
164 maildrop.msgptr->MessageSize()) < 0)
165 {
166 #if HAVE_COURIER
167 throw 75;
168 #else
169 throw 77;
170 #endif
171 }
172
173 format_mbox.Init(0);
174 if (format_mbox.DeliverTo(deliver_file))
175 {
176 log(mailbox, -1, format_mbox);
177 return (-1);
178 }
179 deliver_maildir.MaildirSave();
180 log(mailbox, 0, format_mbox);
181 }
182 else // Delivering to a mailbox (hopefully)
183 {
184 if (VerboseLevel() > 0)
185 merr << "maildrop: Delivering to " << mailbox << "\n";
186
187 #if USE_DOTLOCK
188 dotlock.LockMailbox(mailbox);
189 #endif
190
191 struct stat stat_buf;
192 Mio mio;
193 Buffer name_buf;
194
195 name_buf="UMASK";
196 const char *um=GetVarStr(name_buf);
197 unsigned int umask_val=077;
198
199 sscanf(um, "%o", &umask_val);
200
201 umask_val=umask(umask_val);
202
203 if (mio.Open(mailbox, O_CREAT | O_WRONLY, 0666) < 0)
204 {
205 umask_val=umask(umask_val);
206 throw "Unable to open mailbox.";
207 }
208 umask_val=umask(umask_val);
209
210 if (fstat(mio.fd(), &stat_buf) < 0)
211 throw "Unable to open mailbox.";
212
213 #if USE_FLOCK
214
215 if (VerboseLevel() > 4)
216 merr << "maildrop: Flock()ing " << mailbox << ".\n";
217
218 FileLock::do_filelock(mio.fd());
219 #endif
220 if (S_ISREG(stat_buf.st_mode))
221 {
222 if (mio.seek(0L, SEEK_END) < 0)
223 throw "Seek error on mailbox.";
224 dotlock.trap_truncate(mio.fd(), stat_buf.st_size);
225 }
226
227 if (VerboseLevel() > 4)
228 merr << "maildrop: Appending to " << mailbox << ".\n";
229
230 try
231 {
232 format_mbox.Init(1);
233
234 if ((stat_buf.st_size > 0 &&
235 mio.write(
236 #if CRLF_TERM
237 "\r\n", 2
238 #else
239 "\n", 1
240 #endif
241 ) < 0) ||
242 format_mbox.DeliverTo(mio))
243 {
244 dotlock.truncate();
245 log(mailbox, -1, format_mbox);
246 return (-1);
247 }
248 }
249 catch (...)
250 {
251 dotlock.truncate();
252 log(mailbox, -1, format_mbox);
253 throw;
254 }
255 log(mailbox, 0, format_mbox);
256 }
257
258 if (VerboseLevel() > 4)
259 merr << "maildrop: Delivery complete.\n";
260
261 return (0);
262 }
263
subshell(const char * cmd)264 void subshell(const char *cmd)
265 {
266 Buffer b;
267
268 b="SHELL";
269
270 const char *shell=GetVarStr(b);
271
272 const char *p, *q;
273
274 for (p=q=shell; *p; p++)
275 if (*p == SLASH_CHAR) q=p+1;
276
277 char **env=ExportEnv();
278
279 int n;
280
281 for (n=0; n<NSIG; n++)
282 signal(n, SIG_DFL);
283
284 setgroupid(getgid()); // Just in case.
285 setuid(getuid());
286 ExitTrap::onfork();
287 execle(shell, q, "-c", cmd, (const char *)0, env);
288 if (write (2, "Unable to execute ", 18) < 0 ||
289 write (2, shell, strlen(shell)) < 0 ||
290 write (2, "\n", 1) < 0)
291 ; /* ignored */
292 _exit(100);
293 }
294