masqmail

annotate src/accept.c @ 246:4cff8638dd9b

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