masqmail

view src/spool.c @ 323:29de6a1c4538

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:47:27 +0200
parents 5a0e8ed56c2a
children b45dc53f2829
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 DEBUG(6) debugf("spool_read_header(): hdr start\n");
186 hdr = get_header(&(buf[3]));
187 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
188 } else if ((buf[0] == ' ' || buf[0] == '\t') && hdr) {
189 DEBUG(6) debugf("spool_read_header(): hdr continuation\n");
190 char *tmp = hdr->header;
191 /* header continuation */
192 hdr->header = g_strconcat(hdr->header, buf, NULL);
193 hdr->value = hdr->header + (hdr->value - tmp);
194 free(tmp); /* because g_strconcat() allocs and copies */
195 } else {
196 break;
197 }
198 }
199 fclose(in);
200 return TRUE;
201 }
203 message*
204 msg_spool_read(gchar * uid, gboolean do_readdata)
205 {
206 message *msg;
207 gboolean ok = FALSE;
209 msg = create_message();
210 msg->uid = g_strdup(uid);
212 DEBUG(4) debugf("msg_spool_read():\n");
213 /* header spool: */
214 ok = spool_read_header(msg);
215 DEBUG(4) debugf(" spool_read_header() returned: %d (do_readdata had been: %d)\n",
216 ok, do_readdata);
217 if (ok && do_readdata) {
218 /* data spool: */
219 ok = spool_read_data(msg);
220 DEBUG(4) debugf(" spool_read_data() returned: %d\n", ok);
221 }
222 return msg;
223 }
225 /* write header. uid and gid should already be set to the
226 mail ids. Better call spool_write(msg, FALSE).
227 */
228 static gboolean
229 spool_write_header(message * msg)
230 {
231 GList *node;
232 gchar *spool_file, *tmp_file;
233 FILE *out;
234 gboolean ok = TRUE;
236 /* header spool: */
237 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
238 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
240 if ((out = fopen(tmp_file, "w"))) {
241 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
243 fprintf(out, "%s\n", msg->uid);
244 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
246 DEBUG(6) debugf("after MF\n");
247 foreach(msg->rcpt_list, node) {
248 address *rcpt = (address *) (node->data);
249 spool_write_rcpt(out, rcpt);
250 }
251 foreach(msg->non_rcpt_list, node) {
252 address *rcpt = (address *) (node->data);
253 spool_write_rcpt(out, rcpt);
254 }
255 DEBUG(6) debugf("after RT\n");
256 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
257 if (msg->received_host != NULL)
258 fprintf(out, "RH:%s\n", msg->received_host);
260 if (msg->ident != NULL)
261 fprintf(out, "ID:%s\n", msg->ident);
263 if (msg->data_size >= 0)
264 fprintf(out, "DS: %d\n", msg->data_size);
266 if (msg->received_time > 0)
267 fprintf(out, "TR: %u\n", (int) (msg->received_time));
269 if (msg->warned_time > 0)
270 fprintf(out, "TW: %u\n", (int) (msg->warned_time));
272 DEBUG(6) debugf("after RH\n");
273 fprintf(out, "\n");
275 foreach(msg->hdr_list, node) {
276 header *hdr = (header *) (node->data);
277 fprintf(out, "HD:%s", hdr->header);
278 }
279 if (fflush(out) == EOF)
280 ok = FALSE;
281 else if (fdatasync(fileno(out)) != 0) {
282 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
283 ok = FALSE;
284 }
285 fclose(out);
286 if (ok) {
287 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
288 DEBUG(4) debugf("spool_file = %s\n", spool_file);
289 ok = (rename(tmp_file, spool_file) != -1);
290 g_free(spool_file);
291 }
292 } else {
293 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
294 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
295 ok = FALSE;
296 }
298 g_free(tmp_file);
300 return ok;
301 }
303 gboolean
304 spool_write(message * msg, gboolean do_write_data)
305 {
306 GList *list;
307 gchar *spool_file, *tmp_file;
308 FILE *out;
309 gboolean ok = TRUE;
310 uid_t saved_uid, saved_gid;
311 /* user can read/write, group can read, others cannot do anything: */
312 mode_t saved_mode = saved_mode = umask(026);
314 /* set uid and gid to the mail ids */
315 if (!conf.run_as_user) {
316 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
317 }
319 /* header spool: */
320 ok = spool_write_header(msg);
322 if (ok && do_write_data) {
323 /* data spool: */
324 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid());
325 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
327 if ((out = fopen(tmp_file, "w"))) {
328 fprintf(out, "%s\n", msg->uid);
329 for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) {
330 fprintf(out, "%s", (gchar *) (list->data));
331 }
333 /* possibly paranoid ;-) */
334 if (fflush(out) == EOF) {
335 ok = FALSE;
336 } else if (fdatasync(fileno(out)) != 0) {
337 if (errno != EINVAL) { /* some fs do not support this.. I hope this also means that it is not necessary */
338 ok = FALSE;
339 }
340 }
341 fclose(out);
342 if (ok) {
343 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
344 DEBUG(4) debugf("spool_file = %s\n", spool_file);
345 ok = (rename(tmp_file, spool_file) != -1);
346 g_free(spool_file);
347 }
348 } else {
349 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n",
350 strerror(errno));
351 ok = FALSE;
352 }
353 g_free(tmp_file);
354 }
356 /* set uid and gid back */
357 if (!conf.run_as_user) {
358 set_euidgid(saved_uid, saved_gid, NULL, NULL);
359 }
361 umask(saved_mode);
363 return ok;
364 }
366 #define MAX_LOCKAGE 300
368 gboolean
369 spool_lock(gchar * uid)
370 {
371 uid_t saved_uid, saved_gid;
372 gchar *hitch_name;
373 gchar *lock_name;
374 gboolean ok = FALSE;
376 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
377 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
379 /* set uid and gid to the mail ids */
380 if (!conf.run_as_user) {
381 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
382 }
384 ok = dot_lock(lock_name, hitch_name);
385 if (!ok)
386 logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
388 /* set uid and gid back */
389 if (!conf.run_as_user) {
390 set_euidgid(saved_uid, saved_gid, NULL, NULL);
391 }
393 g_free(lock_name);
394 g_free(hitch_name);
396 return ok;
397 }
399 gboolean
400 spool_unlock(gchar * uid)
401 {
402 uid_t saved_uid, saved_gid;
403 gchar *lock_name;
405 /* set uid and gid to the mail ids */
406 if (!conf.run_as_user) {
407 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
408 }
410 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
411 dot_unlock(lock_name);
412 g_free(lock_name);
414 /* set uid and gid back */
415 if (!conf.run_as_user) {
416 set_euidgid(saved_uid, saved_gid, NULL, NULL);
417 }
418 return TRUE;
419 }
421 gboolean
422 spool_delete_all(message * msg)
423 {
424 uid_t saved_uid, saved_gid;
425 gchar *spool_file;
427 /* set uid and gid to the mail ids */
428 if (!conf.run_as_user) {
429 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
430 }
432 /* header spool: */
433 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
434 if (unlink(spool_file) != 0) {
435 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
436 }
437 g_free(spool_file);
439 /* data spool: */
440 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
441 if (unlink(spool_file) != 0) {
442 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
443 }
444 g_free(spool_file);
446 /* set uid and gid back */
447 if (!conf.run_as_user) {
448 set_euidgid(saved_uid, saved_gid, NULL, NULL);
449 }
450 return TRUE;
451 }