masqmail

view src/spool.c @ 378:5781ba87df95

Removed ident. This had been discussed on the mailing list in Oct 2011. Ident is hardly useful in typical setups for masqmail. Probably Oliver had used it in his setup; that would make sense. Now, I know of nobody who needs it.
author markus schnalke <meillo@marmaro.de>
date Sat, 14 Jan 2012 21:36:58 +0100
parents 41958685480d
children 309935f59820
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] != '\0') {
77 if (line[4] != '|') {
78 rcpt = create_address(&(line[4]), TRUE);
79 } else {
80 rcpt = create_address_pipe(&(line[4]));
81 }
82 if (line[3] == 'X') {
83 addr_mark_delivered(rcpt);
84 } else if (line[3] == 'F') {
85 addr_mark_failed(rcpt);
86 }
87 }
88 return rcpt;
89 }
91 gboolean
92 spool_read_data(message *msg)
93 {
94 FILE *in;
95 gchar *spool_file;
97 DEBUG(5) debugf("spool_read_data entered\n");
98 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
99 DEBUG(5) debugf("reading data spool file '%s'\n", spool_file);
100 in = fopen(spool_file, "r");
101 if (!in) {
102 logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", spool_file, strerror(errno));
103 return FALSE;
104 }
106 char buf[MAX_DATALINE];
107 int len;
109 /* msg uid */
110 read_line(in, buf, MAX_DATALINE);
112 /* data */
113 msg->data_list = NULL;
114 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
115 msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf));
116 }
117 msg->data_list = g_list_reverse(msg->data_list);
118 fclose(in);
119 return TRUE;
120 }
122 gboolean
123 spool_read_header(message *msg)
124 {
125 FILE *in;
126 gchar *spool_file;
128 /* header spool: */
129 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
130 in = fopen(spool_file, "r");
131 if (!in) {
132 logwrite(LOG_ALERT, "could not open spool header file %s: %s\n",
133 spool_file, strerror(errno));
134 return FALSE;
135 }
137 header *hdr = NULL;
138 char buf[MAX_DATALINE];
139 int len;
141 /* msg uid */
142 read_line(in, buf, MAX_DATALINE);
144 /* envelope header */
145 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
146 if (buf[0] == '\n') {
147 break;
148 } else if (strncasecmp(buf, "MF:", 3) == 0) {
149 msg->return_path = create_address(&(buf[3]), TRUE);
150 DEBUG(3) debugf("spool_read: MAIL FROM: %s", msg->return_path->address);
151 } else if (strncasecmp(buf, "RT:", 3) == 0) {
152 address *addr;
153 addr = spool_scan_rcpt(buf);
154 if (addr_is_delivered(addr) || addr_is_failed(addr)) {
155 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr);
156 } else {
157 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
158 }
159 } else if (strncasecmp(buf, "PR:", 3) == 0) {
160 prot_id i;
161 for (i = 0; i < PROT_NUM; i++) {
162 if (strncasecmp(prot_names[i], &(buf[3]), strlen(prot_names[i])) == 0) {
163 break;
164 }
165 }
166 msg->received_prot = i;
167 } else if (strncasecmp(buf, "RH:", 3) == 0) {
168 g_strchomp(buf);
169 msg->received_host = g_strdup(&(buf[3]));
170 } else if (strncasecmp(buf, "ID:", 3) == 0) {
171 g_strchomp(buf);
172 msg->ident = g_strdup(&(buf[3]));
173 } else if (strncasecmp(buf, "DS:", 3) == 0) {
174 msg->data_size = atoi(&(buf[3]));
175 } else if (strncasecmp(buf, "TR:", 3) == 0) {
176 msg->received_time = (time_t) (atoi(&(buf[3])));
177 } else if (strncasecmp(buf, "TW:", 3) == 0) {
178 msg->warned_time = (time_t) (atoi(&(buf[3])));
179 }
180 /* so far ignore other tags */
181 }
183 /* mail headers */
184 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
185 if (strncasecmp(buf, "HD:", 3) == 0) {
186 DEBUG(6) debugf("spool_read_header(): hdr start\n");
187 hdr = get_header(&(buf[3]));
188 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
189 } else if ((buf[0] == ' ' || buf[0] == '\t') && hdr) {
190 DEBUG(6) debugf("spool_read_header(): hdr continuation\n");
191 char *tmp = hdr->header;
192 /* header continuation */
193 hdr->header = g_strconcat(hdr->header, buf, NULL);
194 hdr->value = hdr->header + (hdr->value - tmp);
195 free(tmp); /* because g_strconcat() allocs and copies */
196 } else {
197 break;
198 }
199 }
200 fclose(in);
201 return TRUE;
202 }
204 message*
205 msg_spool_read(gchar *uid)
206 {
207 message *msg;
208 gboolean ok = FALSE;
210 msg = create_message();
211 msg->uid = g_strdup(uid);
213 DEBUG(4) debugf("msg_spool_read():\n");
214 /* header spool: */
215 ok = spool_read_header(msg);
216 DEBUG(4) debugf(" spool_read_header() returned: %d\n", ok);
217 return msg;
218 }
220 /*
221 ** write header. uid and gid should already be set to the
222 ** mail ids. Better call spool_write(msg, FALSE).
223 */
224 static gboolean
225 spool_write_header(message *msg)
226 {
227 GList *node;
228 gchar *spool_file, *tmp_file;
229 FILE *out;
230 gboolean ok = TRUE;
232 /* header spool: */
233 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
234 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
236 if ((out = fopen(tmp_file, "w"))) {
237 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
239 fprintf(out, "%s\n", msg->uid);
240 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
242 DEBUG(6) debugf("after MF\n");
243 foreach(msg->rcpt_list, node) {
244 address *rcpt = (address *) (node->data);
245 spool_write_rcpt(out, rcpt);
246 }
247 foreach(msg->non_rcpt_list, node) {
248 address *rcpt = (address *) (node->data);
249 spool_write_rcpt(out, rcpt);
250 }
251 DEBUG(6) debugf("after RT\n");
252 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
253 if (msg->received_host != NULL)
254 fprintf(out, "RH:%s\n", msg->received_host);
256 if (msg->ident != NULL)
257 fprintf(out, "ID:%s\n", msg->ident);
259 if (msg->data_size >= 0)
260 fprintf(out, "DS: %d\n", msg->data_size);
262 if (msg->received_time > 0)
263 fprintf(out, "TR: %u\n", (int) (msg->received_time));
265 if (msg->warned_time > 0)
266 fprintf(out, "TW: %u\n", (int) (msg->warned_time));
268 DEBUG(6) debugf("after RH\n");
269 fprintf(out, "\n");
271 foreach(msg->hdr_list, node) {
272 header *hdr = (header *) (node->data);
273 fprintf(out, "HD:%s", hdr->header);
274 }
275 if (fflush(out) == EOF)
276 ok = FALSE;
277 else if (fdatasync(fileno(out)) != 0) {
278 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
279 ok = FALSE;
280 }
281 fclose(out);
282 if (ok) {
283 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
284 DEBUG(4) debugf("spool_file = %s\n", spool_file);
285 ok = (rename(tmp_file, spool_file) != -1);
286 g_free(spool_file);
287 }
288 } else {
289 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
290 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
291 ok = FALSE;
292 }
294 g_free(tmp_file);
296 return ok;
297 }
299 gboolean
300 spool_write(message *msg, gboolean do_write_data)
301 {
302 GList *list;
303 gchar *spool_file, *tmp_file;
304 FILE *out;
305 gboolean ok = TRUE;
306 uid_t saved_uid, saved_gid;
307 /* user can read/write, group can read, others cannot do anything: */
308 mode_t saved_mode = saved_mode = umask(026);
310 /* set uid and gid to the mail ids */
311 if (!conf.run_as_user) {
312 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
313 }
315 /* header spool: */
316 ok = spool_write_header(msg);
318 if (ok && do_write_data) {
319 /* data spool: */
320 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid());
321 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
323 if ((out = fopen(tmp_file, "w"))) {
324 fprintf(out, "%s\n", msg->uid);
325 for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) {
326 fprintf(out, "%s", (gchar *) (list->data));
327 }
329 /* possibly paranoid ;-) */
330 if (fflush(out) == EOF) {
331 ok = FALSE;
332 } else if (fdatasync(fileno(out)) != 0) {
333 if (errno != EINVAL) { /* some fs do not support this.. I hope this also means that it is not necessary */
334 ok = FALSE;
335 }
336 }
337 fclose(out);
338 if (ok) {
339 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
340 DEBUG(4) debugf("spool_file = %s\n", spool_file);
341 ok = (rename(tmp_file, spool_file) != -1);
342 g_free(spool_file);
343 }
344 } else {
345 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n",
346 strerror(errno));
347 ok = FALSE;
348 }
349 g_free(tmp_file);
350 }
352 /* set uid and gid back */
353 if (!conf.run_as_user) {
354 set_euidgid(saved_uid, saved_gid, NULL, NULL);
355 }
357 umask(saved_mode);
359 return ok;
360 }
362 #define MAX_LOCKAGE 300
364 gboolean
365 spool_lock(gchar *uid)
366 {
367 uid_t saved_uid, saved_gid;
368 gchar *hitch_name;
369 gchar *lock_name;
370 gboolean ok = FALSE;
372 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
373 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
375 /* set uid and gid to the mail ids */
376 if (!conf.run_as_user) {
377 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
378 }
380 ok = dot_lock(lock_name, hitch_name);
381 if (!ok)
382 logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
384 /* set uid and gid back */
385 if (!conf.run_as_user) {
386 set_euidgid(saved_uid, saved_gid, NULL, NULL);
387 }
389 g_free(lock_name);
390 g_free(hitch_name);
392 return ok;
393 }
395 gboolean
396 spool_unlock(gchar *uid)
397 {
398 uid_t saved_uid, saved_gid;
399 gchar *lock_name;
401 /* set uid and gid to the mail ids */
402 if (!conf.run_as_user) {
403 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
404 }
406 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
407 dot_unlock(lock_name);
408 g_free(lock_name);
410 /* set uid and gid back */
411 if (!conf.run_as_user) {
412 set_euidgid(saved_uid, saved_gid, NULL, NULL);
413 }
414 return TRUE;
415 }
417 gboolean
418 spool_delete_all(message *msg)
419 {
420 uid_t saved_uid, saved_gid;
421 gchar *spool_file;
423 /* set uid and gid to the mail ids */
424 if (!conf.run_as_user) {
425 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
426 }
428 /* header spool: */
429 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
430 if (unlink(spool_file) != 0) {
431 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
432 }
433 g_free(spool_file);
435 /* data spool: */
436 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
437 if (unlink(spool_file) != 0) {
438 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
439 }
440 g_free(spool_file);
442 /* set uid and gid back */
443 if (!conf.run_as_user) {
444 set_euidgid(saved_uid, saved_gid, NULL, NULL);
445 }
446 return TRUE;
447 }