masqmail

annotate src/spool.c @ 304:d5ce2ba71e7b

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