1 /* $Id$ */
2 
3 /*
4  * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 
30 #include "fdm.h"
31 #include "deliver.h"
32 
33 /* With gcc 2.95.x, you can't include zlib.h before openssl.h. */
34 #include <zlib.h>
35 
36 int	 deliver_mbox_deliver(struct deliver_ctx *, struct actitem *);
37 void	 deliver_mbox_desc(struct actitem *, char *, size_t);
38 
39 int	 deliver_mbox_write(FILE *, gzFile, const void *, size_t);
40 
41 struct deliver deliver_mbox = {
42 	"mbox",
43 	DELIVER_ASUSER,
44 	deliver_mbox_deliver,
45 	deliver_mbox_desc
46 };
47 
48 int
deliver_mbox_write(FILE * f,gzFile gzf,const void * buf,size_t len)49 deliver_mbox_write(FILE *f, gzFile gzf, const void *buf, size_t len)
50 {
51 	if (gzf == NULL) {
52 		if (fwrite(buf, len, 1, f) != 1) {
53 			errno = EIO;
54 			return (-1);
55 		}
56 	} else {
57 		if ((size_t) gzwrite(gzf, buf, len) != len) {
58 			errno = EIO;
59 			return (-1);
60 		}
61 	}
62 
63 	return (0);
64 }
65 
66 int
deliver_mbox_deliver(struct deliver_ctx * dctx,struct actitem * ti)67 deliver_mbox_deliver(struct deliver_ctx *dctx, struct actitem *ti)
68 {
69 	struct account			*a = dctx->account;
70 	struct mail			*m = dctx->mail;
71 	struct deliver_mbox_data	*data = ti->data;
72 	char				*path, *ptr, *lptr, *from = NULL;
73 	const char			*msg;
74 	size_t				 len, llen;
75 	int				 fd, saved_errno;
76 	FILE				*f;
77 	gzFile				 gzf;
78 	long long			 used;
79 	sigset_t			 set, oset;
80 	struct stat			 sb;
81 
82 	f = gzf = NULL;
83 	fd = -1;
84 
85 	path = replacepath(&data->path, m->tags, m, &m->rml, dctx->udata->home);
86 	if (path == NULL || *path == '\0') {
87 		log_warnx("%s: empty path", a->name);
88 		goto error;
89 	}
90 	if (data->compress) {
91 		len = strlen(path);
92 		if (len < 3 || strcmp(path + len - 3, ".gz") != 0) {
93 			path = xrealloc(path, 1, len + 4);
94 			strlcat(path, ".gz", len + 4);
95 		}
96 	}
97 	log_debug2("%s: saving to mbox %s", a->name, path);
98 
99 	/* Save the mbox path. */
100 	add_tag(&m->tags, "mbox_file", "%s", path);
101 
102 	/* Check permissions and ownership. */
103 	if (stat(path, &sb) != 0) {
104 		if (conf.no_create || errno != ENOENT)
105 			goto error_log;
106 
107 		log_debug2("%s: creating %s", a->name, xdirname(path));
108 		if (xmkpath(xdirname(path), -1, conf.file_group, DIRMODE) != 0)
109 			goto error_log;
110 	} else {
111 		if ((msg = checkmode(&sb, UMASK(FILEMODE))) != NULL)
112 			log_warnx("%s: %s: %s", a->name, path, msg);
113 		if ((msg = checkowner(&sb, -1)) != NULL)
114 			log_warnx("%s: %s: %s", a->name, path, msg);
115 		if ((msg = checkgroup(&sb, conf.file_group)) != NULL)
116 			log_warnx("%s: %s: %s", a->name, path, msg);
117 	}
118 
119 	/* Create or open the mbox. */
120 	used = 0;
121 	do {
122 		if (conf.no_create)
123 			fd = openlock(path, O_WRONLY|O_APPEND, conf.lock_types);
124 		else {
125 			fd = createlock(path, O_WRONLY|O_APPEND,
126 			    -1, conf.file_group, FILEMODE, conf.lock_types);
127 		}
128 		if (fd == -1 && errno == EEXIST)
129 			fd = openlock(path, O_WRONLY|O_APPEND, conf.lock_types);
130 		if (fd == -1) {
131 			if (errno == EAGAIN) {
132 				if (locksleep(a->name, path, &used) != 0)
133 					goto error;
134 				continue;
135 			}
136 			goto error_log;
137 		}
138 	} while (fd < 0);
139 
140 	/* Open gzFile or FILE * for writing. */
141 	if (data->compress) {
142 		if ((gzf = gzdopen(fd, "a")) == NULL) {
143 			errno = ENOMEM;
144 			goto error_log;
145 		}
146 	} else {
147 		if ((f = fdopen(fd, "a")) == NULL)
148 			goto error_log;
149 	}
150 
151 	/*
152 	 * mboxes are a pain: if we are interrupted after this we risk
153 	 * having written a partial mail. So, block SIGTERM until we're
154 	 * done.
155 	 */
156 	sigemptyset(&set);
157 	sigaddset(&set, SIGTERM);
158 	if (sigprocmask(SIG_BLOCK, &set, &oset) < 0)
159 		fatal("sigprocmask failed");
160 
161 	/* Write the from line. */
162 	from = make_from(m, dctx->udata->name);
163 	if (deliver_mbox_write(f, gzf, from, strlen(from)) < 0) {
164 		xfree(from);
165 		goto error_unblock;
166 	}
167 	if (deliver_mbox_write(f, gzf, "\n", 1) < 0) {
168 		xfree(from);
169 		goto error_unblock;
170 	}
171 	log_debug3("%s: using from line: %s", a->name, from);
172 	xfree(from);
173 
174 	/* Write the mail, escaping from lines. */
175 	line_init(m, &ptr, &len);
176 	while (ptr != NULL) {
177 		if (ptr != m->data) {
178 			/* Skip leading >s. */
179 			lptr = ptr;
180 			llen = len;
181 			while (*lptr == '>' && llen > 0) {
182 				lptr++;
183 				llen--;
184 			}
185 
186 			if (llen >= 5 && strncmp(lptr, "From ", 5) == 0) {
187 				log_debug2("%s: quoting from line: %.*s",
188 				    a->name, (int) len - 1, ptr);
189 				if (deliver_mbox_write(f, gzf, ">", 1) < 0)
190 					goto error_unblock;
191 			}
192 		}
193 
194 		if (deliver_mbox_write(f, gzf, ptr, len) < 0)
195 			goto error_unblock;
196 
197 		line_next(m, &ptr, &len);
198 	}
199 
200 	/* Append newlines. */
201 	if (deliver_mbox_write(f, gzf, "\n\n", 2) < 0)
202 		goto error_unblock;
203 
204 	/* Flush buffers and sync. */
205 	if (gzf == NULL) {
206 		if (fflush(f) != 0)
207 			goto error_unblock;
208 	} else {
209 		if (gzflush(gzf, Z_FINISH) != Z_OK) {
210 			errno = EIO;
211 			goto error_unblock;
212 		}
213 	}
214 	if (fsync(fd) != 0)
215 		goto error_unblock;
216 
217 	if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0)
218 		fatal("sigprocmask failed");
219 
220 	if (gzf != NULL)
221 		gzclose(gzf);
222 	if (f != NULL)
223 		fclose(f);
224 	closelock(fd, path, conf.lock_types);
225 
226 	xfree(path);
227 	return (DELIVER_SUCCESS);
228 
229 error_unblock:
230 	saved_errno = errno;
231 	if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0)
232 		fatal("sigprocmask failed");
233 	errno = saved_errno;
234 
235 error_log:
236 	log_warn("%s: %s", a->name, path);
237 
238 error:
239 	if (gzf != NULL)
240 		gzclose(gzf);
241 	if (f != NULL)
242 		fclose(f);
243 	if (fd != -1)
244 		closelock(fd, path, conf.lock_types);
245 
246 	if (path != NULL)
247 		xfree(path);
248 	return (DELIVER_FAILURE);
249 }
250 
251 void
deliver_mbox_desc(struct actitem * ti,char * buf,size_t len)252 deliver_mbox_desc(struct actitem *ti, char *buf, size_t len)
253 {
254 	struct deliver_mbox_data	*data = ti->data;
255 
256 	if (data->compress)
257 		xsnprintf(buf, len, "mbox \"%s\" compress", data->path.str);
258 	else
259 		xsnprintf(buf, len, "mbox \"%s\"", data->path.str);
260 }
261