masqmail

view src/spool.c @ 433:81c438e1891b

Marks version number as development.
author markus schnalke <meillo@marmaro.de>
date Sat, 07 Feb 2015 11:40:21 +0100
parents 0430194f7ef8
children
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/%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/%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/%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/%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/%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/%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/%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/%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 }