masqmail

view src/spool.c @ 75:257a9e6d1a8e

fixed correct processing of mails with data lines longer 4096 chars Mail messages with lines longer than 4096 chars were already read correctly, i.e. the spool files were correct. This commit fixes the reading of spool files with long lines. The old behavior was that the message body was truncated right before the first line longer 4096 chars. The number comes from MAX_DATALINE.
author meillo@marmaro.de
date Wed, 16 Jun 2010 19:06:34 +0200
parents 90644c204265
children 3b344bf57162
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
19 #include <sys/stat.h>
21 #include "masqmail.h"
22 #include "dotlock.h"
24 static gint
25 read_line(FILE * in, gchar * buf, gint buf_len)
26 {
27 gint p = 0;
28 gint c;
30 while ((c = getc(in)) != '\n' && (c != EOF)) {
31 if (p >= buf_len - 1) {
32 buf[buf_len-1] = '\0';
33 ungetc(c, in);
34 return buf_len;
35 }
36 buf[p++] = c;
37 }
39 if (c == EOF) {
40 return -1;
41 }
42 if ((p > 0) && (buf[p - 1] == '\r'))
43 p--;
44 buf[p++] = '\n';
45 buf[p] = '\0';
47 return p;
48 }
50 static void
51 spool_write_rcpt(FILE * out, address * rcpt)
52 {
53 gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' ');
55 if (rcpt->local_part[0] != '|') {
56 /* this is a paranoid check, in case it slipped through: */
57 /* if this happens, it is a bug */
58 if (rcpt->domain == NULL) {
59 logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", rcpt->local_part, conf.host_name);
60 logwrite(LOG_WARNING, "please report this bug.\n");
61 rcpt->domain = g_strdup(conf.host_name);
62 }
63 fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt));
64 } else {
65 fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part);
66 }
67 }
69 static address*
70 spool_scan_rcpt(gchar * line)
71 {
72 address *rcpt = NULL;
74 if (line[3] != '\0') {
75 if (line[4] != '|') {
76 rcpt = create_address(&(line[4]), TRUE);
77 } else {
78 rcpt = create_address_pipe(&(line[4]));
79 }
80 if (line[3] == 'X') {
81 addr_mark_delivered(rcpt);
82 } else if (line[3] == 'F') {
83 addr_mark_failed(rcpt);
84 }
85 }
86 return rcpt;
87 }
89 gboolean
90 spool_read_data(message * msg)
91 {
92 FILE *in;
93 gboolean ok = FALSE;
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 if ((in = fopen(spool_file, "r"))) {
100 char buf[MAX_DATALINE];
101 int len;
103 /* msg uid */
104 read_line(in, buf, MAX_DATALINE);
106 /* data */
107 msg->data_list = NULL;
108 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
109 msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf));
110 }
111 msg->data_list = g_list_reverse(msg->data_list);
112 fclose(in);
113 ok = TRUE;
114 } else
115 logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", spool_file, strerror(errno));
116 return ok;
117 }
119 gboolean
120 spool_read_header(message * msg)
121 {
122 FILE *in;
123 gboolean ok = FALSE;
124 gchar *spool_file;
126 /* header spool: */
127 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
128 if ((in = fopen(spool_file, "r"))) {
129 header *hdr = NULL;
130 char buf[MAX_DATALINE];
131 int len;
133 /* msg uid */
134 read_line(in, buf, MAX_DATALINE);
136 /* envelope header */
137 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
138 if (buf[0] == '\n')
139 break;
140 else if (strncasecmp(buf, "MF:", 3) == 0) {
141 msg->return_path = create_address(&(buf[3]), TRUE);
142 DEBUG(3) debugf("spool_read: MAIL FROM: %s", msg->return_path->address);
143 } else if (strncasecmp(buf, "RT:", 3) == 0) {
144 address *addr;
145 addr = spool_scan_rcpt(buf);
146 if (!addr_is_delivered(addr) && !addr_is_failed(addr)) {
147 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
148 } else {
149 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr);
150 }
151 } else if (strncasecmp(buf, "PR:", 3) == 0) {
152 prot_id i;
153 for (i = 0; i < PROT_NUM; i++) {
154 if (strncasecmp(prot_names[i], &(buf[3]), strlen(prot_names[i])) == 0) {
155 break;
156 }
157 }
158 msg->received_prot = i;
159 } else if (strncasecmp(buf, "RH:", 3) == 0) {
160 g_strchomp(buf);
161 msg->received_host = g_strdup(&(buf[3]));
162 } else if (strncasecmp(buf, "ID:", 3) == 0) {
163 g_strchomp(buf);
164 msg->ident = g_strdup(&(buf[3]));
165 } else if (strncasecmp(buf, "DS:", 3) == 0) {
166 msg->data_size = atoi(&(buf[3]));
167 } else if (strncasecmp(buf, "TR:", 3) == 0) {
168 msg->received_time = (time_t) (atoi(&(buf[3])));
169 } else if (strncasecmp(buf, "TW:", 3) == 0) {
170 msg->warned_time = (time_t) (atoi(&(buf[3])));
171 }
172 /* so far ignore other tags */
173 }
175 /* mail headers */
176 while ((len = read_line(in, buf, MAX_DATALINE)) > 0) {
177 if (strncasecmp(buf, "HD:", 3) == 0) {
178 hdr = get_header(&(buf[3]));
179 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
180 } else if ((buf[0] == ' ' || buf[0] == '\t') && hdr) {
181 char *tmp = hdr->header;
182 /* header continuation */
183 hdr->header = g_strconcat(hdr->header, buf, NULL);
184 hdr->value = hdr->header + (hdr->value - tmp);
185 } else
186 break;
187 }
188 fclose(in);
189 ok = TRUE;
190 } else
191 logwrite(LOG_ALERT, "could not open spool header file %s: %s\n", spool_file, strerror(errno));
192 return ok;
193 }
195 message*
196 msg_spool_read(gchar * uid, gboolean do_readdata)
197 {
198 message *msg;
199 gboolean ok = FALSE;
201 msg = create_message();
202 msg->uid = g_strdup(uid);
204 /* header spool: */
205 ok = spool_read_header(msg);
206 if (ok && do_readdata) {
207 /* data spool: */
208 ok = spool_read_data(msg);
209 }
210 return msg;
211 }
213 /* write header. uid and gid should already be set to the
214 mail ids. Better call spool_write(msg, FALSE).
215 */
216 static gboolean
217 spool_write_header(message * msg)
218 {
219 GList *node;
220 gchar *spool_file, *tmp_file;
221 FILE *out;
222 gboolean ok = TRUE;
224 /* header spool: */
225 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
226 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
228 if ((out = fopen(tmp_file, "w"))) {
229 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
231 fprintf(out, "%s\n", msg->uid);
232 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
234 DEBUG(6) debugf("after MF\n");
235 foreach(msg->rcpt_list, node) {
236 address *rcpt = (address *) (node->data);
237 spool_write_rcpt(out, rcpt);
238 }
239 foreach(msg->non_rcpt_list, node) {
240 address *rcpt = (address *) (node->data);
241 spool_write_rcpt(out, rcpt);
242 }
243 DEBUG(6) debugf("after RT\n");
244 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
245 if (msg->received_host != NULL)
246 fprintf(out, "RH:%s\n", msg->received_host);
248 if (msg->ident != NULL)
249 fprintf(out, "ID:%s\n", msg->ident);
251 if (msg->data_size >= 0)
252 fprintf(out, "DS: %d\n", msg->data_size);
254 if (msg->received_time > 0)
255 fprintf(out, "TR: %u\n", (int) (msg->received_time));
257 if (msg->warned_time > 0)
258 fprintf(out, "TW: %u\n", (int) (msg->warned_time));
260 DEBUG(6) debugf("after RH\n");
261 fprintf(out, "\n");
263 foreach(msg->hdr_list, node) {
264 header *hdr = (header *) (node->data);
265 fprintf(out, "HD:%s", hdr->header);
266 }
267 if (fflush(out) == EOF)
268 ok = FALSE;
269 else if (fdatasync(fileno(out)) != 0) {
270 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
271 ok = FALSE;
272 }
273 fclose(out);
274 if (ok) {
275 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
276 DEBUG(4) debugf("spool_file = %s\n", spool_file);
277 ok = (rename(tmp_file, spool_file) != -1);
278 g_free(spool_file);
279 }
280 } else {
281 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
282 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
283 ok = FALSE;
284 }
286 g_free(tmp_file);
288 return ok;
289 }
291 gboolean
292 spool_write(message * msg, gboolean do_write_data)
293 {
294 GList *list;
295 gchar *spool_file, *tmp_file;
296 FILE *out;
297 gboolean ok = TRUE;
298 uid_t saved_uid, saved_gid;
299 /* user can read/write, group can read, others cannot do anything: */
300 mode_t saved_mode = saved_mode = umask(026);
302 /* set uid and gid to the mail ids */
303 if (!conf.run_as_user) {
304 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
305 }
307 /* header spool: */
308 ok = spool_write_header(msg);
310 if (ok) {
312 if (do_write_data) {
313 /* data spool: */
314 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid());
315 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
317 if ((out = fopen(tmp_file, "w"))) {
318 fprintf(out, "%s\n", msg->uid);
319 for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) {
320 fprintf(out, "%s", (gchar *) (list->data));
321 }
323 /* possibly paranoid ;-) */
324 if (fflush(out) == EOF)
325 ok = FALSE;
326 else if (fdatasync(fileno(out)) != 0) {
327 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
328 ok = FALSE;
329 }
330 fclose(out);
331 if (ok) {
332 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
333 DEBUG(4) debugf("spool_file = %s\n", spool_file);
334 ok = (rename(tmp_file, spool_file) != -1);
335 g_free(spool_file);
336 }
337 } else {
338 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n", strerror(errno));
339 ok = FALSE;
340 }
341 g_free(tmp_file);
342 }
343 }
345 /* set uid and gid back */
346 if (!conf.run_as_user) {
347 set_euidgid(saved_uid, saved_gid, NULL, NULL);
348 }
350 umask(saved_mode);
352 return ok;
353 }
355 #define MAX_LOCKAGE 300
357 gboolean
358 spool_lock(gchar * uid)
359 {
360 uid_t saved_uid, saved_gid;
361 gchar *hitch_name;
362 gchar *lock_name;
363 gboolean ok = FALSE;
365 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
366 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
368 /* set uid and gid to the mail ids */
369 if (!conf.run_as_user) {
370 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
371 }
373 ok = dot_lock(lock_name, hitch_name);
374 if (!ok)
375 logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
377 /* set uid and gid back */
378 if (!conf.run_as_user) {
379 set_euidgid(saved_uid, saved_gid, NULL, NULL);
380 }
382 g_free(lock_name);
383 g_free(hitch_name);
385 return ok;
386 }
388 gboolean
389 spool_unlock(gchar * uid)
390 {
391 uid_t saved_uid, saved_gid;
392 gchar *lock_name;
394 /* set uid and gid to the mail ids */
395 if (!conf.run_as_user) {
396 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
397 }
399 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
400 dot_unlock(lock_name);
401 g_free(lock_name);
403 /* set uid and gid back */
404 if (!conf.run_as_user) {
405 set_euidgid(saved_uid, saved_gid, NULL, NULL);
406 }
407 return TRUE;
408 }
410 gboolean
411 spool_delete_all(message * msg)
412 {
413 uid_t saved_uid, saved_gid;
414 gchar *spool_file;
416 /* set uid and gid to the mail ids */
417 if (!conf.run_as_user) {
418 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
419 }
421 /* header spool: */
422 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
423 if (unlink(spool_file) != 0)
424 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
425 g_free(spool_file);
427 /* data spool: */
428 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
429 if (unlink(spool_file) != 0)
430 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
431 g_free(spool_file);
433 /* set uid and gid back */
434 if (!conf.run_as_user) {
435 set_euidgid(saved_uid, saved_gid, NULL, NULL);
436 }
437 return TRUE;
438 }