masqmail-0.2

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