masqmail

annotate src/accept.c @ 281:ea5f86e0a81c

modes are now enforced exclusive Other MTAs (exim, postfix) are more relaxing, but as combinations of exclusive modes are senseless we behave more obvious if we fail early. This makes understanding the behavior easier too.
author markus schnalke <meillo@marmaro.de>
date Tue, 07 Dec 2010 14:04:56 -0300
parents 1405012509e3
children bb3005ce0837
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@224 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@0 20 #include "masqmail.h"
meillo@0 21 #include "readsock.h"
meillo@0 22
meillo@10 23 gchar *prot_names[] = {
meillo@10 24 "local",
meillo@10 25 "bsmtp",
meillo@10 26 "smtp",
meillo@10 27 "esmtp",
meillo@10 28 "(unknown)" /* should not happen, but better than crashing. */
meillo@0 29 };
meillo@0 30
meillo@10 31 static gchar*
meillo@10 32 string_base62(gchar * res, guint value, gchar len)
meillo@0 33 {
meillo@10 34 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
meillo@10 35 gchar *p = res + len;
meillo@14 36 *p = '\0';
meillo@10 37 while (p > res) {
meillo@10 38 *(--p) = base62_chars[value % 62];
meillo@10 39 value /= 62;
meillo@10 40 }
meillo@10 41 return res;
meillo@0 42 }
meillo@0 43
meillo@10 44 static gint
meillo@10 45 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
meillo@0 46 {
meillo@10 47 address *addr1 = (address *) a;
meillo@10 48 address *addr2 = (address *) b;
meillo@10 49 int ret;
meillo@0 50
meillo@270 51 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) {
meillo@10 52 return strcmp(addr1->local_part, addr2->local_part);
meillo@270 53 }
meillo@270 54 return ret;
meillo@0 55 }
meillo@0 56
meillo@0 57 /* accept message from anywhere.
meillo@276 58 A message from local is indicated by msg->recieved_host == NULL
meillo@0 59
meillo@276 60 The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
meillo@276 61 in To/Cc/Bcc headers are added to the recipient list.
meillo@0 62 */
meillo@0 63
meillo@10 64 accept_error
meillo@10 65 accept_message_stream(FILE * in, message * msg, guint flags)
meillo@0 66 {
meillo@10 67 gchar *line, *line1;
meillo@10 68 int line_size = MAX_DATALINE;
meillo@10 69 gboolean in_headers = TRUE;
meillo@10 70 header *hdr = NULL;
meillo@10 71 gint line_cnt = 0, data_size = 0;
meillo@0 72
meillo@10 73 line = g_malloc(line_size);
meillo@14 74 line[0] = '\0';
meillo@0 75
meillo@10 76 while (TRUE) {
meillo@10 77 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
meillo@0 78
meillo@10 79 line1 = line;
meillo@0 80
meillo@110 81 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
meillo@10 82 if (line[1] == '\n') {
meillo@10 83 g_free(line);
meillo@10 84 break;
meillo@10 85 }
meillo@10 86 line1++;
meillo@10 87 }
meillo@10 88
meillo@270 89 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
meillo@270 90 /* we got an EOF, and the last line was not terminated by a CR */
meillo@270 91 gint len1 = strlen(line1);
meillo@270 92 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
meillo@270 93 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
meillo@270 94 line1[len1] = '\n';
meillo@270 95 line1[len1 + 1] = '\0';
meillo@270 96 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@270 97 data_size += strlen(line1);
meillo@270 98 line_cnt++;
meillo@10 99 }
meillo@270 100 }
meillo@270 101 break;
meillo@270 102
meillo@270 103 } else if (len == -1) {
meillo@270 104 g_free(line);
meillo@270 105 return AERR_EOF;
meillo@270 106
meillo@270 107 } else if (len == -2) {
meillo@270 108 /* should not happen any more */
meillo@270 109 g_free(line);
meillo@270 110 return AERR_OVERFLOW;
meillo@270 111
meillo@270 112 } else if (len == -3) {
meillo@270 113 g_free(line);
meillo@270 114 return AERR_TIMEOUT;
meillo@270 115
meillo@270 116 } else if (len <= 0) {
meillo@270 117 /* does not happen */
meillo@270 118 g_free(line);
meillo@270 119 DEBUG(5) debugf("read_sockline returned %d\n", len);
meillo@270 120 return AERR_UNKNOWN;
meillo@270 121
meillo@270 122 }
meillo@270 123
meillo@270 124 if (in_headers) {
meillo@270 125
meillo@270 126 /* some pop servers send the 'From ' line, skip it: */
meillo@270 127 if (!msg->hdr_list && strncmp(line1, "From ", 5) == 0) {
meillo@270 128 continue;
meillo@270 129 }
meillo@270 130
meillo@270 131 if (line1[0] == ' ' || line1[0] == '\t') {
meillo@270 132 /* continuation of 'folded' header: */
meillo@270 133 if (hdr) {
meillo@270 134 hdr->header = g_strconcat(hdr->header, line1, NULL);
meillo@270 135 }
meillo@270 136
meillo@270 137 } else if (line1[0] == '\n') {
meillo@270 138 /* an empty line marks end of headers */
meillo@270 139 in_headers = FALSE;
meillo@10 140 } else {
meillo@270 141 /* in all other cases we expect another header */
meillo@270 142 if ((hdr = get_header(line1))) {
meillo@270 143 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
meillo@10 144 } else {
meillo@270 145 /* if get_header() returns NULL, no header was recognized,
meillo@270 146 so this seems to be the first data line of a broken mailer
meillo@270 147 which does not send an empty line after the headers */
meillo@270 148 in_headers = FALSE;
meillo@270 149 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@10 150 }
meillo@10 151 }
meillo@10 152 } else {
meillo@270 153 /* message body */
meillo@270 154 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@270 155 data_size += strlen(line1);
meillo@270 156 line_cnt++;
meillo@270 157 }
meillo@10 158
meillo@120 159 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
meillo@117 160 DEBUG(4) debugf("accept_message_stream(): "
meillo@117 161 "received %d bytes (conf.max_msg_size=%d)\n",
meillo@117 162 data_size, conf.max_msg_size);
meillo@117 163 return AERR_SIZE;
meillo@117 164 }
meillo@117 165
meillo@0 166 }
meillo@0 167
meillo@270 168 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
meillo@270 169
meillo@270 170 if (!msg->data_list) {
meillo@10 171 /* make sure data list is not NULL: */
meillo@10 172 msg->data_list = g_list_append(NULL, g_strdup(""));
meillo@270 173 }
meillo@270 174 msg->data_list = g_list_reverse(msg->data_list);
meillo@0 175
meillo@10 176 /* we get here after we succesfully received the mail data */
meillo@0 177
meillo@10 178 msg->data_size = data_size;
meillo@10 179 msg->received_time = time(NULL);
meillo@0 180
meillo@10 181 return AERR_OK;
meillo@0 182 }
meillo@0 183
meillo@10 184 accept_error
meillo@10 185 accept_message_prepare(message * msg, guint flags)
meillo@0 186 {
meillo@10 187 struct passwd *passwd = NULL;
meillo@10 188 time_t rec_time = time(NULL);
meillo@0 189
meillo@10 190 DEBUG(5) debugf("accept_message_prepare()\n");
meillo@0 191
meillo@10 192 /* create unique message id */
meillo@10 193 msg->uid = g_malloc(14);
meillo@0 194
meillo@10 195 string_base62(msg->uid, rec_time, 6);
meillo@10 196 msg->uid[6] = '-';
meillo@10 197 string_base62(&(msg->uid[7]), getpid(), 3);
meillo@10 198 msg->uid[10] = '-';
meillo@10 199 string_base62(&(msg->uid[11]), msg->transfer_id, 2);
meillo@10 200 msg->uid[13] = 0;
meillo@0 201
meillo@10 202 /* if local, get password entry */
meillo@10 203 if (msg->received_host == NULL) {
meillo@10 204 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
meillo@10 205 msg->ident = g_strdup(passwd->pw_name);
meillo@10 206 }
meillo@0 207
meillo@10 208 /* set return path if local */
meillo@22 209 if (msg->return_path == NULL && msg->received_host == NULL) {
meillo@22 210 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
meillo@22 211 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
meillo@22 212 msg->return_path = create_address(path, TRUE);
meillo@22 213 g_free(path);
meillo@10 214 }
meillo@0 215
meillo@10 216 /* scan headers */
meillo@10 217 {
meillo@10 218 gboolean has_id = FALSE;
meillo@10 219 gboolean has_date = FALSE;
meillo@10 220 gboolean has_sender = FALSE;
meillo@10 221 gboolean has_from = FALSE;
meillo@10 222 gboolean has_to_or_cc = FALSE;
meillo@10 223 GList *hdr_node, *hdr_node_next;
meillo@10 224 header *hdr;
meillo@0 225
meillo@10 226 for (hdr_node = g_list_first(msg->hdr_list);
meillo@10 227 hdr_node != NULL; hdr_node = hdr_node_next) {
meillo@10 228 hdr_node_next = g_list_next(hdr_node);
meillo@10 229 hdr = ((header *) (hdr_node->data));
meillo@10 230 DEBUG(5) debugf("scanning headers: %s", hdr->header);
meillo@10 231 switch (hdr->id) {
meillo@10 232 case HEAD_MESSAGE_ID:
meillo@10 233 has_id = TRUE;
meillo@10 234 break;
meillo@10 235 case HEAD_DATE:
meillo@10 236 has_date = TRUE;
meillo@10 237 break;
meillo@10 238 case HEAD_FROM:
meillo@10 239 has_from = TRUE;
meillo@10 240 break;
meillo@10 241 case HEAD_SENDER:
meillo@10 242 has_sender = TRUE;
meillo@10 243 break;
meillo@10 244 case HEAD_TO:
meillo@10 245 case HEAD_CC:
meillo@106 246 has_to_or_cc = TRUE;
meillo@106 247 /* fall through */
meillo@10 248 case HEAD_BCC:
meillo@10 249 if (flags & ACC_RCPT_FROM_HEAD) {
meillo@109 250 /* -t option (see comment above) */
meillo@10 251 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
meillo@10 252 if (hdr->value) {
meillo@10 253 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
meillo@10 254 }
meillo@10 255 }
meillo@106 256 if (hdr->id == HEAD_BCC) {
meillo@10 257 DEBUG(3) debugf("removing 'Bcc' header\n");
meillo@10 258 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 259 g_list_free_1(hdr_node);
meillo@10 260 destroy_header(hdr);
meillo@106 261 }
meillo@10 262 break;
meillo@10 263 case HEAD_ENVELOPE_TO:
meillo@10 264 if (flags & ACC_SAVE_ENVELOPE_TO) {
meillo@10 265 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
meillo@22 266 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@276 267 "X-Orig-Envelope-To: %s", hdr->value));
meillo@10 268 }
meillo@10 269 DEBUG(3) debugf("removing 'Envelope-To' header\n");
meillo@10 270 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 271 g_list_free_1(hdr_node);
meillo@10 272 destroy_header(hdr);
meillo@10 273 break;
meillo@10 274 case HEAD_RETURN_PATH:
meillo@10 275 if (flags & ACC_MAIL_FROM_HEAD) {
meillo@10 276 /* usually POP3 accept */
meillo@10 277 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
meillo@10 278 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@10 279 }
meillo@10 280 DEBUG(3) debugf("removing 'Return-Path' header\n");
meillo@10 281 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 282 g_list_free_1(hdr_node);
meillo@10 283 destroy_header(hdr);
meillo@10 284 break;
meillo@10 285 default:
meillo@10 286 break; /* make compiler happy */
meillo@10 287 }
meillo@10 288 }
meillo@10 289
meillo@10 290 if (msg->return_path == NULL) {
meillo@10 291 /* this can happen for pop3 accept only and if no Return-path: header was given */
meillo@10 292 GList *hdr_list;
meillo@10 293 header *hdr;
meillo@10 294
meillo@10 295 DEBUG(3) debugf("return_path == NULL\n");
meillo@10 296
meillo@10 297 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
meillo@10 298 if (!hdr_list)
meillo@10 299 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
meillo@10 300 if (hdr_list) {
meillo@10 301 gchar *addr;
meillo@10 302 hdr = (header *) (g_list_first(hdr_list)->data);
meillo@10 303
meillo@10 304 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
meillo@10 305
meillo@10 306 addr = g_strdup(hdr->value);
meillo@10 307 g_strchomp(addr);
meillo@10 308
meillo@10 309 if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) {
meillo@10 310 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@13 311 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@13 312 "X-Warning: return path set from %s address\n",
meillo@13 313 hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
meillo@10 314 }
meillo@10 315 g_free(addr);
meillo@10 316 }
meillo@10 317 if (msg->return_path == NULL) { /* no Sender: or From: or create_address_qualified failed */
meillo@10 318 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
meillo@10 319 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@13 320 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@25 321 "X-Warning: real return path is unknown\n"));
meillo@10 322 }
meillo@10 323 }
meillo@10 324
meillo@10 325 /* here we should have our recipients, fail if not: */
meillo@276 326 if (!msg->rcpt_list) {
meillo@10 327 logwrite(LOG_WARNING, "no recipients found in message\n");
meillo@10 328 return AERR_NORCPT;
meillo@10 329 }
meillo@10 330
meillo@10 331 if (!(has_sender || has_from)) {
meillo@10 332 DEBUG(3) debugf("adding 'From' header\n");
meillo@10 333 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@10 334 msg->full_sender_name
meillo@10 335 ?
meillo@13 336 create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name,
meillo@13 337 msg->return_path->local_part, msg->return_path->domain)
meillo@10 338 :
meillo@10 339 create_header(HEAD_FROM, "From: <%s@%s>\n",
meillo@13 340 msg->return_path->local_part, msg->return_path->domain)
meillo@10 341 );
meillo@10 342 }
meillo@106 343 if (!has_to_or_cc) {
meillo@106 344 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
meillo@105 345 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
meillo@10 346 }
meillo@10 347 if (!has_date) {
meillo@10 348 DEBUG(3) debugf("adding 'Date:' header\n");
meillo@10 349 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
meillo@10 350 }
meillo@10 351 if (!has_id) {
meillo@10 352 DEBUG(3) debugf("adding 'Message-ID:' header\n");
meillo@13 353 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@13 354 create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
meillo@10 355 }
meillo@0 356 }
meillo@10 357
meillo@10 358 /* Received header: */
meillo@10 359 /* At this point because we have to know the rcpts for the 'for' part */
meillo@102 360 gchar *for_string = NULL;
meillo@102 361 header *hdr = NULL;
meillo@10 362
meillo@102 363 DEBUG(3) debugf("adding 'Received:' header\n");
meillo@10 364
meillo@102 365 if (g_list_length(msg->rcpt_list) == 1) {
meillo@102 366 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
meillo@102 367 for_string = g_strdup_printf(" for %s", addr_string(addr));
meillo@102 368 }
meillo@10 369
meillo@102 370 if (msg->received_host == NULL) {
meillo@102 371 /* received locally */
meillo@102 372 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
meillo@102 373 passwd->pw_name, conf.host_name, prot_names[msg->received_prot],
meillo@102 374 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
meillo@102 375 } else {
meillo@102 376 /* received from remote */
meillo@10 377 #ifdef ENABLE_IDENT
meillo@102 378 DEBUG(5) debugf("adding 'Received:' header (5)\n");
meillo@102 379 hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n",
meillo@102 380 msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name,
meillo@102 381 prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "",
meillo@102 382 rec_timestamp());
meillo@10 383 #else
meillo@102 384 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
meillo@102 385 msg->received_host, conf.host_name, prot_names[msg->received_prot],
meillo@102 386 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
meillo@10 387 #endif
meillo@102 388 }
meillo@102 389 header_fold(hdr);
meillo@102 390 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
meillo@10 391
meillo@102 392 if (for_string)
meillo@102 393 g_free(for_string);
meillo@0 394
meillo@10 395 return AERR_OK;
meillo@0 396 }
meillo@0 397
meillo@10 398 accept_error
meillo@10 399 accept_message(FILE * in, message * msg, guint flags)
meillo@0 400 {
meillo@10 401 accept_error err;
meillo@0 402
meillo@10 403 err = accept_message_stream(in, msg, flags);
meillo@270 404 if (err == AERR_OK) {
meillo@10 405 err = accept_message_prepare(msg, flags);
meillo@270 406 }
meillo@0 407
meillo@10 408 return err;
meillo@0 409 }