masqmail-0.2

annotate src/accept.c @ 171:349518b940db

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