masqmail

annotate src/accept.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents aa40710f09fe
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@0 21 #include "masqmail.h"
meillo@0 22 #include "readsock.h"
meillo@0 23
meillo@299 24 /* must match PROT_* in masqmail.h */
meillo@10 25 gchar *prot_names[] = {
meillo@10 26 "local",
meillo@299 27 "SMTP",
meillo@299 28 "ESMTP",
meillo@10 29 "(unknown)" /* should not happen, but better than crashing. */
meillo@0 30 };
meillo@0 31
meillo@10 32 static gchar*
meillo@366 33 string_base62(gchar *res, guint value, gchar len)
meillo@0 34 {
meillo@388 35 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
meillo@388 36 "abcdefghijklmnopqrstuvwxyz";
meillo@10 37 gchar *p = res + len;
meillo@14 38 *p = '\0';
meillo@10 39 while (p > res) {
meillo@10 40 *(--p) = base62_chars[value % 62];
meillo@10 41 value /= 62;
meillo@10 42 }
meillo@10 43 return res;
meillo@0 44 }
meillo@0 45
meillo@367 46 /*
meillo@367 47 ** accept message from anywhere.
meillo@367 48 ** A message from local is indicated by msg->recieved_host == NULL
meillo@367 49 **
meillo@367 50 ** The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
meillo@367 51 ** in To/Cc/Bcc headers are added to the recipient list.
meillo@0 52 */
meillo@10 53 accept_error
meillo@366 54 accept_message_stream(FILE *in, message *msg, guint flags)
meillo@0 55 {
meillo@10 56 gchar *line, *line1;
meillo@10 57 int line_size = MAX_DATALINE;
meillo@10 58 gboolean in_headers = TRUE;
meillo@10 59 header *hdr = NULL;
meillo@10 60 gint line_cnt = 0, data_size = 0;
meillo@0 61
meillo@10 62 line = g_malloc(line_size);
meillo@388 63 *line = '\0';
meillo@0 64
meillo@388 65 while (1) {
meillo@388 66 int len = read_sockline1(in, &line, &line_size, 5 * 60,
meillo@388 67 READSOCKL_CVT_CRLF);
meillo@10 68 line1 = line;
meillo@0 69
meillo@388 70 if ((*line == '.') && (!(flags & ACC_DOT_IGNORE))) {
meillo@10 71 if (line[1] == '\n') {
meillo@10 72 g_free(line);
meillo@10 73 break;
meillo@10 74 }
meillo@10 75 line1++;
meillo@10 76 }
meillo@10 77
meillo@388 78 if (len==-1 && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
meillo@388 79 /* at EOF but last line was not terminated by CR */
meillo@388 80 /* some MUAs allow unterminated lines */
meillo@270 81 gint len1 = strlen(line1);
meillo@388 82 if (len1 > 0 && line1[len1-1] != '\n') {
meillo@388 83 line1[len1] = '\n';
meillo@388 84 line1[len1+1] = '\0';
meillo@388 85 msg->data_list = g_list_prepend(msg->data_list,
meillo@388 86 g_strdup(line1));
meillo@388 87 data_size += strlen(line1);
meillo@388 88 line_cnt++;
meillo@270 89 }
meillo@270 90 break;
meillo@270 91
meillo@270 92 } else if (len == -1) {
meillo@270 93 g_free(line);
meillo@270 94 return AERR_EOF;
meillo@270 95
meillo@270 96 } else if (len == -2) {
meillo@270 97 /* should not happen any more */
meillo@270 98 g_free(line);
meillo@270 99 return AERR_OVERFLOW;
meillo@270 100
meillo@270 101 } else if (len == -3) {
meillo@270 102 g_free(line);
meillo@270 103 return AERR_TIMEOUT;
meillo@270 104
meillo@270 105 } else if (len <= 0) {
meillo@270 106 /* does not happen */
meillo@270 107 g_free(line);
meillo@270 108 DEBUG(5) debugf("read_sockline returned %d\n", len);
meillo@270 109 return AERR_UNKNOWN;
meillo@270 110
meillo@270 111 }
meillo@270 112
meillo@270 113 if (in_headers) {
meillo@270 114 /* some pop servers send the 'From ' line, skip it: */
meillo@388 115 if (!msg->hdr_list && strncmp(line1, "From ", 5)==0) {
meillo@270 116 continue;
meillo@270 117 }
meillo@270 118
meillo@388 119 if (*line1 == ' ' || *line1 == '\t') {
meillo@270 120 /* continuation of 'folded' header: */
meillo@270 121 if (hdr) {
meillo@366 122 char *cp;
meillo@388 123 cp = g_strconcat(hdr->header, line1,
meillo@388 124 NULL);
meillo@388 125 hdr->value = cp + (hdr->value -
meillo@388 126 hdr->header);
meillo@323 127 free(hdr->header);
meillo@323 128 hdr->header = cp;
meillo@270 129 }
meillo@388 130 } else if (*line1 == '\n') {
meillo@270 131 /* an empty line marks end of headers */
meillo@270 132 in_headers = FALSE;
meillo@388 133
meillo@388 134 } else if ((hdr = get_header(line1))) {
meillo@388 135 /* another header */
meillo@388 136 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
meillo@10 137 } else {
meillo@388 138 /*
meillo@388 139 ** Should be another header but none was
meillo@388 140 ** recognized, so this seems to be the first
meillo@388 141 ** data line of a broken mailer which does
meillo@388 142 ** not add an empty line after the headers.
meillo@388 143 */
meillo@388 144 in_headers = FALSE;
meillo@388 145 msg->data_list = g_list_prepend(msg->data_list,
meillo@388 146 g_strdup(line1));
meillo@10 147 }
meillo@10 148 } else {
meillo@270 149 /* message body */
meillo@388 150 msg->data_list = g_list_prepend(msg->data_list,
meillo@388 151 g_strdup(line1));
meillo@270 152 data_size += strlen(line1);
meillo@270 153 line_cnt++;
meillo@270 154 }
meillo@10 155
meillo@120 156 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
meillo@117 157 DEBUG(4) debugf("accept_message_stream(): "
meillo@117 158 "received %d bytes (conf.max_msg_size=%d)\n",
meillo@117 159 data_size, conf.max_msg_size);
meillo@117 160 return AERR_SIZE;
meillo@117 161 }
meillo@0 162 }
meillo@388 163 DEBUG(4) debugf("received %d lines of data (%d bytes)\n",
meillo@388 164 line_cnt, data_size);
meillo@270 165
meillo@270 166 if (!msg->data_list) {
meillo@388 167 /* make sure data list is not NULL */
meillo@10 168 msg->data_list = g_list_append(NULL, g_strdup(""));
meillo@270 169 }
meillo@270 170 msg->data_list = g_list_reverse(msg->data_list);
meillo@0 171
meillo@388 172 /* we have succesfully received the mail data */
meillo@0 173
meillo@10 174 msg->data_size = data_size;
meillo@10 175 msg->received_time = time(NULL);
meillo@0 176
meillo@10 177 return AERR_OK;
meillo@0 178 }
meillo@0 179
meillo@388 180 static void
meillo@388 181 ensure_return_path(message *msg)
meillo@388 182 {
meillo@388 183 GList *hdr_list;
meillo@388 184 header *hdr;
meillo@388 185 gchar *addr;
meillo@388 186
meillo@388 187 if (msg->return_path) {
meillo@388 188 return;
meillo@388 189 }
meillo@388 190
meillo@388 191 DEBUG(3) debugf("return_path == NULL\n");
meillo@388 192
meillo@388 193 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
meillo@388 194 if (!hdr_list) {
meillo@388 195 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
meillo@388 196 }
meillo@388 197 if (hdr_list) {
meillo@388 198 hdr = (header *) (g_list_first(hdr_list)->data);
meillo@388 199
meillo@388 200 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
meillo@388 201
meillo@411 202 addr = g_strstrip(g_strdup(hdr->value));
meillo@388 203 msg->return_path = create_address_qualified(addr,
meillo@388 204 FALSE, msg->received_host);
meillo@388 205 if (msg->return_path) {
meillo@388 206 DEBUG(3) debugf("setting return_path to %s\n",
meillo@388 207 addr_string(msg->return_path));
meillo@388 208 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 209 create_header(HEAD_UNKNOWN,
meillo@388 210 "X-Warning: return path set from %s "
meillo@388 211 "address\n",
meillo@388 212 (hdr->id == HEAD_SENDER) ?
meillo@388 213 "Sender:" : "From:"));
meillo@388 214 }
meillo@388 215 g_free(addr);
meillo@388 216 }
meillo@388 217 if (!msg->return_path) {
meillo@388 218 /* no Sender: or From: or create_address_qualified failed */
meillo@388 219 msg->return_path = create_address_qualified("postmaster",
meillo@388 220 TRUE, conf.host_name);
meillo@388 221 DEBUG(3) debugf("setting return_path to %s\n",
meillo@388 222 addr_string(msg->return_path));
meillo@388 223 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 224 create_header(HEAD_UNKNOWN,
meillo@388 225 "X-Warning: real return path is unknown\n"));
meillo@388 226 }
meillo@388 227 }
meillo@388 228
meillo@388 229 static accept_error
meillo@388 230 scan_headers(message *msg, guint flags)
meillo@388 231 {
meillo@388 232 gboolean has_id = FALSE;
meillo@388 233 gboolean has_date = FALSE;
meillo@388 234 gboolean has_sender = FALSE;
meillo@388 235 gboolean has_from = FALSE;
meillo@388 236 gboolean has_to_or_cc = FALSE;
meillo@388 237 GList *hdr_node, *hdr_node_next;
meillo@388 238 header *hdr;
meillo@388 239
meillo@388 240 for (hdr_node = g_list_first(msg->hdr_list); hdr_node;
meillo@388 241 hdr_node = hdr_node_next) {
meillo@388 242 hdr_node_next = g_list_next(hdr_node);
meillo@388 243 hdr = ((header *) (hdr_node->data));
meillo@388 244 DEBUG(5) debugf("scanning headers: %s", hdr->header);
meillo@388 245 switch (hdr->id) {
meillo@388 246 case HEAD_MESSAGE_ID:
meillo@388 247 has_id = TRUE;
meillo@388 248 break;
meillo@388 249 case HEAD_DATE:
meillo@388 250 has_date = TRUE;
meillo@388 251 break;
meillo@388 252 case HEAD_FROM:
meillo@388 253 has_from = TRUE;
meillo@388 254 break;
meillo@388 255 case HEAD_SENDER:
meillo@388 256 has_sender = TRUE;
meillo@388 257 break;
meillo@388 258 case HEAD_TO:
meillo@388 259 case HEAD_CC:
meillo@388 260 has_to_or_cc = TRUE;
meillo@388 261 /* fall through */
meillo@388 262 case HEAD_BCC:
meillo@388 263 if (flags & ACC_RCPT_FROM_HEAD) {
meillo@388 264 /* -t option (see comment above) */
meillo@388 265 DEBUG(5) debugf("hdr->value = %s\n",
meillo@388 266 hdr->value);
meillo@388 267 if (hdr->value) {
meillo@388 268 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
meillo@388 269 }
meillo@388 270 }
meillo@388 271 if (hdr->id == HEAD_BCC) {
meillo@388 272 DEBUG(3) debugf("removing 'Bcc' header\n");
meillo@388 273 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@388 274 g_list_free_1(hdr_node);
meillo@388 275 destroy_header(hdr);
meillo@388 276 }
meillo@388 277 break;
meillo@388 278 case HEAD_ENVELOPE_TO:
meillo@388 279 if (flags & ACC_SAVE_ENVELOPE_TO) {
meillo@388 280 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
meillo@388 281 msg->hdr_list = g_list_prepend(msg->hdr_list,
meillo@388 282 create_header(HEAD_UNKNOWN,
meillo@388 283 "X-Orig-Envelope-To: %s",
meillo@388 284 hdr->value));
meillo@388 285 }
meillo@388 286 DEBUG(3) debugf("removing 'Envelope-To' header\n");
meillo@388 287 msg->hdr_list = g_list_remove_link(msg->hdr_list,
meillo@388 288 hdr_node);
meillo@388 289 g_list_free_1(hdr_node);
meillo@388 290 destroy_header(hdr);
meillo@388 291 break;
meillo@388 292 case HEAD_RETURN_PATH:
meillo@388 293 if (flags & ACC_MAIL_FROM_HEAD) {
meillo@388 294 /* usually POP3 accept */
meillo@388 295 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
meillo@388 296 DEBUG(3) debugf("setting return_path to %s\n",
meillo@388 297 addr_string(msg->return_path));
meillo@388 298 }
meillo@388 299 DEBUG(3) debugf("removing 'Return-Path' header\n");
meillo@388 300 msg->hdr_list = g_list_remove_link(msg->hdr_list,
meillo@388 301 hdr_node);
meillo@388 302 g_list_free_1(hdr_node);
meillo@388 303 destroy_header(hdr);
meillo@388 304 break;
meillo@388 305 default:
meillo@388 306 break; /* make compiler happy */
meillo@388 307 }
meillo@388 308 }
meillo@388 309
meillo@388 310 /*
meillo@388 311 ** TODO: do we still need this as we don't fetch
meillo@388 312 ** mail anymore?
meillo@388 313 ** This can happen for pop3 accept only and if no
meillo@388 314 ** Return-Path: header was given
meillo@388 315 */
meillo@388 316 ensure_return_path(msg);
meillo@388 317
meillo@388 318 /* here we should have our recipients, fail if not: */
meillo@388 319 if (!msg->rcpt_list) {
meillo@388 320 logwrite(LOG_WARNING, "no recipients found in message\n");
meillo@388 321 return AERR_NORCPT;
meillo@388 322 }
meillo@388 323
meillo@388 324 if (!has_sender && !has_from) {
meillo@388 325 DEBUG(3) debugf("adding 'From:' header\n");
meillo@388 326 if (msg->full_sender_name) {
meillo@388 327 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 328 create_header(HEAD_FROM,
meillo@388 329 "From: \"%s\" <%s@%s>\n",
meillo@388 330 msg->full_sender_name,
meillo@388 331 msg->return_path->local_part,
meillo@388 332 msg->return_path->domain));
meillo@388 333 } else {
meillo@388 334 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 335 create_header(HEAD_FROM,
meillo@388 336 "From: <%s@%s>\n",
meillo@388 337 msg->return_path->local_part,
meillo@388 338 msg->return_path->domain));
meillo@388 339 }
meillo@388 340 }
meillo@388 341 if (!has_to_or_cc) {
meillo@388 342 DEBUG(3) debugf("no To: or Cc: header, hence adding "
meillo@388 343 "`To: undisclosed recipients:;'\n");
meillo@388 344 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 345 create_header(HEAD_TO,
meillo@388 346 "To: undisclosed-recipients:;\n"));
meillo@388 347 }
meillo@388 348 if (!has_date) {
meillo@388 349 DEBUG(3) debugf("adding 'Date:' header\n");
meillo@388 350 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 351 create_header(HEAD_DATE, "Date: %s\n",
meillo@388 352 rec_timestamp()));
meillo@388 353 }
meillo@388 354 if (!has_id) {
meillo@388 355 DEBUG(3) debugf("adding 'Message-ID:' header\n");
meillo@388 356 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@388 357 create_header(HEAD_MESSAGE_ID,
meillo@388 358 "Message-ID: <%s@%s>\n",
meillo@388 359 msg->uid, conf.host_name));
meillo@388 360 }
meillo@388 361
meillo@388 362 return AERR_OK;
meillo@388 363 }
meillo@388 364
meillo@388 365 static void
meillo@388 366 add_received_hdr(message *msg)
meillo@388 367 {
meillo@388 368 gchar *for_string = NULL;
meillo@388 369 header *hdr = NULL;
meillo@388 370 address *addr;
meillo@388 371
meillo@388 372 DEBUG(3) debugf("adding 'Received:' header\n");
meillo@388 373 if (g_list_length(msg->rcpt_list) == 1) {
meillo@388 374 /* The `for' part only if exactly one rcpt is present */
meillo@388 375 addr = (address *) (g_list_first(msg->rcpt_list)->data);
meillo@388 376 for_string = g_strdup_printf("\n\tfor %s", addr_string(addr));
meillo@388 377 }
meillo@388 378 if (!msg->received_host) {
meillo@388 379 /* received locally */
meillo@388 380 hdr = create_header(HEAD_RECEIVED,
meillo@388 381 "Received: by %s (%s %s, from userid %d)\n"
meillo@388 382 "\tid %s%s; %s\n",
meillo@388 383 conf.host_name, PACKAGE, VERSION, geteuid(),
meillo@388 384 msg->uid,
meillo@388 385 for_string ? for_string : "", rec_timestamp());
meillo@388 386 } else {
meillo@388 387 /* received from remote */
meillo@388 388 DEBUG(5) debugf("adding 'Received:' header (5)\n");
meillo@388 389 hdr = create_header(HEAD_RECEIVED,
meillo@388 390 "Received: from %s\n"
meillo@388 391 "\tby %s with %s (%s %s)\n"
meillo@388 392 "\tid %s%s; %s\n",
meillo@388 393 msg->received_host, conf.host_name,
meillo@388 394 prot_names[msg->received_prot], PACKAGE,
meillo@388 395 VERSION, msg->uid,
meillo@388 396 for_string ? for_string : "", rec_timestamp());
meillo@388 397 }
meillo@388 398 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
meillo@388 399 if (for_string) {
meillo@388 400 g_free(for_string);
meillo@388 401 }
meillo@388 402 }
meillo@388 403
meillo@10 404 accept_error
meillo@366 405 accept_message_prepare(message *msg, guint flags)
meillo@0 406 {
meillo@10 407 DEBUG(5) debugf("accept_message_prepare()\n");
meillo@0 408
meillo@388 409 /* generate unique message id */
meillo@10 410 msg->uid = g_malloc(14);
meillo@388 411 string_base62(msg->uid, time(NULL), 6);
meillo@10 412 msg->uid[6] = '-';
meillo@298 413 string_base62(msg->uid + 7, getpid(), 3);
meillo@10 414 msg->uid[10] = '-';
meillo@298 415 string_base62(msg->uid + 11, msg->transfer_id, 2);
meillo@298 416 msg->uid[13] = '\0';
meillo@0 417
meillo@298 418 /* if local, get password entry and set return path if missing */
meillo@298 419 if (!msg->received_host) {
meillo@388 420 struct passwd *passwd = NULL;
meillo@388 421
meillo@10 422 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
meillo@10 423 msg->ident = g_strdup(passwd->pw_name);
meillo@298 424 if (!msg->return_path) {
meillo@388 425 gchar *path = g_strdup_printf("<%s@%s>",
meillo@388 426 passwd->pw_name, conf.host_name);
meillo@388 427 DEBUG(3) debugf("setting return_path for local "
meillo@388 428 "accept: %s\n", path);
meillo@298 429 msg->return_path = create_address(path, TRUE);
meillo@298 430 g_free(path);
meillo@298 431 }
meillo@10 432 }
meillo@0 433
meillo@388 434 if (scan_headers(msg, flags) == AERR_NORCPT) {
meillo@388 435 return AERR_NORCPT;
meillo@0 436 }
meillo@10 437
meillo@388 438 /* after the hdrs are scanned because we need to know the rcpts */
meillo@388 439 add_received_hdr(msg);
meillo@0 440
meillo@10 441 return AERR_OK;
meillo@0 442 }
meillo@0 443
meillo@10 444 accept_error
meillo@366 445 accept_message(FILE *in, message *msg, guint flags)
meillo@0 446 {
meillo@10 447 accept_error err;
meillo@0 448
meillo@10 449 err = accept_message_stream(in, msg, flags);
meillo@270 450 if (err == AERR_OK) {
meillo@10 451 err = accept_message_prepare(msg, flags);
meillo@270 452 }
meillo@0 453
meillo@10 454 return err;
meillo@0 455 }