masqmail

annotate src/smtp_out.c @ 156:ee2afbf92428

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