masqmail-0.2

view src/spool.c @ 179:ec3fe72a3e99

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:52:17 +0200
parents 46f407c0727a
children
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()=%d, do_readdata=%d\n", ok, do_readdata);
213 if (ok && do_readdata) {
214 /* data spool: */
215 ok = spool_read_data(msg);
216 DEBUG(4) debugf(" spool_read_data()=%d\n", ok);
217 }
218 return msg;
219 }
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 }