masqmail
view src/spool.c @ 281:ea5f86e0a81c
modes are now enforced exclusive
Other MTAs (exim, postfix) are more relaxing, but as combinations
of exclusive modes are senseless we behave more obvious if we
fail early. This makes understanding the behavior easier too.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Tue, 07 Dec 2010 14:04:56 -0300 |
parents | 92063f90f9be |
children | d04894d0fc64 |
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #include <sys/stat.h>
22 #include "masqmail.h"
23 #include "dotlock.h"
25 static gint
26 read_line(FILE * in, gchar * buf, gint buf_len)
27 {
28 gint p = 0;
29 gint c;
31 while ((c = getc(in)) != '\n' && (c != EOF)) {
32 if (p >= buf_len - 1) {
33 buf[buf_len-1] = '\0';
34 ungetc(c, in);
35 return buf_len;
36 }
37 buf[p++] = c;
38 }
40 if (c == EOF) {
41 return -1;
42 }
43 if ((p > 0) && (buf[p - 1] == '\r'))
44 p--;
45 buf[p++] = '\n';
46 buf[p] = '\0';
48 return p;
49 }
51 static void
52 spool_write_rcpt(FILE * out, address * rcpt)
53 {
54 gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' ');
56 if (rcpt->local_part[0] != '|') {
57 /* this is a paranoid check, in case it slipped through: */
58 /* if this happens, it is a bug */
59 if (rcpt->domain == NULL) {
60 logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", rcpt->local_part, conf.host_name);
61 logwrite(LOG_WARNING, "please report this bug.\n");
62 rcpt->domain = g_strdup(conf.host_name);
63 }
64 fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt));
65 } else {
66 fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part);
67 }
68 }
70 static address*
71 spool_scan_rcpt(gchar * line)
72 {
73 address *rcpt = NULL;
75 if (line[3] != '\0') {
76 if (line[4] != '|') {
77 rcpt = create_address(&(line[4]), TRUE);
78 } else {
79 rcpt = create_address_pipe(&(line[4]));
80 }
81 if (line[3] == 'X') {
82 addr_mark_delivered(rcpt);
83 } else if (line[3] == 'F') {
84 addr_mark_failed(rcpt);
85 }
86 }
87 return rcpt;
88 }
90 gboolean
91 spool_read_data(message * msg)
92 {
93 FILE *in;
94 gchar *spool_file;
96 DEBUG(5) debugf("spool_read_data entered\n");
97 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
98 DEBUG(5) debugf("reading data spool file '%s'\n", spool_file);
99 in = fopen(spool_file, "r");
100 if (!in) {
101 logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", spool_file, strerror(errno));
102 return FALSE;
103 }
105 char buf[MAX_DATALINE];
106 int len;
108 /* msg uid */
109 read_line(in, buf, MAX_DATALINE);
111 /* data */
112 msg->data_list = NULL;
113 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
114 msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf));
115 }
116 msg->data_list = g_list_reverse(msg->data_list);
117 fclose(in);
118 return TRUE;
119 }
121 gboolean
122 spool_read_header(message * msg)
123 {
124 FILE *in;
125 gchar *spool_file;
127 /* header spool: */
128 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
129 in = fopen(spool_file, "r");
130 if (!in) {
131 logwrite(LOG_ALERT, "could not open spool header file %s: %s\n",
132 spool_file, strerror(errno));
133 return FALSE;
134 }
136 header *hdr = NULL;
137 char buf[MAX_DATALINE];
138 int len;
140 /* msg uid */
141 read_line(in, buf, MAX_DATALINE);
143 /* envelope header */
144 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
145 if (buf[0] == '\n') {
146 break;
147 } else if (strncasecmp(buf, "MF:", 3) == 0) {
148 msg->return_path = create_address(&(buf[3]), TRUE);
149 DEBUG(3) debugf("spool_read: MAIL FROM: %s", msg->return_path->address);
150 } else if (strncasecmp(buf, "RT:", 3) == 0) {
151 address *addr;
152 addr = spool_scan_rcpt(buf);
153 if (addr_is_delivered(addr) || addr_is_failed(addr)) {
154 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr);
155 } else {
156 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
157 }
158 } else if (strncasecmp(buf, "PR:", 3) == 0) {
159 prot_id i;
160 for (i = 0; i < PROT_NUM; i++) {
161 if (strncasecmp(prot_names[i], &(buf[3]), strlen(prot_names[i])) == 0) {
162 break;
163 }
164 }
165 msg->received_prot = i;
166 } else if (strncasecmp(buf, "RH:", 3) == 0) {
167 g_strchomp(buf);
168 msg->received_host = g_strdup(&(buf[3]));
169 } else if (strncasecmp(buf, "ID:", 3) == 0) {
170 g_strchomp(buf);
171 msg->ident = g_strdup(&(buf[3]));
172 } else if (strncasecmp(buf, "DS:", 3) == 0) {
173 msg->data_size = atoi(&(buf[3]));
174 } else if (strncasecmp(buf, "TR:", 3) == 0) {
175 msg->received_time = (time_t) (atoi(&(buf[3])));
176 } else if (strncasecmp(buf, "TW:", 3) == 0) {
177 msg->warned_time = (time_t) (atoi(&(buf[3])));
178 }
179 /* so far ignore other tags */
180 }
182 /* mail headers */
183 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
184 if (strncasecmp(buf, "HD:", 3) == 0) {
185 hdr = get_header(&(buf[3]));
186 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
187 } else if ((buf[0] == ' ' || buf[0] == '\t') && hdr) {
188 char *tmp = hdr->header;
189 /* header continuation */
190 hdr->header = g_strconcat(hdr->header, buf, NULL);
191 hdr->value = hdr->header + (hdr->value - tmp);
192 } else {
193 break;
194 }
195 }
196 fclose(in);
197 return TRUE;
198 }
200 message*
201 msg_spool_read(gchar * uid, gboolean do_readdata)
202 {
203 message *msg;
204 gboolean ok = FALSE;
206 msg = create_message();
207 msg->uid = g_strdup(uid);
209 DEBUG(4) debugf("msg_spool_read():\n");
210 /* header spool: */
211 ok = spool_read_header(msg);
212 DEBUG(4) debugf(" spool_read_header() returned: %d (do_readdata had been: %d)\n",
213 ok, do_readdata);
214 if (ok && do_readdata) {
215 /* data spool: */
216 ok = spool_read_data(msg);
217 DEBUG(4) debugf(" spool_read_data() returned: %d\n", ok);
218 }
219 return msg;
220 }
222 /* write header. uid and gid should already be set to the
223 mail ids. Better call spool_write(msg, FALSE).
224 */
225 static gboolean
226 spool_write_header(message * msg)
227 {
228 GList *node;
229 gchar *spool_file, *tmp_file;
230 FILE *out;
231 gboolean ok = TRUE;
233 /* header spool: */
234 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
235 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
237 if ((out = fopen(tmp_file, "w"))) {
238 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
240 fprintf(out, "%s\n", msg->uid);
241 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
243 DEBUG(6) debugf("after MF\n");
244 foreach(msg->rcpt_list, node) {
245 address *rcpt = (address *) (node->data);
246 spool_write_rcpt(out, rcpt);
247 }
248 foreach(msg->non_rcpt_list, node) {
249 address *rcpt = (address *) (node->data);
250 spool_write_rcpt(out, rcpt);
251 }
252 DEBUG(6) debugf("after RT\n");
253 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
254 if (msg->received_host != NULL)
255 fprintf(out, "RH:%s\n", msg->received_host);
257 if (msg->ident != NULL)
258 fprintf(out, "ID:%s\n", msg->ident);
260 if (msg->data_size >= 0)
261 fprintf(out, "DS: %d\n", msg->data_size);
263 if (msg->received_time > 0)
264 fprintf(out, "TR: %u\n", (int) (msg->received_time));
266 if (msg->warned_time > 0)
267 fprintf(out, "TW: %u\n", (int) (msg->warned_time));
269 DEBUG(6) debugf("after RH\n");
270 fprintf(out, "\n");
272 foreach(msg->hdr_list, node) {
273 header *hdr = (header *) (node->data);
274 fprintf(out, "HD:%s", hdr->header);
275 }
276 if (fflush(out) == EOF)
277 ok = FALSE;
278 else if (fdatasync(fileno(out)) != 0) {
279 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
280 ok = FALSE;
281 }
282 fclose(out);
283 if (ok) {
284 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
285 DEBUG(4) debugf("spool_file = %s\n", spool_file);
286 ok = (rename(tmp_file, spool_file) != -1);
287 g_free(spool_file);
288 }
289 } else {
290 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
291 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
292 ok = FALSE;
293 }
295 g_free(tmp_file);
297 return ok;
298 }
300 gboolean
301 spool_write(message * msg, gboolean do_write_data)
302 {
303 GList *list;
304 gchar *spool_file, *tmp_file;
305 FILE *out;
306 gboolean ok = TRUE;
307 uid_t saved_uid, saved_gid;
308 /* user can read/write, group can read, others cannot do anything: */
309 mode_t saved_mode = saved_mode = umask(026);
311 /* set uid and gid to the mail ids */
312 if (!conf.run_as_user) {
313 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
314 }
316 /* header spool: */
317 ok = spool_write_header(msg);
319 if (ok && do_write_data) {
320 /* data spool: */
321 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid());
322 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
324 if ((out = fopen(tmp_file, "w"))) {
325 fprintf(out, "%s\n", msg->uid);
326 for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) {
327 fprintf(out, "%s", (gchar *) (list->data));
328 }
330 /* possibly paranoid ;-) */
331 if (fflush(out) == EOF) {
332 ok = FALSE;
333 } else if (fdatasync(fileno(out)) != 0) {
334 if (errno != EINVAL) { /* some fs do not support this.. I hope this also means that it is not necessary */
335 ok = FALSE;
336 }
337 }
338 fclose(out);
339 if (ok) {
340 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
341 DEBUG(4) debugf("spool_file = %s\n", spool_file);
342 ok = (rename(tmp_file, spool_file) != -1);
343 g_free(spool_file);
344 }
345 } else {
346 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n",
347 strerror(errno));
348 ok = FALSE;
349 }
350 g_free(tmp_file);
351 }
353 /* set uid and gid back */
354 if (!conf.run_as_user) {
355 set_euidgid(saved_uid, saved_gid, NULL, NULL);
356 }
358 umask(saved_mode);
360 return ok;
361 }
363 #define MAX_LOCKAGE 300
365 gboolean
366 spool_lock(gchar * uid)
367 {
368 uid_t saved_uid, saved_gid;
369 gchar *hitch_name;
370 gchar *lock_name;
371 gboolean ok = FALSE;
373 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
374 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
376 /* set uid and gid to the mail ids */
377 if (!conf.run_as_user) {
378 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
379 }
381 ok = dot_lock(lock_name, hitch_name);
382 if (!ok)
383 logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
385 /* set uid and gid back */
386 if (!conf.run_as_user) {
387 set_euidgid(saved_uid, saved_gid, NULL, NULL);
388 }
390 g_free(lock_name);
391 g_free(hitch_name);
393 return ok;
394 }
396 gboolean
397 spool_unlock(gchar * uid)
398 {
399 uid_t saved_uid, saved_gid;
400 gchar *lock_name;
402 /* set uid and gid to the mail ids */
403 if (!conf.run_as_user) {
404 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
405 }
407 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
408 dot_unlock(lock_name);
409 g_free(lock_name);
411 /* set uid and gid back */
412 if (!conf.run_as_user) {
413 set_euidgid(saved_uid, saved_gid, NULL, NULL);
414 }
415 return TRUE;
416 }
418 gboolean
419 spool_delete_all(message * msg)
420 {
421 uid_t saved_uid, saved_gid;
422 gchar *spool_file;
424 /* set uid and gid to the mail ids */
425 if (!conf.run_as_user) {
426 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
427 }
429 /* header spool: */
430 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
431 if (unlink(spool_file) != 0) {
432 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
433 }
434 g_free(spool_file);
436 /* data spool: */
437 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
438 if (unlink(spool_file) != 0) {
439 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
440 }
441 g_free(spool_file);
443 /* set uid and gid back */
444 if (!conf.run_as_user) {
445 set_euidgid(saved_uid, saved_gid, NULL, NULL);
446 }
447 return TRUE;
448 }