masqmail

annotate src/spool.c @ 432:ddc05e12e307

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