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