masqmail-0.2

annotate src/smtp_out.c @ 179:ec3fe72a3e99

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:52:17 +0200
parents 087e99c7702a
children
rev   line source
meillo@174 1 /* smtp_out.c
meillo@174 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@174 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@174 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@10 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., 675 Mass Ave, Cambridge, MA 02139, USA.
meillo@0 18 */
meillo@0 19
meillo@0 20 /*
meillo@0 21 I always forget these rfc numbers:
meillo@0 22 RFC 821 (SMTP)
meillo@0 23 RFC 1869 (ESMTP)
meillo@0 24 RFC 1870 (ESMTP SIZE)
meillo@0 25 RFC 2197 (ESMTP PIPELINE)
meillo@0 26 RFC 2554 (ESMTP AUTH)
meillo@0 27 */
meillo@0 28
meillo@0 29 #include "masqmail.h"
meillo@0 30 #include "smtp_out.h"
meillo@0 31 #include "readsock.h"
meillo@0 32
meillo@0 33 #ifdef ENABLE_AUTH
meillo@0 34
meillo@0 35 #ifdef USE_LIB_CRYPTO
meillo@0 36 #include <openssl/hmac.h>
meillo@0 37 #include <openssl/md5.h>
meillo@0 38 #include <openssl/evp.h>
meillo@0 39 #else
meillo@0 40 #include "md5/md5.h"
meillo@0 41 #include "md5/hmac_md5.h"
meillo@0 42 #endif
meillo@0 43
meillo@0 44 #include "base64/base64.h"
meillo@0 45 #endif
meillo@0 46
meillo@10 47 void
meillo@10 48 destroy_smtpbase(smtp_base * psb)
meillo@0 49 {
meillo@10 50 fclose(psb->in);
meillo@10 51 fclose(psb->out);
meillo@0 52
meillo@10 53 close(psb->sock);
meillo@0 54
meillo@10 55 if (psb->helo_name)
meillo@10 56 g_free(psb->helo_name);
meillo@10 57 if (psb->buffer)
meillo@10 58 g_free(psb->buffer);
meillo@10 59 if (psb->auth_names)
meillo@10 60 g_strfreev(psb->auth_names);
meillo@0 61
meillo@10 62 if (psb->auth_name)
meillo@10 63 g_free(psb->auth_name);
meillo@10 64 if (psb->auth_login)
meillo@10 65 g_free(psb->auth_login);
meillo@10 66 if (psb->auth_secret)
meillo@10 67 g_free(psb->auth_secret);
meillo@0 68 }
meillo@0 69
meillo@10 70 gchar*
meillo@10 71 set_heloname(smtp_base * psb, gchar * default_name, gboolean do_correct)
meillo@0 72 {
meillo@10 73 struct sockaddr_in sname;
meillo@10 74 int len = sizeof(struct sockaddr_in);
meillo@10 75 struct hostent *host_entry;
meillo@0 76
meillo@10 77 if (do_correct) {
meillo@10 78 getsockname(psb->sock, (struct sockaddr *) (&sname), &len);
meillo@10 79 DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr));
meillo@10 80 host_entry = gethostbyaddr((const char *) &(sname.sin_addr), sizeof(sname.sin_addr), AF_INET);
meillo@10 81 if (host_entry) {
meillo@10 82 psb->helo_name = g_strdup(host_entry->h_name);
meillo@10 83 } else {
meillo@10 84 /* we failed to look up our own name. Instead of giving our local hostname,
meillo@10 85 we may give our IP number to show the server that we are at least
meillo@10 86 willing to be honest. For the really picky ones. */
meillo@10 87 DEBUG(5) debugf("failed to look up own host name.\n");
meillo@10 88 psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr));
meillo@10 89 }
meillo@10 90 DEBUG(5) debugf("helo_name = %s\n", psb->helo_name);
meillo@10 91 }
meillo@10 92 if (psb->helo_name == NULL) {
meillo@10 93 psb->helo_name = g_strdup(default_name);
meillo@10 94 }
meillo@10 95 return psb->helo_name;
meillo@10 96 }
meillo@0 97
meillo@0 98 #ifdef ENABLE_AUTH
meillo@0 99
meillo@10 100 gboolean
meillo@10 101 set_auth(smtp_base * psb, gchar * name, gchar * login, gchar * secret)
meillo@0 102 {
meillo@10 103 if ((strcasecmp(name, "CRAM-MD5") == 0) || (strcasecmp(name, "LOGIN") == 0)) {
meillo@10 104 psb->auth_name = g_strdup(name);
meillo@10 105 psb->auth_login = g_strdup(login);
meillo@10 106 psb->auth_secret = g_strdup(secret);
meillo@10 107
meillo@10 108 return TRUE;
meillo@10 109 }
meillo@10 110 return FALSE;
meillo@0 111 }
meillo@0 112
meillo@0 113 #endif
meillo@0 114
meillo@10 115 static smtp_base*
meillo@10 116 create_smtpbase(gint sock)
meillo@0 117 {
meillo@10 118 gint dup_sock;
meillo@0 119
meillo@10 120 smtp_base *psb = (smtp_base *) g_malloc(sizeof(smtp_base));
meillo@0 121
meillo@10 122 psb->sock = sock;
meillo@0 123
meillo@10 124 psb->use_esmtp = FALSE;
meillo@10 125 psb->use_size = FALSE;
meillo@10 126 psb->use_pipelining = FALSE;
meillo@10 127 psb->use_auth = FALSE;
meillo@0 128
meillo@10 129 psb->max_size = 0;
meillo@10 130 psb->auth_names = NULL;
meillo@0 131
meillo@10 132 psb->buffer = (gchar *) g_malloc(SMTP_BUF_LEN);
meillo@0 133
meillo@10 134 dup_sock = dup(sock);
meillo@10 135 psb->out = fdopen(sock, "w");
meillo@10 136 psb->in = fdopen(dup_sock, "r");
meillo@0 137
meillo@10 138 psb->error = smtp_ok;
meillo@0 139
meillo@10 140 psb->helo_name = NULL;
meillo@0 141
meillo@10 142 psb->auth_name = psb->auth_login = psb->auth_secret = NULL;
meillo@10 143
meillo@10 144 return psb;
meillo@0 145 }
meillo@0 146
meillo@10 147 static gboolean
meillo@10 148 read_response(smtp_base * psb, int timeout)
meillo@0 149 {
meillo@10 150 gint buf_pos = 0;
meillo@10 151 gchar code[5];
meillo@10 152 gint i, len;
meillo@0 153
meillo@10 154 do {
meillo@10 155 len = read_sockline(psb->in, &(psb->buffer[buf_pos]), SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG);
meillo@10 156 if (len == -3) {
meillo@10 157 psb->error = smtp_timeout;
meillo@10 158 return FALSE;
meillo@10 159 } else if (len == -2) {
meillo@10 160 psb->error = smtp_syntax;
meillo@10 161 return FALSE;
meillo@10 162 } else if (len == -1) {
meillo@10 163 psb->error = smtp_eof;
meillo@10 164 return FALSE;
meillo@10 165 }
meillo@10 166 for (i = 0; i < 4; i++)
meillo@10 167 code[i] = psb->buffer[buf_pos + i];
meillo@15 168 code[i] = '\0';
meillo@10 169 psb->last_code = atoi(code);
meillo@0 170
meillo@10 171 buf_pos += len;
meillo@0 172
meillo@10 173 } while (code[3] == '-');
meillo@0 174
meillo@10 175 return TRUE;
meillo@0 176 }
meillo@0 177
meillo@10 178 static gboolean
meillo@10 179 check_response(smtp_base * psb, gboolean after_data)
meillo@0 180 {
meillo@10 181 char c = psb->buffer[0];
meillo@0 182
meillo@10 183 if (((c == '2') && !after_data) || ((c == '3') && after_data)) {
meillo@10 184 psb->error = smtp_ok;
meillo@10 185 DEBUG(6) debugf("response OK:'%s' after_date = %d\n", psb->buffer, (int) after_data);
meillo@10 186 return TRUE;
meillo@10 187 } else {
meillo@10 188 if (c == '4')
meillo@10 189 psb->error = smtp_trylater;
meillo@10 190 else if (c == '5')
meillo@10 191 psb->error = smtp_fail;
meillo@10 192 else
meillo@10 193 psb->error = smtp_syntax;
meillo@10 194 DEBUG(6) debugf("response failure:'%s' after_date = %d\n", psb->buffer, (int) after_data);
meillo@10 195 return FALSE;
meillo@10 196 }
meillo@0 197 }
meillo@0 198
meillo@10 199 static gboolean
meillo@10 200 check_init_response(smtp_base * psb)
meillo@0 201 {
meillo@10 202 if (check_response(psb, FALSE)) {
meillo@10 203 psb->use_esmtp = (strstr(psb->buffer, "ESMTP") != NULL);
meillo@0 204
meillo@10 205 DEBUG(4) debugf(psb->use_esmtp ? "uses esmtp\n" : "no esmtp\n");
meillo@0 206
meillo@10 207 return TRUE;
meillo@10 208 }
meillo@10 209 return FALSE;
meillo@0 210 }
meillo@0 211
meillo@10 212 static gchar*
meillo@10 213 get_response_arg(gchar * response)
meillo@0 214 {
meillo@10 215 gchar buf[SMTP_BUF_LEN];
meillo@10 216 gchar *p = response, *q = buf;
meillo@0 217
meillo@10 218 while (*p && (*p != '\n') && isspace(*p))
meillo@10 219 p++;
meillo@10 220 if (*p && (*p != '\n')) {
meillo@10 221 while (*p && (*p != '\n') && (*p != '\r') && (q < buf + SMTP_BUF_LEN - 1))
meillo@10 222 *(q++) = *(p++);
meillo@15 223 *q = '\0';
meillo@10 224 return g_strdup(buf);
meillo@10 225 }
meillo@10 226 return NULL;
meillo@0 227 }
meillo@0 228
meillo@10 229 static gboolean
meillo@10 230 check_helo_response(smtp_base * psb)
meillo@0 231 {
meillo@171 232 gchar *ptr;
meillo@0 233
meillo@10 234 if (!check_response(psb, FALSE))
meillo@10 235 return FALSE;
meillo@0 236
meillo@171 237 if (psb->last_code == 220) {
meillo@171 238 logwrite(LOG_NOTICE, "received a 220 greeting after sending EHLO,\n");
meillo@171 239 logwrite(LOG_NOTICE, "please remove `instant_helo' from your route config\n");
meillo@171 240 /* read the next response, cause that's the actual helo response */
meillo@171 241 if (!read_response(psb, SMTP_CMD_TIMEOUT) || !check_response(psb, FALSE)) {
meillo@171 242 return FALSE;
meillo@171 243 }
meillo@171 244 }
meillo@171 245
meillo@171 246 ptr = psb->buffer;
meillo@171 247
meillo@10 248 while (*ptr) {
meillo@10 249 if (strncasecmp(&(ptr[4]), "SIZE", 4) == 0) {
meillo@10 250 gchar *arg;
meillo@10 251 psb->use_size = TRUE;
meillo@10 252 arg = get_response_arg(&(ptr[8]));
meillo@10 253 if (arg) {
meillo@10 254 psb->max_size = atoi(arg);
meillo@10 255 g_free(arg);
meillo@10 256 }
meillo@10 257 }
meillo@0 258
meillo@10 259 if (strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0)
meillo@10 260 psb->use_pipelining = TRUE;
meillo@0 261
meillo@10 262 if (strncasecmp(&(ptr[4]), "AUTH", 4) == 0) {
meillo@10 263 if ((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')) { /* not sure about '\t' */
meillo@10 264 gchar *arg;
meillo@10 265 psb->use_auth = TRUE;
meillo@10 266 arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */
meillo@10 267 if (arg) {
meillo@10 268 psb->auth_names = g_strsplit(arg, " ", 0);
meillo@10 269 g_free(arg);
meillo@10 270
meillo@10 271 DEBUG(4) {
meillo@10 272 gint i = 0;
meillo@114 273 debugf("in check_helo_response()\n");
meillo@10 274 while (psb->auth_names[i]) {
meillo@114 275 debugf(" offered AUTH %s\n", psb->auth_names[i]);
meillo@10 276 i++;
meillo@10 277 }
meillo@10 278 }
meillo@10 279 }
meillo@10 280 }
meillo@10 281 }
meillo@10 282
meillo@10 283 while (*ptr != '\n')
meillo@10 284 ptr++;
meillo@10 285 ptr++;
meillo@0 286 }
meillo@0 287
meillo@10 288 DEBUG(4) {
meillo@114 289 debugf(" %s\n", psb->use_size ? "uses SIZE" : "no size");
meillo@114 290 debugf(" %s\n", psb->use_pipelining ? "uses PIPELINING" : "no pipelining");
meillo@114 291 debugf(" %s\n", psb->use_auth ? "uses AUTH" : "no auth");
meillo@10 292 }
meillo@0 293
meillo@10 294 return TRUE;
meillo@0 295 }
meillo@0 296
meillo@10 297 static gboolean
meillo@10 298 smtp_helo(smtp_base * psb, gchar * helo)
meillo@0 299 {
meillo@10 300 while (TRUE) {
meillo@10 301 if (psb->use_esmtp) {
meillo@10 302 fprintf(psb->out, "EHLO %s\r\n", helo);
meillo@10 303 fflush(psb->out);
meillo@0 304
meillo@10 305 DEBUG(4) debugf("EHLO %s\r\n", helo);
meillo@0 306
meillo@10 307 } else {
meillo@10 308 fprintf(psb->out, "HELO %s\r\n", helo);
meillo@10 309 fflush(psb->out);
meillo@0 310
meillo@10 311 DEBUG(4) debugf("HELO %s\r\n", helo);
meillo@0 312
meillo@10 313 }
meillo@0 314
meillo@10 315 if (!read_response(psb, SMTP_CMD_TIMEOUT))
meillo@10 316 return FALSE;
meillo@10 317
meillo@10 318 if (check_helo_response(psb))
meillo@10 319 return TRUE;
meillo@10 320 else {
meillo@10 321 if (psb->error == smtp_fail) {
meillo@10 322 if (psb->use_esmtp) {
meillo@10 323 /* our guess that server understands EHLO was wrong, try again with HELO */
meillo@10 324 psb->use_esmtp = FALSE;
meillo@10 325 } else {
meillo@10 326 /* what sort of server ist THAT ?! give up... */
meillo@10 327 return FALSE;
meillo@10 328 }
meillo@10 329 } else
meillo@10 330 return FALSE;
meillo@10 331 }
meillo@0 332 }
meillo@0 333 }
meillo@0 334
meillo@10 335 static void
meillo@10 336 smtp_cmd_mailfrom(smtp_base * psb, address * return_path, guint size)
meillo@0 337 {
meillo@10 338 if (psb->use_size) {
meillo@10 339 fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size);
meillo@10 340 fflush(psb->out);
meillo@0 341
meillo@10 342 DEBUG(4) debugf("MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size);
meillo@0 343
meillo@10 344 } else {
meillo@10 345 fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path));
meillo@10 346 fflush(psb->out);
meillo@0 347
meillo@10 348 DEBUG(4) debugf("MAIL FROM:%s\r\n", addr_string(return_path));
meillo@10 349 }
meillo@0 350 }
meillo@0 351
meillo@10 352 static void
meillo@10 353 smtp_cmd_rcptto(smtp_base * psb, address * rcpt)
meillo@0 354 {
meillo@10 355 fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt));
meillo@10 356 fflush(psb->out);
meillo@10 357 DEBUG(4) debugf("RCPT TO:%s\n", addr_string(rcpt));
meillo@0 358 }
meillo@0 359
meillo@10 360 static void
meillo@10 361 send_data_line(smtp_base * psb, gchar * data)
meillo@0 362 {
meillo@10 363 /* According to RFC 821 each line should be terminated with CRLF.
meillo@10 364 Since a dot on a line itself marks the end of data, each line
meillo@10 365 beginning with a dot is prepended with another dot.
meillo@10 366 */
meillo@10 367 gchar *ptr;
meillo@15 368 gboolean new_line = TRUE; /* previous versions assumed that each item was exactly one line.
meillo@15 369 This is no longer the case */
meillo@0 370
meillo@10 371 ptr = data;
meillo@10 372 while (*ptr) {
meillo@10 373 int c = (int) (*ptr);
meillo@119 374 if (c == '.' && new_line) {
meillo@119 375 /* dot-stuffing */
meillo@119 376 putc('.', psb->out);
meillo@119 377 }
meillo@10 378 if (c == '\n') {
meillo@119 379 /* CRLF line terminators */
meillo@10 380 putc('\r', psb->out);
meillo@10 381 putc('\n', psb->out);
meillo@10 382 new_line = TRUE;
meillo@10 383 } else {
meillo@10 384 putc(c, psb->out);
meillo@10 385 new_line = FALSE;
meillo@10 386 }
meillo@10 387 ptr++;
meillo@10 388 }
meillo@0 389 }
meillo@0 390
meillo@10 391 static void
meillo@10 392 send_header(smtp_base * psb, GList * hdr_list)
meillo@0 393 {
meillo@10 394 GList *node;
meillo@10 395 gint num_hdrs = 0;
meillo@0 396
meillo@10 397 /* header */
meillo@10 398 if (hdr_list) {
meillo@10 399 foreach(hdr_list, node) {
meillo@10 400 if (node->data) {
meillo@10 401 header *hdr = (header *) (node->data);
meillo@10 402 if (hdr->header) {
meillo@10 403 send_data_line(psb, hdr->header);
meillo@10 404 num_hdrs++;
meillo@10 405 }
meillo@10 406 }
meillo@10 407 }
meillo@0 408 }
meillo@0 409
meillo@10 410 /* empty line separating headers from data: */
meillo@10 411 putc('\r', psb->out);
meillo@10 412 putc('\n', psb->out);
meillo@0 413
meillo@10 414 DEBUG(4) debugf("sent %d headers\n", num_hdrs);
meillo@0 415 }
meillo@0 416
meillo@10 417 static void
meillo@10 418 send_data(smtp_base * psb, message * msg)
meillo@0 419 {
meillo@10 420 GList *node;
meillo@10 421 gint num_lines = 0;
meillo@0 422
meillo@10 423 /* data */
meillo@10 424 if (msg->data_list) {
meillo@10 425 for (node = g_list_first(msg->data_list); node; node = g_list_next(node)) {
meillo@10 426 if (node->data) {
meillo@10 427 send_data_line(psb, node->data);
meillo@10 428 num_lines++;
meillo@10 429 }
meillo@10 430 }
meillo@10 431 }
meillo@0 432
meillo@10 433 DEBUG(4) debugf("sent %d lines of data\n", num_lines);
meillo@0 434
meillo@10 435 fprintf(psb->out, ".\r\n");
meillo@10 436 fflush(psb->out);
meillo@0 437 }
meillo@0 438
meillo@10 439 void
meillo@10 440 smtp_out_mark_rcpts(smtp_base * psb, GList * rcpt_list)
meillo@0 441 {
meillo@10 442 GList *rcpt_node;
meillo@10 443 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 444 address *rcpt = (address *) (rcpt_node->data);
meillo@0 445
meillo@10 446 addr_unmark_delivered(rcpt);
meillo@0 447
meillo@10 448 if ((psb->error == smtp_trylater) || (psb->error == smtp_timeout) || (psb->error == smtp_eof)) {
meillo@10 449 addr_mark_defered(rcpt);
meillo@10 450 } else {
meillo@10 451 addr_mark_failed(rcpt);
meillo@10 452 }
meillo@10 453 }
meillo@0 454 }
meillo@0 455
meillo@10 456 void
meillo@10 457 smtp_out_log_failure(smtp_base * psb, message * msg)
meillo@0 458 {
meillo@10 459 gchar *err_str;
meillo@0 460
meillo@10 461 if (psb->error == smtp_timeout)
meillo@10 462 err_str = g_strdup("connection timed out.");
meillo@10 463 else if (psb->error == smtp_eof)
meillo@10 464 err_str = g_strdup("connection terminated prematurely.");
meillo@10 465 else if (psb->error == smtp_syntax)
meillo@10 466 err_str = g_strdup_printf("got unexpected response: %s", psb->buffer);
meillo@10 467 else if (psb->error == smtp_cancel)
meillo@10 468 err_str = g_strdup("delivery was canceled.\n");
meillo@10 469 else
meillo@10 470 /* error message should still be in the buffer */
meillo@10 471 err_str = g_strdup_printf("failed: %s\n", psb->buffer);
meillo@0 472
meillo@10 473 if (msg == NULL)
meillo@10 474 logwrite(LOG_NOTICE, "host=%s %s\n", psb->remote_host, err_str);
meillo@10 475 else
meillo@10 476 logwrite(LOG_NOTICE, "%s == host=%s %s\n", msg->uid, psb->remote_host, err_str);
meillo@0 477
meillo@10 478 g_free(err_str);
meillo@0 479 }
meillo@0 480
meillo@10 481 smtp_base*
meillo@10 482 smtp_out_open(gchar * host, gint port, GList * resolve_list)
meillo@0 483 {
meillo@10 484 smtp_base *psb;
meillo@10 485 gint sock;
meillo@10 486 mxip_addr *addr;
meillo@0 487
meillo@10 488 DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host);
meillo@0 489
meillo@10 490 if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) {
meillo@10 491 /* create structure to hold status data: */
meillo@10 492 psb = create_smtpbase(sock);
meillo@10 493 psb->remote_host = addr->name;
meillo@0 494
meillo@10 495 DEBUG(5) {
meillo@10 496 struct sockaddr_in name;
meillo@10 497 int len = sizeof(struct sockaddr);
meillo@10 498 getsockname(sock, (struct sockaddr *) (&name), &len);
meillo@10 499 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
meillo@10 500 }
meillo@10 501 return psb;
meillo@10 502 } else {
meillo@10 503 DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno));
meillo@10 504 }
meillo@0 505
meillo@10 506 return NULL;
meillo@0 507 }
meillo@0 508
meillo@10 509 smtp_base*
meillo@10 510 smtp_out_open_child(gchar * cmd)
meillo@0 511 {
meillo@10 512 smtp_base *psb;
meillo@10 513 gint sock;
meillo@0 514
meillo@10 515 DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd);
meillo@0 516
meillo@10 517 sock = child(cmd);
meillo@0 518
meillo@10 519 if (sock > 0) {
meillo@10 520 psb = create_smtpbase(sock);
meillo@10 521 psb->remote_host = NULL;
meillo@0 522
meillo@10 523 return psb;
meillo@10 524 }
meillo@0 525
meillo@10 526 return NULL;
meillo@0 527 }
meillo@0 528
meillo@10 529 gboolean
meillo@10 530 smtp_out_rset(smtp_base * psb)
meillo@0 531 {
meillo@10 532 gboolean ok;
meillo@0 533
meillo@10 534 fprintf(psb->out, "RSET\r\n");
meillo@10 535 fflush(psb->out);
meillo@10 536 DEBUG(4) debugf("RSET\n");
meillo@0 537
meillo@10 538 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 539 if (check_response(psb, FALSE))
meillo@10 540 return TRUE;
meillo@0 541
meillo@10 542 smtp_out_log_failure(psb, NULL);
meillo@10 543
meillo@10 544 return FALSE;
meillo@0 545 }
meillo@0 546
meillo@0 547 #ifdef ENABLE_AUTH
meillo@0 548
meillo@10 549 static gboolean
meillo@10 550 smtp_out_auth_cram_md5(smtp_base * psb)
meillo@0 551 {
meillo@10 552 gboolean ok = FALSE;
meillo@0 553
meillo@10 554 fprintf(psb->out, "AUTH CRAM-MD5\r\n");
meillo@10 555 fflush(psb->out);
meillo@10 556 DEBUG(4) debugf("AUTH CRAM-MD5\n");
meillo@10 557 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 558 if ((ok = check_response(psb, TRUE))) {
meillo@10 559 gchar *chall64 = get_response_arg(&(psb->buffer[4]));
meillo@10 560 gint chall_size;
meillo@10 561 gchar *chall = base64_decode(chall64, &chall_size);
meillo@10 562 guchar digest[16], *reply64, *reply;
meillo@10 563 gchar digest_string[33];
meillo@10 564 gint i;
meillo@0 565 #ifdef USE_LIB_CRYPTO
meillo@10 566 unsigned int digest_len;
meillo@0 567 #endif
meillo@10 568
meillo@114 569 DEBUG(5) debugf("smtp_out_auth_cram_md5():\n");
meillo@114 570 DEBUG(5) debugf(" encoded challenge = %s\n", chall64);
meillo@114 571 DEBUG(5) debugf(" decoded challenge = %s, size = %d\n", chall, chall_size);
meillo@114 572 DEBUG(5) debugf(" secret = %s\n", psb->auth_secret);
meillo@10 573
meillo@0 574 #ifdef USE_LIB_CRYPTO
meillo@10 575 HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
meillo@0 576 #else
meillo@10 577 hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
meillo@0 578 #endif
meillo@10 579
meillo@10 580 for (i = 0; i < 16; i++)
meillo@10 581 sprintf(&(digest_string[i + i]), "%02x", (unsigned int) (digest[i]));
meillo@15 582 digest_string[32] = '\0';
meillo@10 583
meillo@114 584 DEBUG(5) debugf(" digest = %s\n", digest_string);
meillo@10 585
meillo@10 586 reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
meillo@114 587 DEBUG(5) debugf(" unencoded reply = %s\n", reply);
meillo@10 588
meillo@10 589 reply64 = base64_encode(reply, strlen(reply));
meillo@114 590 DEBUG(5) debugf(" encoded reply = %s\n", reply64);
meillo@10 591
meillo@10 592 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 593 fflush(psb->out);
meillo@114 594 DEBUG(4) debugf(" reply64 = %s\n", reply64);
meillo@10 595
meillo@10 596 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 597 ok = check_response(psb, FALSE);
meillo@10 598
meillo@10 599 g_free(reply64);
meillo@10 600 g_free(reply);
meillo@10 601 g_free(chall);
meillo@10 602 g_free(chall64);
meillo@10 603 }
meillo@10 604 }
meillo@10 605 return ok;
meillo@0 606 }
meillo@0 607
meillo@10 608 static gboolean
meillo@10 609 smtp_out_auth_login(smtp_base * psb)
meillo@0 610 {
meillo@10 611 gboolean ok = FALSE;
meillo@10 612 fprintf(psb->out, "AUTH LOGIN\r\n");
meillo@10 613 fflush(psb->out);
meillo@10 614 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 615 if ((ok = check_response(psb, TRUE))) {
meillo@10 616 gchar *resp64;
meillo@10 617 guchar *resp;
meillo@10 618 gint resp_size;
meillo@10 619 gchar *reply64;
meillo@10 620
meillo@114 621 DEBUG(5) debugf("smtp_out_auth_login():\n");
meillo@10 622 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@114 623 DEBUG(5) debugf(" encoded response = %s\n", resp64);
meillo@10 624 resp = base64_decode(resp64, &resp_size);
meillo@10 625 g_free(resp64);
meillo@114 626 DEBUG(5) debugf(" decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 627 g_free(resp);
meillo@10 628 reply64 = base64_encode(psb->auth_login, strlen(psb->auth_login));
meillo@10 629 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 630 fflush(psb->out);
meillo@10 631 g_free(reply64);
meillo@10 632 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 633 if ((ok = check_response(psb, TRUE))) {
meillo@10 634 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@114 635 DEBUG(5) debugf(" encoded response = %s\n", resp64);
meillo@10 636 resp = base64_decode(resp64, &resp_size);
meillo@10 637 g_free(resp64);
meillo@114 638 DEBUG(5) debugf(" decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 639 g_free(resp);
meillo@10 640 reply64 = base64_encode(psb->auth_secret, strlen(psb->auth_secret));
meillo@10 641 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 642 fflush(psb->out);
meillo@10 643 g_free(reply64);
meillo@10 644 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 645 ok = check_response(psb, FALSE);
meillo@10 646 }
meillo@10 647 }
meillo@10 648 }
meillo@0 649 }
meillo@10 650 return ok;
meillo@0 651 }
meillo@0 652
meillo@10 653 gboolean
meillo@10 654 smtp_out_auth(smtp_base * psb)
meillo@0 655 {
meillo@10 656 gboolean ok = FALSE;
meillo@10 657 gint i = 0;
meillo@10 658 while (psb->auth_names[i]) {
meillo@10 659 if (strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
meillo@10 660 break;
meillo@10 661 i++;
meillo@10 662 }
meillo@10 663 if (psb->auth_names[i]) {
meillo@10 664 if (strcasecmp(psb->auth_name, "cram-md5") == 0) {
meillo@10 665 smtp_out_auth_cram_md5(psb);
meillo@10 666 } else if (strcasecmp(psb->auth_name, "login") == 0) {
meillo@10 667 smtp_out_auth_login(psb);
meillo@10 668 } else {
meillo@10 669 logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name);
meillo@10 670 }
meillo@10 671 } else {
meillo@10 672 logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
meillo@10 673 }
meillo@10 674 return ok;
meillo@0 675 }
meillo@0 676
meillo@0 677 #endif
meillo@0 678
meillo@10 679 gboolean
meillo@171 680 smtp_out_init(smtp_base * psb, gboolean instant_helo)
meillo@0 681 {
meillo@10 682 gboolean ok;
meillo@0 683
meillo@171 684 logwrite(LOG_INFO, "smtp_out_init(): instant_helo:%d\n", instant_helo);
meillo@10 685
meillo@171 686 if (instant_helo) {
meillo@171 687 /* we say hello right away, hence we don't know if
meillo@171 688 ESMTP is supported; we just assume it */
meillo@171 689 psb->use_esmtp = 1;
meillo@171 690 } else {
meillo@171 691 if ((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))) {
meillo@171 692 ok = check_init_response(psb);
meillo@171 693 }
meillo@171 694 if (!ok) {
meillo@171 695 smtp_out_log_failure(psb, NULL);
meillo@171 696 return ok;
meillo@171 697 }
meillo@171 698 }
meillo@171 699
meillo@171 700 if ((ok = smtp_helo(psb, psb->helo_name))) {
meillo@0 701 #ifdef ENABLE_AUTH
meillo@171 702 if (psb->auth_name && psb->use_auth) {
meillo@171 703 /* we completely disregard the response of server here. If
meillo@171 704 authentication fails, the server will complain later
meillo@171 705 anyway. I know, this is not polite... */
meillo@171 706 smtp_out_auth(psb);
meillo@171 707 }
meillo@10 708 #endif
meillo@0 709 }
meillo@10 710 if (!ok)
meillo@10 711 smtp_out_log_failure(psb, NULL);
meillo@10 712 return ok;
meillo@0 713 }
meillo@0 714
meillo@10 715 gint
meillo@10 716 smtp_out_msg(smtp_base * psb, message * msg, address * return_path, GList * rcpt_list, GList * hdr_list)
meillo@0 717 {
meillo@10 718 gint i, size;
meillo@10 719 gboolean ok = TRUE;
meillo@10 720 int rcpt_cnt;
meillo@10 721 int rcpt_accept = 0;
meillo@0 722
meillo@10 723 DEBUG(5) debugf("smtp_out_msg entered\n");
meillo@0 724
meillo@10 725 /* defaults: */
meillo@10 726 if (return_path == NULL)
meillo@10 727 return_path = msg->return_path;
meillo@10 728 if (hdr_list == NULL)
meillo@10 729 hdr_list = msg->hdr_list;
meillo@10 730 if (rcpt_list == NULL)
meillo@10 731 rcpt_list = msg->rcpt_list;
meillo@10 732 rcpt_cnt = g_list_length(rcpt_list);
meillo@0 733
meillo@10 734 size = msg_calc_size(msg, TRUE);
meillo@0 735
meillo@10 736 /* respect maximum size given by server: */
meillo@10 737 if ((psb->max_size > 0) && (size > psb->max_size)) {
meillo@114 738 logwrite(LOG_WARNING, "%s == host=%s message size (%d) > "
meillo@114 739 "fixed maximum message size of server (%d)",
meillo@10 740 msg->uid, psb->remote_host, size, psb->max_size);
meillo@10 741 psb->error = smtp_cancel;
meillo@10 742 ok = FALSE;
meillo@10 743 }
meillo@0 744
meillo@10 745 if (ok) {
meillo@119 746 /* pretend the message is a bit larger,
meillo@119 747 just in case the size calculation is buggy */
meillo@119 748 smtp_cmd_mailfrom(psb, return_path, psb->use_size ? size+SMTP_SIZE_ADD : 0);
meillo@0 749
meillo@10 750 if (!psb->use_pipelining) {
meillo@10 751 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 752 ok = check_response(psb, FALSE);
meillo@10 753 }
meillo@10 754 }
meillo@10 755 if (ok) {
meillo@10 756 GList *rcpt_node;
meillo@10 757 rcpt_accept = 0;
meillo@0 758
meillo@10 759 for (rcpt_node = g_list_first(rcpt_list); rcpt_node != NULL; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 760 address *rcpt = (address *) (rcpt_node->data);
meillo@10 761 smtp_cmd_rcptto(psb, rcpt);
meillo@10 762 if (!psb->use_pipelining) {
meillo@10 763 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 764 if (check_response(psb, FALSE)) {
meillo@10 765 rcpt_accept++;
meillo@10 766 addr_mark_delivered(rcpt);
meillo@10 767 } else {
meillo@10 768 /* if server returned an error for one recp. we
meillo@10 769 may still try the others. But if it is a timeout, eof
meillo@10 770 or unexpected response, it is more serious and we should
meillo@10 771 give up. */
meillo@10 772 if ((psb->error != smtp_trylater) && (psb->error != smtp_fail)) {
meillo@10 773 ok = FALSE;
meillo@10 774 break;
meillo@10 775 } else {
meillo@114 776 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n",
meillo@15 777 msg->uid, addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 778 if (psb->error == smtp_trylater) {
meillo@10 779 addr_mark_defered(rcpt);
meillo@10 780 } else {
meillo@10 781 addr_mark_failed(rcpt);
meillo@10 782 }
meillo@10 783 }
meillo@10 784 } else
meillo@10 785 break;
meillo@10 786 }
meillo@10 787 }
meillo@0 788
meillo@10 789 /* There is no point in going on if no recp.s were accpted.
meillo@10 790 But we can check that at this point only if not pipelining: */
meillo@10 791 ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
meillo@10 792 if (ok) {
meillo@0 793
meillo@10 794 fprintf(psb->out, "DATA\r\n");
meillo@10 795 fflush(psb->out);
meillo@0 796
meillo@10 797 DEBUG(4) debugf("DATA\r\n");
meillo@10 798
meillo@10 799 if (psb->use_pipelining) {
meillo@10 800 /* the first pl'ed command was MAIL FROM
meillo@10 801 the last was DATA, whose response can be handled by the 'normal' code
meillo@10 802 all in between were RCPT TO:
meillo@10 803 */
meillo@10 804 /* response to MAIL FROM: */
meillo@10 805 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 806 if ((ok = check_response(psb, FALSE))) {
meillo@10 807
meillo@10 808 /* response(s) to RCPT TO:
meillo@10 809 this is very similar to the sequence above for no pipeline
meillo@10 810 */
meillo@10 811 for (i = 0; i < rcpt_cnt; i++) {
meillo@10 812 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 813 address *rcpt = g_list_nth_data(rcpt_list, i);
meillo@10 814 if (check_response(psb, FALSE)) {
meillo@10 815 rcpt_accept++;
meillo@10 816 addr_mark_delivered(rcpt);
meillo@10 817 } else {
meillo@10 818 /* if server returned an error 4xx or 5xx for one recp. we
meillo@10 819 may still try the others. But if it is a timeout, eof
meillo@10 820 or unexpected response, it is more serious and we
meillo@10 821 should give up. */
meillo@10 822 if ((psb->error != smtp_trylater) &&
meillo@10 823 (psb->error != smtp_fail)) {
meillo@10 824 ok = FALSE;
meillo@10 825 break;
meillo@10 826 } else {
meillo@114 827 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n", msg->uid,
meillo@10 828 addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 829 if (psb->error == smtp_trylater) {
meillo@10 830 addr_mark_defered(rcpt);
meillo@10 831 } else {
meillo@10 832 addr_mark_failed(rcpt);
meillo@10 833 }
meillo@10 834 }
meillo@10 835 }
meillo@10 836 } else {
meillo@10 837 DEBUG(5) debugf("check_response failed after RCPT TO\n");
meillo@10 838 break;
meillo@10 839 }
meillo@10 840 }
meillo@10 841 if (rcpt_accept == 0)
meillo@10 842 ok = FALSE;
meillo@10 843 } else {
meillo@10 844 DEBUG(5) debugf("check_response failed after MAIL FROM\n");
meillo@10 845 }
meillo@10 846 } else {
meillo@10 847 DEBUG(5)
meillo@10 848 debugf("read_response failed after MAIL FROM\n");
meillo@10 849 }
meillo@10 850 }
meillo@10 851
meillo@10 852 /* if(psb->use_pipelining) */
meillo@10 853 /* response to the DATA cmd */
meillo@10 854 if (ok) {
meillo@10 855 if (read_response(psb, SMTP_DATA_TIMEOUT)) {
meillo@10 856 if (check_response(psb, TRUE)) {
meillo@10 857 send_header(psb, hdr_list);
meillo@10 858 send_data(psb, msg);
meillo@10 859
meillo@10 860 if (read_response(psb, SMTP_FINAL_TIMEOUT))
meillo@10 861 ok = check_response(psb, FALSE);
meillo@10 862 }
meillo@10 863 }
meillo@10 864 }
meillo@0 865 }
meillo@10 866 }
meillo@10 867
meillo@10 868 DEBUG(5) {
meillo@114 869 debugf("smtp_out_msg():\n");
meillo@114 870 debugf(" psb->error = %d\n", psb->error);
meillo@114 871 debugf(" ok = %d\n", ok);
meillo@114 872 debugf(" rcpt_accept = %d\n", rcpt_accept);
meillo@10 873 }
meillo@10 874
meillo@10 875 if (psb->error == smtp_ok) {
meillo@10 876 GList *rcpt_node;
meillo@10 877 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 878 address *rcpt = (address *) (rcpt_node->data);
meillo@10 879 if (addr_is_delivered(rcpt))
meillo@10 880 logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n", msg->uid, addr_string(rcpt),
meillo@10 881 psb->remote_host, psb->use_esmtp ? "esmtp" : "smtp");
meillo@0 882 }
meillo@10 883 } else {
meillo@10 884 /* if something went wrong,
meillo@10 885 we have to unmark the rcpts prematurely marked as delivered
meillo@10 886 and mark the status */
meillo@10 887 smtp_out_mark_rcpts(psb, rcpt_list);
meillo@10 888
meillo@10 889 /* log the failure: */
meillo@10 890 smtp_out_log_failure(psb, msg);
meillo@0 891 }
meillo@10 892 return rcpt_accept;
meillo@0 893 }
meillo@0 894
meillo@10 895 gboolean
meillo@10 896 smtp_out_quit(smtp_base * psb)
meillo@0 897 {
meillo@10 898 fprintf(psb->out, "QUIT\r\n");
meillo@10 899 fflush(psb->out);
meillo@0 900
meillo@10 901 DEBUG(4) debugf("QUIT\n");
meillo@0 902
meillo@10 903 signal(SIGALRM, SIG_DFL);
meillo@10 904
meillo@10 905 return TRUE;
meillo@0 906 }
meillo@10 907
meillo@10 908 gint
meillo@10 909 smtp_deliver(gchar * host, gint port, GList * resolve_list, message * msg, address * return_path, GList * rcpt_list)
meillo@0 910 {
meillo@10 911 smtp_base *psb;
meillo@10 912 smtp_error err;
meillo@0 913
meillo@10 914 DEBUG(5) debugf("smtp_deliver entered\n");
meillo@0 915
meillo@10 916 if (return_path == NULL)
meillo@10 917 return_path = msg->return_path;
meillo@0 918
meillo@10 919 if ((psb = smtp_out_open(host, port, resolve_list))) {
meillo@10 920 set_heloname(psb, return_path->domain, TRUE);
meillo@10 921 /* initiate connection, send message and quit: */
meillo@171 922 if (smtp_out_init(psb, FALSE)) {
meillo@10 923 smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
meillo@10 924 if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater)
meillo@10 925 || (psb->error == smtp_syntax) || (psb->error == smtp_cancel))
meillo@10 926 smtp_out_quit(psb);
meillo@10 927 }
meillo@10 928
meillo@10 929 err = psb->error;
meillo@10 930 destroy_smtpbase(psb);
meillo@10 931
meillo@10 932 return err;
meillo@10 933 }
meillo@10 934 return -1;
meillo@0 935 }