masqmail

view src/spool.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents 309935f59820
children bdbedce60247
line source
1 /*
2 ** MasqMail
3 ** Copyright (C) 1999-2001 Oliver Kurth
4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
21 #include <sys/stat.h>
23 #include "masqmail.h"
24 #include "dotlock.h"
26 static gint
27 read_line(FILE *in, gchar *buf, gint buf_len)
28 {
29 gint p = 0;
30 gint c;
32 while ((c = getc(in)) != '\n' && (c != EOF)) {
33 if (p >= buf_len - 1) {
34 buf[buf_len-1] = '\0';
35 ungetc(c, in);
36 return buf_len;
37 }
38 buf[p++] = c;
39 }
41 if (c == EOF) {
42 return -1;
43 }
44 if ((p > 0) && (buf[p - 1] == '\r'))
45 p--;
46 buf[p++] = '\n';
47 buf[p] = '\0';
49 return p;
50 }
52 static void
53 spool_write_rcpt(FILE *out, address *rcpt)
54 {
55 gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' ');
57 if (rcpt->local_part[0] != '|') {
58 /* this is a paranoid check, in case it slipped through: */
59 /* if this happens, it is a bug */
60 if (rcpt->domain == NULL) {
61 logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", rcpt->local_part, conf.host_name);
62 logwrite(LOG_WARNING, "please report this bug.\n");
63 rcpt->domain = g_strdup(conf.host_name);
64 }
65 fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt));
66 } else {
67 fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part);
68 }
69 }
71 static address*
72 spool_scan_rcpt(gchar *line)
73 {
74 address *rcpt = NULL;
76 if (!line[3]) {
77 return NULL;
78 }
79 if (line[4] == '|') {
80 rcpt = create_address_pipe(line+4);
81 } else {
82 rcpt = create_address(line+4, TRUE);
83 }
84 if (line[3] == 'X') {
85 addr_mark_delivered(rcpt);
86 } else if (line[3] == 'F') {
87 addr_mark_failed(rcpt);
88 }
89 return rcpt;
90 }
92 gboolean
93 spool_read_data(message *msg)
94 {
95 FILE *in;
96 gchar *spool_file;
98 DEBUG(5) debugf("spool_read_data entered\n");
99 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
100 DEBUG(5) debugf("reading data spool file '%s'\n", spool_file);
101 in = fopen(spool_file, "r");
102 if (!in) {
103 logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", spool_file, strerror(errno));
104 return FALSE;
105 }
107 char buf[MAX_DATALINE];
108 int len;
110 /* msg uid */
111 read_line(in, buf, MAX_DATALINE);
113 /* data */
114 msg->data_list = NULL;
115 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
116 msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf));
117 }
118 msg->data_list = g_list_reverse(msg->data_list);
119 fclose(in);
120 return TRUE;
121 }
123 gboolean
124 spool_read_header(message *msg)
125 {
126 FILE *in;
127 gchar *spool_file;
129 /* header spool: */
130 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
131 in = fopen(spool_file, "r");
132 if (!in) {
133 logwrite(LOG_ALERT, "could not open spool header file %s: %s\n",
134 spool_file, strerror(errno));
135 return FALSE;
136 }
138 header *hdr = NULL;
139 char buf[MAX_DATALINE];
140 int len;
142 /* msg uid */
143 read_line(in, buf, MAX_DATALINE);
145 /* envelope header */
146 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
147 if (buf[0] == '\n') {
148 break;
149 } else if (strncasecmp(buf, "MF:", 3) == 0) {
150 msg->return_path = create_address(&(buf[3]), TRUE);
151 DEBUG(3) debugf("spool_read: MAIL FROM: %s\n",
152 msg->return_path->address);
153 } else if (strncasecmp(buf, "RT:", 3) == 0) {
154 address *addr;
155 addr = spool_scan_rcpt(buf);
156 if (addr_is_delivered(addr) || addr_is_failed(addr)) {
157 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr);
158 } else {
159 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
160 }
161 } else if (strncasecmp(buf, "PR:", 3) == 0) {
162 prot_id i;
163 for (i = 0; i < PROT_NUM; i++) {
164 if (strncasecmp(prot_names[i], &(buf[3]), strlen(prot_names[i])) == 0) {
165 break;
166 }
167 }
168 msg->received_prot = i;
169 } else if (strncasecmp(buf, "RH:", 3) == 0) {
170 g_strchomp(buf);
171 msg->received_host = g_strdup(&(buf[3]));
172 } else if (strncasecmp(buf, "ID:", 3) == 0) {
173 g_strchomp(buf);
174 msg->ident = g_strdup(&(buf[3]));
175 } else if (strncasecmp(buf, "DS:", 3) == 0) {
176 msg->data_size = atoi(&(buf[3]));
177 } else if (strncasecmp(buf, "TR:", 3) == 0) {
178 msg->received_time = (time_t) (atoi(&(buf[3])));
179 } else if (strncasecmp(buf, "TW:", 3) == 0) {
180 msg->warned_time = (time_t) (atoi(&(buf[3])));
181 }
182 /* so far ignore other tags */
183 }
185 /* mail headers */
186 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
187 if (strncasecmp(buf, "HD:", 3) == 0) {
188 DEBUG(6) debugf("spool_read_header(): hdr start\n");
189 hdr = get_header(&(buf[3]));
190 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
191 } else if ((buf[0] == ' ' || buf[0] == '\t') && hdr) {
192 DEBUG(6) debugf("spool_read_header(): hdr continuation\n");
193 char *tmp = hdr->header;
194 /* header continuation */
195 hdr->header = g_strconcat(hdr->header, buf, NULL);
196 hdr->value = hdr->header + (hdr->value - tmp);
197 free(tmp); /* because g_strconcat() allocs and copies */
198 } else {
199 break;
200 }
201 }
202 fclose(in);
203 return TRUE;
204 }
206 message*
207 msg_spool_read(gchar *uid)
208 {
209 message *msg;
210 gboolean ok = FALSE;
212 msg = create_message();
213 msg->uid = g_strdup(uid);
215 DEBUG(4) debugf("msg_spool_read():\n");
216 /* header spool: */
217 ok = spool_read_header(msg);
218 DEBUG(4) debugf("spool_read_header() returned: %d\n", ok);
219 return msg;
220 }
222 /*
223 ** write header. uid and gid should already be set to the
224 ** mail ids. Better call spool_write(msg, FALSE).
225 */
226 static gboolean
227 spool_write_header(message *msg)
228 {
229 GList *node;
230 gchar *spool_file, *tmp_file;
231 FILE *out;
232 gboolean ok = TRUE;
234 /* header spool: */
235 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
236 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
238 if ((out = fopen(tmp_file, "w"))) {
239 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
241 fprintf(out, "%s\n", msg->uid);
242 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
244 DEBUG(6) debugf("after MF\n");
245 foreach(msg->rcpt_list, node) {
246 address *rcpt = (address *) (node->data);
247 spool_write_rcpt(out, rcpt);
248 }
249 foreach(msg->non_rcpt_list, node) {
250 address *rcpt = (address *) (node->data);
251 spool_write_rcpt(out, rcpt);
252 }
253 DEBUG(6) debugf("after RT\n");
254 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
255 if (msg->received_host != NULL)
256 fprintf(out, "RH:%s\n", msg->received_host);
258 if (msg->ident != NULL)
259 fprintf(out, "ID:%s\n", msg->ident);
261 if (msg->data_size >= 0)
262 fprintf(out, "DS: %d\n", msg->data_size);
264 if (msg->received_time > 0)
265 fprintf(out, "TR: %u\n", (int) (msg->received_time));
267 if (msg->warned_time > 0)
268 fprintf(out, "TW: %u\n", (int) (msg->warned_time));
270 DEBUG(6) debugf("after RH\n");
271 fprintf(out, "\n");
273 foreach(msg->hdr_list, node) {
274 header *hdr = (header *) (node->data);
275 fprintf(out, "HD:%s", hdr->header);
276 }
277 if (fflush(out) == EOF)
278 ok = FALSE;
279 else if (fdatasync(fileno(out)) != 0) {
280 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
281 ok = FALSE;
282 }
283 fclose(out);
284 if (ok) {
285 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
286 DEBUG(4) debugf("spool_file = %s\n", spool_file);
287 ok = (rename(tmp_file, spool_file) != -1);
288 g_free(spool_file);
289 }
290 } else {
291 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
292 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
293 ok = FALSE;
294 }
296 g_free(tmp_file);
298 return ok;
299 }
301 gboolean
302 spool_write(message *msg, gboolean do_write_data)
303 {
304 GList *list;
305 gchar *spool_file, *tmp_file;
306 FILE *out;
307 gboolean ok = TRUE;
308 uid_t saved_uid, saved_gid;
309 /* user can read/write, group can read, others cannot do anything: */
310 mode_t saved_mode = saved_mode = umask(026);
312 /* set uid and gid to the mail ids */
313 if (!conf.run_as_user) {
314 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
315 }
317 /* header spool: */
318 ok = spool_write_header(msg);
320 if (ok && do_write_data) {
321 /* data spool: */
322 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid());
323 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
325 if ((out = fopen(tmp_file, "w"))) {
326 fprintf(out, "%s\n", msg->uid);
327 for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) {
328 fprintf(out, "%s", (gchar *) (list->data));
329 }
331 /* possibly paranoid ;-) */
332 if (fflush(out) == EOF) {
333 ok = FALSE;
334 } else if (fdatasync(fileno(out)) != 0) {
335 if (errno != EINVAL) { /* some fs do not support this.. I hope this also means that it is not necessary */
336 ok = FALSE;
337 }
338 }
339 fclose(out);
340 if (ok) {
341 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
342 DEBUG(4) debugf("spool_file = %s\n", spool_file);
343 ok = (rename(tmp_file, spool_file) != -1);
344 g_free(spool_file);
345 }
346 } else {
347 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n",
348 strerror(errno));
349 ok = FALSE;
350 }
351 g_free(tmp_file);
352 }
354 /* set uid and gid back */
355 if (!conf.run_as_user) {
356 set_euidgid(saved_uid, saved_gid, NULL, NULL);
357 }
359 umask(saved_mode);
361 return ok;
362 }
364 #define MAX_LOCKAGE 300
366 gboolean
367 spool_lock(gchar *uid)
368 {
369 uid_t saved_uid, saved_gid;
370 gchar *hitch_name;
371 gchar *lock_name;
372 gboolean ok = FALSE;
374 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
375 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
377 /* set uid and gid to the mail ids */
378 if (!conf.run_as_user) {
379 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
380 }
382 ok = dot_lock(lock_name, hitch_name);
383 if (!ok)
384 logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
386 /* set uid and gid back */
387 if (!conf.run_as_user) {
388 set_euidgid(saved_uid, saved_gid, NULL, NULL);
389 }
391 g_free(lock_name);
392 g_free(hitch_name);
394 return ok;
395 }
397 gboolean
398 spool_unlock(gchar *uid)
399 {
400 uid_t saved_uid, saved_gid;
401 gchar *lock_name;
403 /* set uid and gid to the mail ids */
404 if (!conf.run_as_user) {
405 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
406 }
408 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
409 dot_unlock(lock_name);
410 g_free(lock_name);
412 /* set uid and gid back */
413 if (!conf.run_as_user) {
414 set_euidgid(saved_uid, saved_gid, NULL, NULL);
415 }
416 return TRUE;
417 }
419 gboolean
420 spool_delete_all(message *msg)
421 {
422 uid_t saved_uid, saved_gid;
423 gchar *spool_file;
425 /* set uid and gid to the mail ids */
426 if (!conf.run_as_user) {
427 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
428 }
430 /* header spool: */
431 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
432 if (unlink(spool_file) != 0) {
433 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
434 }
435 g_free(spool_file);
437 /* data spool: */
438 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
439 if (unlink(spool_file) != 0) {
440 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
441 }
442 g_free(spool_file);
444 /* set uid and gid back */
445 if (!conf.run_as_user) {
446 set_euidgid(saved_uid, saved_gid, NULL, NULL);
447 }
448 return TRUE;
449 }