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