masqmail-0.2

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