Mercurial > masqmail-0.2
comparison src/spool.c @ 0:08114f7dcc23 0.2.21
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:08114f7dcc23 |
---|---|
1 /* MasqMail | |
2 Copyright (C) 1999-2001 Oliver Kurth | |
3 | |
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. | |
8 | |
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. | |
13 | |
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 */ | |
18 | |
19 #include "masqmail.h" | |
20 #include <sys/stat.h> | |
21 #include "dotlock.h" | |
22 | |
23 static | |
24 gint read_line(FILE *in, gchar *buf, gint buf_len) | |
25 { | |
26 gint p = 0; | |
27 gint c; | |
28 | |
29 while((c = getc(in)) != '\n' && (c != EOF)){ | |
30 if(p >= buf_len-1) { return 0; } | |
31 buf[p++] = c; | |
32 } | |
33 | |
34 if(c == EOF){ | |
35 return -1; | |
36 } | |
37 if((p > 0) && (buf[p-1] == '\r')) | |
38 p--; | |
39 buf[p++] = '\n'; | |
40 buf[p] = 0; | |
41 | |
42 return p; | |
43 } | |
44 | |
45 static | |
46 void spool_write_rcpt(FILE *out, address *rcpt) | |
47 { | |
48 gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' '); | |
49 | |
50 if(rcpt->local_part[0] != '|'){ | |
51 /* this is a paranoid check, in case it slipped through: */ | |
52 /* if this happens, it is a bug */ | |
53 if(rcpt->domain == NULL){ | |
54 logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", | |
55 rcpt->local_part, conf.host_name); | |
56 logwrite(LOG_WARNING, "please report this bug.\n"); | |
57 rcpt->domain = g_strdup(conf.host_name); | |
58 } | |
59 fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt)); | |
60 }else{ | |
61 fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part); | |
62 } | |
63 } | |
64 | |
65 static | |
66 address *spool_scan_rcpt(gchar *line) | |
67 { | |
68 address *rcpt = NULL; | |
69 | |
70 if(line[3] != 0){ | |
71 if(line[4] != '|'){ | |
72 rcpt = create_address(&(line[4]), TRUE); | |
73 }else{ | |
74 rcpt = create_address_pipe(&(line[4])); | |
75 } | |
76 if(line[3] == 'X'){ | |
77 addr_mark_delivered(rcpt); | |
78 }else if(line[3] == 'F'){ | |
79 addr_mark_failed(rcpt); | |
80 } | |
81 } | |
82 return rcpt; | |
83 } | |
84 | |
85 gboolean spool_read_data(message *msg) | |
86 { | |
87 FILE *in; | |
88 gboolean ok = FALSE; | |
89 gchar *spool_file; | |
90 | |
91 DEBUG(5) debugf("spool_read_data entered\n"); | |
92 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); | |
93 DEBUG(5) debugf("reading data spool file '%s'\n", spool_file); | |
94 if((in = fopen(spool_file, "r"))){ | |
95 char buf[MAX_DATALINE]; | |
96 int len; | |
97 | |
98 /* msg uid */ | |
99 read_line(in, buf, MAX_DATALINE); | |
100 | |
101 /* data */ | |
102 msg->data_list = NULL; | |
103 while((len = read_line(in, buf, MAX_DATALINE)) > 0){ | |
104 msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf)); | |
105 } | |
106 msg->data_list = g_list_reverse(msg->data_list); | |
107 fclose(in); | |
108 ok = TRUE; | |
109 }else | |
110 logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", | |
111 spool_file, strerror(errno)); | |
112 return ok; | |
113 } | |
114 | |
115 gboolean spool_read_header(message *msg) | |
116 { | |
117 FILE *in; | |
118 gboolean ok = FALSE; | |
119 gchar *spool_file; | |
120 | |
121 /* header spool: */ | |
122 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); | |
123 if((in = fopen(spool_file, "r"))){ | |
124 header *hdr = NULL; | |
125 char buf[MAX_DATALINE]; | |
126 int len; | |
127 | |
128 /* msg uid */ | |
129 read_line(in, buf, MAX_DATALINE); | |
130 | |
131 /* envelope header */ | |
132 while((len = read_line(in, buf, MAX_DATALINE)) > 0){ | |
133 if(buf[0] == '\n') | |
134 break; | |
135 else if(strncasecmp(buf, "MF:", 3) == 0){ | |
136 msg->return_path = create_address(&(buf[3]), TRUE); | |
137 DEBUG(3) debugf("spool_read: MAIL FROM: %s", | |
138 msg->return_path->address); | |
139 }else if(strncasecmp(buf, "RT:", 3) == 0){ | |
140 address *addr; | |
141 addr = spool_scan_rcpt(buf); | |
142 if(!addr_is_delivered(addr) && !addr_is_failed(addr)){ | |
143 msg->rcpt_list = g_list_append(msg->rcpt_list, addr); | |
144 }else{ | |
145 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr); | |
146 } | |
147 }else if(strncasecmp(buf, "PR:", 3) == 0){ | |
148 prot_id i; | |
149 for(i = 0; i < PROT_NUM; i++){ | |
150 if(strncasecmp(prot_names[i], &(buf[3]), | |
151 strlen(prot_names[i])) == 0){ | |
152 break; | |
153 } | |
154 } | |
155 msg->received_prot = i; | |
156 }else if(strncasecmp(buf, "RH:", 3) == 0){ | |
157 g_strchomp(buf); | |
158 msg->received_host = g_strdup(&(buf[3])); | |
159 }else if(strncasecmp(buf, "ID:", 3) == 0){ | |
160 g_strchomp(buf); | |
161 msg->ident = g_strdup(&(buf[3])); | |
162 }else if(strncasecmp(buf, "DS:", 3) == 0){ | |
163 msg->data_size = atoi(&(buf[3])); | |
164 }else if(strncasecmp(buf, "TR:", 3) == 0){ | |
165 msg->received_time = (time_t)(atoi(&(buf[3]))); | |
166 }else if(strncasecmp(buf, "TW:", 3) == 0){ | |
167 msg->warned_time = (time_t)(atoi(&(buf[3]))); | |
168 } | |
169 /* so far ignore other tags */ | |
170 } | |
171 | |
172 /* mail headers */ | |
173 while((len = read_line(in, buf, MAX_DATALINE)) > 0){ | |
174 if(strncasecmp(buf, "HD:", 3) == 0){ | |
175 hdr = get_header(&(buf[3])); | |
176 msg->hdr_list = g_list_append(msg->hdr_list, hdr); | |
177 }else if((buf[0] == ' ' || buf[0] == '\t') && hdr){ | |
178 char *tmp = hdr->header; | |
179 /* header continuation */ | |
180 hdr->header = g_strconcat(hdr->header, buf, NULL); | |
181 hdr->value = hdr->header + (hdr->value - tmp); | |
182 }else | |
183 break; | |
184 } | |
185 fclose(in); | |
186 ok = TRUE; | |
187 }else | |
188 logwrite(LOG_ALERT, "could not open spool header file %s: %s\n", | |
189 spool_file, strerror(errno)); | |
190 return ok; | |
191 } | |
192 | |
193 message *msg_spool_read(gchar *uid, gboolean do_readdata) | |
194 { | |
195 message *msg; | |
196 gboolean ok = FALSE; | |
197 | |
198 msg = create_message(); | |
199 msg->uid = g_strdup(uid); | |
200 | |
201 /* header spool: */ | |
202 ok = spool_read_header(msg); | |
203 if(ok && do_readdata){ | |
204 /* data spool: */ | |
205 ok = spool_read_data(msg); | |
206 } | |
207 return msg; | |
208 } | |
209 | |
210 /* write header. uid and gid should already be set to the | |
211 mail ids. Better call spool_write(msg, FALSE). | |
212 */ | |
213 static | |
214 gboolean spool_write_header(message *msg) | |
215 { | |
216 GList *node; | |
217 gchar *spool_file, *tmp_file; | |
218 FILE *out; | |
219 gboolean ok = TRUE; | |
220 | |
221 /* header spool: */ | |
222 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid()); | |
223 DEBUG(4) debugf("tmp_file = %s\n", tmp_file); | |
224 | |
225 if((out = fopen(tmp_file, "w"))){ | |
226 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file); | |
227 | |
228 fprintf(out, "%s\n", msg->uid); | |
229 fprintf(out, "MF:%s\n", addr_string(msg->return_path)); | |
230 | |
231 DEBUG(6) debugf("after MF\n"); | |
232 foreach(msg->rcpt_list, node){ | |
233 address *rcpt = (address *)(node->data); | |
234 spool_write_rcpt(out, rcpt); | |
235 } | |
236 foreach(msg->non_rcpt_list, node){ | |
237 address *rcpt = (address *)(node->data); | |
238 spool_write_rcpt(out, rcpt); | |
239 } | |
240 DEBUG(6) debugf("after RT\n"); | |
241 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]); | |
242 if(msg->received_host != NULL) | |
243 fprintf(out, "RH:%s\n", msg->received_host); | |
244 | |
245 if(msg->ident != NULL) | |
246 fprintf(out, "ID:%s\n", msg->ident); | |
247 | |
248 if(msg->data_size >= 0) | |
249 fprintf(out, "DS: %d\n", msg->data_size); | |
250 | |
251 if(msg->received_time > 0) | |
252 fprintf(out, "TR: %u\n", (int)(msg->received_time)); | |
253 | |
254 if(msg->warned_time > 0) | |
255 fprintf(out, "TW: %u\n", (int)(msg->warned_time)); | |
256 | |
257 DEBUG(6) debugf("after RH\n"); | |
258 fprintf(out, "\n"); | |
259 | |
260 foreach(msg->hdr_list, node){ | |
261 header *hdr = (header *)(node->data); | |
262 fprintf(out, "HD:%s", hdr->header); | |
263 } | |
264 if(fflush(out) == EOF) ok = FALSE; | |
265 else if(fdatasync(fileno(out)) != 0){ | |
266 if(errno != EINVAL) /* some fs do not support this.. | |
267 I hope this also means that it is not necessary */ | |
268 ok = FALSE; | |
269 } | |
270 fclose(out); | |
271 if(ok){ | |
272 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); | |
273 DEBUG(4) debugf("spool_file = %s\n", spool_file); | |
274 ok = (rename(tmp_file, spool_file) != -1); | |
275 g_free(spool_file); | |
276 } | |
277 }else{ | |
278 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno)); | |
279 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid()); | |
280 ok = FALSE; | |
281 } | |
282 | |
283 g_free(tmp_file); | |
284 | |
285 return ok; | |
286 } | |
287 | |
288 gboolean spool_write(message *msg, gboolean do_write_data) | |
289 { | |
290 GList *list; | |
291 gchar *spool_file, *tmp_file; | |
292 FILE *out; | |
293 gboolean ok = TRUE; | |
294 uid_t saved_uid, saved_gid; | |
295 /* user can read/write, group can read, others cannot do anything: */ | |
296 mode_t saved_mode = saved_mode = umask(026); | |
297 | |
298 /* set uid and gid to the mail ids */ | |
299 if(!conf.run_as_user){ | |
300 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); | |
301 } | |
302 | |
303 /* header spool: */ | |
304 ok = spool_write_header(msg); | |
305 | |
306 if(ok){ | |
307 | |
308 if(do_write_data){ | |
309 /* data spool: */ | |
310 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", | |
311 conf.spool_dir, getpid()); | |
312 DEBUG(4) debugf("tmp_file = %s\n", tmp_file); | |
313 | |
314 if((out = fopen(tmp_file, "w"))){ | |
315 fprintf(out, "%s\n", msg->uid); | |
316 for(list = g_list_first(msg->data_list); | |
317 list != NULL; | |
318 list = g_list_next(list)){ | |
319 fprintf(out, "%s", (gchar *)(list->data)); | |
320 } | |
321 | |
322 /* possibly paranoid ;-) */ | |
323 if(fflush(out) == EOF) ok = FALSE; | |
324 else if(fdatasync(fileno(out)) != 0){ | |
325 if(errno != EINVAL) /* some fs do not support this.. | |
326 I hope this also means that it is not necessary */ | |
327 ok = FALSE; | |
328 } | |
329 fclose(out); | |
330 if(ok){ | |
331 spool_file = g_strdup_printf("%s/input/%s-D", | |
332 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", | |
339 strerror(errno)); | |
340 ok = FALSE; | |
341 } | |
342 g_free(tmp_file); | |
343 } | |
344 } | |
345 | |
346 /* set uid and gid back */ | |
347 if(!conf.run_as_user){ | |
348 set_euidgid(saved_uid, saved_gid, NULL, NULL); | |
349 } | |
350 | |
351 umask(saved_mode); | |
352 | |
353 return ok; | |
354 } | |
355 | |
356 #define MAX_LOCKAGE 300 | |
357 | |
358 gboolean spool_lock(gchar *uid) | |
359 { | |
360 uid_t saved_uid, saved_gid; | |
361 gchar *hitch_name; | |
362 gchar *lock_name; | |
363 gboolean ok = FALSE; | |
364 | |
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); | |
367 | |
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 } | |
372 | |
373 ok = dot_lock(lock_name, hitch_name); | |
374 if(!ok) logwrite(LOG_WARNING, "spool file %s is locked\n", uid); | |
375 | |
376 /* set uid and gid back */ | |
377 if(!conf.run_as_user){ | |
378 set_euidgid(saved_uid, saved_gid, NULL, NULL); | |
379 } | |
380 | |
381 g_free(lock_name); | |
382 g_free(hitch_name); | |
383 | |
384 return ok; | |
385 } | |
386 | |
387 gboolean spool_unlock(gchar *uid) | |
388 { | |
389 uid_t saved_uid, saved_gid; | |
390 gchar *lock_name; | |
391 | |
392 /* set uid and gid to the mail ids */ | |
393 if(!conf.run_as_user){ | |
394 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); | |
395 } | |
396 | |
397 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); | |
398 dot_unlock(lock_name); | |
399 g_free(lock_name); | |
400 | |
401 /* set uid and gid back */ | |
402 if(!conf.run_as_user){ | |
403 set_euidgid(saved_uid, saved_gid, NULL, NULL); | |
404 } | |
405 return TRUE; | |
406 } | |
407 | |
408 gboolean spool_delete_all(message *msg) | |
409 { | |
410 uid_t saved_uid, saved_gid; | |
411 gchar *spool_file; | |
412 | |
413 /* set uid and gid to the mail ids */ | |
414 if(!conf.run_as_user){ | |
415 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); | |
416 } | |
417 | |
418 /* header spool: */ | |
419 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); | |
420 if(unlink(spool_file) != 0) | |
421 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", | |
422 spool_file, strerror(errno)); | |
423 g_free(spool_file); | |
424 | |
425 /* data spool: */ | |
426 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); | |
427 if(unlink(spool_file) != 0) | |
428 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", | |
429 spool_file, strerror(errno)); | |
430 g_free(spool_file); | |
431 | |
432 /* set uid and gid back */ | |
433 if(!conf.run_as_user){ | |
434 set_euidgid(saved_uid, saved_gid, NULL, NULL); | |
435 } | |
436 return TRUE; | |
437 } |