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