masqmail-0.2

view src/spool.c @ 91:3e7136221104

correct masqmail path in rmail script; remove docs on uninstall on install the correct path to the masqmail executable gets inserted into the rmail script now. now documentation, examples, and the templates are removed on uninstall. Empty directories are the only thing that may remain if one installs masqmail into an unusual path.
author meillo@marmaro.de
date Mon, 21 Jun 2010 09:40:16 +0200
parents 3b344bf57162
children 46f407c0727a
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->rcpt_list = g_list_append(msg->rcpt_list, addr);
155 } else {
156 msg->non_rcpt_list = g_list_append(msg->non_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 /* header spool: */
210 ok = spool_read_header(msg);
211 if (ok && do_readdata) {
212 /* data spool: */
213 ok = spool_read_data(msg);
214 }
215 return msg;
216 }
218 /* write header. uid and gid should already be set to the
219 mail ids. Better call spool_write(msg, FALSE).
220 */
221 static gboolean
222 spool_write_header(message * msg)
223 {
224 GList *node;
225 gchar *spool_file, *tmp_file;
226 FILE *out;
227 gboolean ok = TRUE;
229 /* header spool: */
230 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
231 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
233 if ((out = fopen(tmp_file, "w"))) {
234 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
236 fprintf(out, "%s\n", msg->uid);
237 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
239 DEBUG(6) debugf("after MF\n");
240 foreach(msg->rcpt_list, node) {
241 address *rcpt = (address *) (node->data);
242 spool_write_rcpt(out, rcpt);
243 }
244 foreach(msg->non_rcpt_list, node) {
245 address *rcpt = (address *) (node->data);
246 spool_write_rcpt(out, rcpt);
247 }
248 DEBUG(6) debugf("after RT\n");
249 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
250 if (msg->received_host != NULL)
251 fprintf(out, "RH:%s\n", msg->received_host);
253 if (msg->ident != NULL)
254 fprintf(out, "ID:%s\n", msg->ident);
256 if (msg->data_size >= 0)
257 fprintf(out, "DS: %d\n", msg->data_size);
259 if (msg->received_time > 0)
260 fprintf(out, "TR: %u\n", (int) (msg->received_time));
262 if (msg->warned_time > 0)
263 fprintf(out, "TW: %u\n", (int) (msg->warned_time));
265 DEBUG(6) debugf("after RH\n");
266 fprintf(out, "\n");
268 foreach(msg->hdr_list, node) {
269 header *hdr = (header *) (node->data);
270 fprintf(out, "HD:%s", hdr->header);
271 }
272 if (fflush(out) == EOF)
273 ok = FALSE;
274 else if (fdatasync(fileno(out)) != 0) {
275 if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */
276 ok = FALSE;
277 }
278 fclose(out);
279 if (ok) {
280 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
281 DEBUG(4) debugf("spool_file = %s\n", spool_file);
282 ok = (rename(tmp_file, spool_file) != -1);
283 g_free(spool_file);
284 }
285 } else {
286 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
287 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
288 ok = FALSE;
289 }
291 g_free(tmp_file);
293 return ok;
294 }
296 gboolean
297 spool_write(message * msg, gboolean do_write_data)
298 {
299 GList *list;
300 gchar *spool_file, *tmp_file;
301 FILE *out;
302 gboolean ok = TRUE;
303 uid_t saved_uid, saved_gid;
304 /* user can read/write, group can read, others cannot do anything: */
305 mode_t saved_mode = saved_mode = umask(026);
307 /* set uid and gid to the mail ids */
308 if (!conf.run_as_user) {
309 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
310 }
312 /* header spool: */
313 ok = spool_write_header(msg);
315 if (ok && do_write_data) {
316 /* data spool: */
317 tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid());
318 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
320 if ((out = fopen(tmp_file, "w"))) {
321 fprintf(out, "%s\n", msg->uid);
322 for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) {
323 fprintf(out, "%s", (gchar *) (list->data));
324 }
326 /* possibly paranoid ;-) */
327 if (fflush(out) == EOF) {
328 ok = FALSE;
329 } else if (fdatasync(fileno(out)) != 0) {
330 if (errno != EINVAL) { /* some fs do not support this.. I hope this also means that it is not necessary */
331 ok = FALSE;
332 }
333 }
334 fclose(out);
335 if (ok) {
336 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
337 DEBUG(4) debugf("spool_file = %s\n", spool_file);
338 ok = (rename(tmp_file, spool_file) != -1);
339 g_free(spool_file);
340 }
341 } else {
342 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n",
343 strerror(errno));
344 ok = FALSE;
345 }
346 g_free(tmp_file);
347 }
349 /* set uid and gid back */
350 if (!conf.run_as_user) {
351 set_euidgid(saved_uid, saved_gid, NULL, NULL);
352 }
354 umask(saved_mode);
356 return ok;
357 }
359 #define MAX_LOCKAGE 300
361 gboolean
362 spool_lock(gchar * uid)
363 {
364 uid_t saved_uid, saved_gid;
365 gchar *hitch_name;
366 gchar *lock_name;
367 gboolean ok = FALSE;
369 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
370 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
372 /* set uid and gid to the mail ids */
373 if (!conf.run_as_user) {
374 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
375 }
377 ok = dot_lock(lock_name, hitch_name);
378 if (!ok)
379 logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
381 /* set uid and gid back */
382 if (!conf.run_as_user) {
383 set_euidgid(saved_uid, saved_gid, NULL, NULL);
384 }
386 g_free(lock_name);
387 g_free(hitch_name);
389 return ok;
390 }
392 gboolean
393 spool_unlock(gchar * uid)
394 {
395 uid_t saved_uid, saved_gid;
396 gchar *lock_name;
398 /* set uid and gid to the mail ids */
399 if (!conf.run_as_user) {
400 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
401 }
403 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
404 dot_unlock(lock_name);
405 g_free(lock_name);
407 /* set uid and gid back */
408 if (!conf.run_as_user) {
409 set_euidgid(saved_uid, saved_gid, NULL, NULL);
410 }
411 return TRUE;
412 }
414 gboolean
415 spool_delete_all(message * msg)
416 {
417 uid_t saved_uid, saved_gid;
418 gchar *spool_file;
420 /* set uid and gid to the mail ids */
421 if (!conf.run_as_user) {
422 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
423 }
425 /* header spool: */
426 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
427 if (unlink(spool_file) != 0) {
428 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno));
429 }
430 g_free(spool_file);
432 /* data spool: */
433 spool_file = g_strdup_printf("%s/input/%s-D", 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 /* set uid and gid back */
440 if (!conf.run_as_user) {
441 set_euidgid(saved_uid, saved_gid, NULL, NULL);
442 }
443 return TRUE;
444 }