masqmail

annotate src/smtp_out.c @ 75:257a9e6d1a8e

fixed correct processing of mails with data lines longer 4096 chars Mail messages with lines longer than 4096 chars were already read correctly, i.e. the spool files were correct. This commit fixes the reading of spool files with long lines. The old behavior was that the message body was truncated right before the first line longer 4096 chars. The number comes from MAX_DATALINE.
author meillo@marmaro.de
date Wed, 16 Jun 2010 19:06:34 +0200
parents 26e34ae9a3e3
children a80ebfa16cd5
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@10 265 while (psb->auth_names[i]) {
meillo@10 266 debugf("offered AUTH %s\n", psb->auth_names[i]);
meillo@10 267 i++;
meillo@10 268 }
meillo@10 269 }
meillo@10 270 }
meillo@10 271 }
meillo@10 272 }
meillo@10 273
meillo@10 274 while (*ptr != '\n')
meillo@10 275 ptr++;
meillo@10 276 ptr++;
meillo@0 277 }
meillo@0 278
meillo@10 279 DEBUG(4) {
meillo@10 280 debugf(psb->use_size ? "uses SIZE\n" : "no size\n");
meillo@10 281 debugf(psb->use_pipelining ? "uses PIPELINING\n" : "no pipelining\n");
meillo@10 282 debugf(psb->use_auth ? "uses AUTH\n" : "no auth\n");
meillo@10 283 }
meillo@0 284
meillo@10 285 return TRUE;
meillo@0 286 }
meillo@0 287
meillo@10 288 static gboolean
meillo@10 289 smtp_helo(smtp_base * psb, gchar * helo)
meillo@0 290 {
meillo@10 291 while (TRUE) {
meillo@10 292 if (psb->use_esmtp) {
meillo@10 293 fprintf(psb->out, "EHLO %s\r\n", helo);
meillo@10 294 fflush(psb->out);
meillo@0 295
meillo@10 296 DEBUG(4) debugf("EHLO %s\r\n", helo);
meillo@0 297
meillo@10 298 } else {
meillo@10 299 fprintf(psb->out, "HELO %s\r\n", helo);
meillo@10 300 fflush(psb->out);
meillo@0 301
meillo@10 302 DEBUG(4) debugf("HELO %s\r\n", helo);
meillo@0 303
meillo@10 304 }
meillo@0 305
meillo@10 306 if (!read_response(psb, SMTP_CMD_TIMEOUT))
meillo@10 307 return FALSE;
meillo@10 308
meillo@10 309 if (check_helo_response(psb))
meillo@10 310 return TRUE;
meillo@10 311 else {
meillo@10 312 if (psb->error == smtp_fail) {
meillo@10 313 if (psb->use_esmtp) {
meillo@10 314 /* our guess that server understands EHLO was wrong, try again with HELO */
meillo@10 315 psb->use_esmtp = FALSE;
meillo@10 316 } else {
meillo@10 317 /* what sort of server ist THAT ?! give up... */
meillo@10 318 return FALSE;
meillo@10 319 }
meillo@10 320 } else
meillo@10 321 return FALSE;
meillo@10 322 }
meillo@0 323 }
meillo@0 324 }
meillo@0 325
meillo@10 326 static void
meillo@10 327 smtp_cmd_mailfrom(smtp_base * psb, address * return_path, guint size)
meillo@0 328 {
meillo@10 329 if (psb->use_size) {
meillo@10 330 fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size);
meillo@10 331 fflush(psb->out);
meillo@0 332
meillo@10 333 DEBUG(4) debugf("MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size);
meillo@0 334
meillo@10 335 } else {
meillo@10 336 fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path));
meillo@10 337 fflush(psb->out);
meillo@0 338
meillo@10 339 DEBUG(4) debugf("MAIL FROM:%s\r\n", addr_string(return_path));
meillo@10 340 }
meillo@0 341 }
meillo@0 342
meillo@10 343 static void
meillo@10 344 smtp_cmd_rcptto(smtp_base * psb, address * rcpt)
meillo@0 345 {
meillo@10 346 fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt));
meillo@10 347 fflush(psb->out);
meillo@10 348 DEBUG(4) debugf("RCPT TO:%s\n", addr_string(rcpt));
meillo@0 349 }
meillo@0 350
meillo@10 351 static void
meillo@10 352 send_data_line(smtp_base * psb, gchar * data)
meillo@0 353 {
meillo@10 354 /* According to RFC 821 each line should be terminated with CRLF.
meillo@10 355 Since a dot on a line itself marks the end of data, each line
meillo@10 356 beginning with a dot is prepended with another dot.
meillo@10 357 */
meillo@10 358 gchar *ptr;
meillo@15 359 gboolean new_line = TRUE; /* previous versions assumed that each item was exactly one line.
meillo@15 360 This is no longer the case */
meillo@0 361
meillo@10 362 ptr = data;
meillo@10 363 while (*ptr) {
meillo@10 364 int c = (int) (*ptr);
meillo@10 365 if (c == '.')
meillo@10 366 if (new_line)
meillo@10 367 putc('.', psb->out);
meillo@10 368 if (c == '\n') {
meillo@10 369 putc('\r', psb->out);
meillo@10 370 putc('\n', psb->out);
meillo@10 371 new_line = TRUE;
meillo@10 372 } else {
meillo@10 373 putc(c, psb->out);
meillo@10 374 new_line = FALSE;
meillo@10 375 }
meillo@10 376 ptr++;
meillo@10 377 }
meillo@0 378 }
meillo@0 379
meillo@10 380 static void
meillo@10 381 send_header(smtp_base * psb, GList * hdr_list)
meillo@0 382 {
meillo@10 383 GList *node;
meillo@10 384 gint num_hdrs = 0;
meillo@0 385
meillo@10 386 /* header */
meillo@10 387 if (hdr_list) {
meillo@10 388 foreach(hdr_list, node) {
meillo@10 389 if (node->data) {
meillo@10 390 header *hdr = (header *) (node->data);
meillo@10 391 if (hdr->header) {
meillo@10 392 send_data_line(psb, hdr->header);
meillo@10 393 num_hdrs++;
meillo@10 394 }
meillo@10 395 }
meillo@10 396 }
meillo@0 397 }
meillo@0 398
meillo@10 399 /* empty line separating headers from data: */
meillo@10 400 putc('\r', psb->out);
meillo@10 401 putc('\n', psb->out);
meillo@0 402
meillo@10 403 DEBUG(4) debugf("sent %d headers\n", num_hdrs);
meillo@0 404 }
meillo@0 405
meillo@10 406 static void
meillo@10 407 send_data(smtp_base * psb, message * msg)
meillo@0 408 {
meillo@10 409 GList *node;
meillo@10 410 gint num_lines = 0;
meillo@0 411
meillo@10 412 /* data */
meillo@10 413 if (msg->data_list) {
meillo@10 414 for (node = g_list_first(msg->data_list); node; node = g_list_next(node)) {
meillo@10 415 if (node->data) {
meillo@10 416 send_data_line(psb, node->data);
meillo@10 417 num_lines++;
meillo@10 418 }
meillo@10 419 }
meillo@10 420 }
meillo@0 421
meillo@10 422 DEBUG(4) debugf("sent %d lines of data\n", num_lines);
meillo@0 423
meillo@10 424 fprintf(psb->out, ".\r\n");
meillo@10 425 fflush(psb->out);
meillo@0 426 }
meillo@0 427
meillo@10 428 void
meillo@10 429 smtp_out_mark_rcpts(smtp_base * psb, GList * rcpt_list)
meillo@0 430 {
meillo@10 431 GList *rcpt_node;
meillo@10 432 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 433 address *rcpt = (address *) (rcpt_node->data);
meillo@0 434
meillo@10 435 addr_unmark_delivered(rcpt);
meillo@0 436
meillo@10 437 if ((psb->error == smtp_trylater) || (psb->error == smtp_timeout) || (psb->error == smtp_eof)) {
meillo@10 438 addr_mark_defered(rcpt);
meillo@10 439 } else {
meillo@10 440 addr_mark_failed(rcpt);
meillo@10 441 }
meillo@10 442 }
meillo@0 443 }
meillo@0 444
meillo@10 445 void
meillo@10 446 smtp_out_log_failure(smtp_base * psb, message * msg)
meillo@0 447 {
meillo@10 448 gchar *err_str;
meillo@0 449
meillo@10 450 if (psb->error == smtp_timeout)
meillo@10 451 err_str = g_strdup("connection timed out.");
meillo@10 452 else if (psb->error == smtp_eof)
meillo@10 453 err_str = g_strdup("connection terminated prematurely.");
meillo@10 454 else if (psb->error == smtp_syntax)
meillo@10 455 err_str = g_strdup_printf("got unexpected response: %s", psb->buffer);
meillo@10 456 else if (psb->error == smtp_cancel)
meillo@10 457 err_str = g_strdup("delivery was canceled.\n");
meillo@10 458 else
meillo@10 459 /* error message should still be in the buffer */
meillo@10 460 err_str = g_strdup_printf("failed: %s\n", psb->buffer);
meillo@0 461
meillo@10 462 if (msg == NULL)
meillo@10 463 logwrite(LOG_NOTICE, "host=%s %s\n", psb->remote_host, err_str);
meillo@10 464 else
meillo@10 465 logwrite(LOG_NOTICE, "%s == host=%s %s\n", msg->uid, psb->remote_host, err_str);
meillo@0 466
meillo@10 467 g_free(err_str);
meillo@0 468 }
meillo@0 469
meillo@10 470 smtp_base*
meillo@10 471 smtp_out_open(gchar * host, gint port, GList * resolve_list)
meillo@0 472 {
meillo@10 473 smtp_base *psb;
meillo@10 474 gint sock;
meillo@10 475 mxip_addr *addr;
meillo@0 476
meillo@10 477 DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host);
meillo@0 478
meillo@10 479 if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) {
meillo@10 480 /* create structure to hold status data: */
meillo@10 481 psb = create_smtpbase(sock);
meillo@10 482 psb->remote_host = addr->name;
meillo@0 483
meillo@10 484 DEBUG(5) {
meillo@10 485 struct sockaddr_in name;
meillo@10 486 int len = sizeof(struct sockaddr);
meillo@10 487 getsockname(sock, (struct sockaddr *) (&name), &len);
meillo@10 488 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
meillo@10 489 }
meillo@10 490 return psb;
meillo@10 491 } else {
meillo@10 492 DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno));
meillo@10 493 }
meillo@0 494
meillo@10 495 return NULL;
meillo@0 496 }
meillo@0 497
meillo@10 498 smtp_base*
meillo@10 499 smtp_out_open_child(gchar * cmd)
meillo@0 500 {
meillo@10 501 smtp_base *psb;
meillo@10 502 gint sock;
meillo@0 503
meillo@10 504 DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd);
meillo@0 505
meillo@10 506 sock = child(cmd);
meillo@0 507
meillo@10 508 if (sock > 0) {
meillo@10 509 psb = create_smtpbase(sock);
meillo@10 510 psb->remote_host = NULL;
meillo@0 511
meillo@10 512 return psb;
meillo@10 513 }
meillo@0 514
meillo@10 515 return NULL;
meillo@0 516 }
meillo@0 517
meillo@10 518 gboolean
meillo@10 519 smtp_out_rset(smtp_base * psb)
meillo@0 520 {
meillo@10 521 gboolean ok;
meillo@0 522
meillo@10 523 fprintf(psb->out, "RSET\r\n");
meillo@10 524 fflush(psb->out);
meillo@10 525 DEBUG(4) debugf("RSET\n");
meillo@0 526
meillo@10 527 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 528 if (check_response(psb, FALSE))
meillo@10 529 return TRUE;
meillo@0 530
meillo@10 531 smtp_out_log_failure(psb, NULL);
meillo@10 532
meillo@10 533 return FALSE;
meillo@0 534 }
meillo@0 535
meillo@0 536 #ifdef ENABLE_AUTH
meillo@0 537
meillo@10 538 static gboolean
meillo@10 539 smtp_out_auth_cram_md5(smtp_base * psb)
meillo@0 540 {
meillo@10 541 gboolean ok = FALSE;
meillo@0 542
meillo@10 543 fprintf(psb->out, "AUTH CRAM-MD5\r\n");
meillo@10 544 fflush(psb->out);
meillo@10 545 DEBUG(4) debugf("AUTH CRAM-MD5\n");
meillo@10 546 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 547 if ((ok = check_response(psb, TRUE))) {
meillo@10 548 gchar *chall64 = get_response_arg(&(psb->buffer[4]));
meillo@10 549 gint chall_size;
meillo@10 550 gchar *chall = base64_decode(chall64, &chall_size);
meillo@10 551 guchar digest[16], *reply64, *reply;
meillo@10 552 gchar digest_string[33];
meillo@10 553 gint i;
meillo@0 554 #ifdef USE_LIB_CRYPTO
meillo@10 555 unsigned int digest_len;
meillo@0 556 #endif
meillo@10 557
meillo@10 558 DEBUG(5) debugf("encoded challenge = %s\n", chall64);
meillo@10 559 DEBUG(5) debugf("decoded challenge = %s, size = %d\n", chall, chall_size);
meillo@10 560
meillo@10 561 DEBUG(5) debugf("secret = %s\n", psb->auth_secret);
meillo@10 562
meillo@0 563 #ifdef USE_LIB_CRYPTO
meillo@10 564 HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
meillo@0 565 #else
meillo@10 566 hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
meillo@0 567 #endif
meillo@10 568
meillo@10 569 for (i = 0; i < 16; i++)
meillo@10 570 sprintf(&(digest_string[i + i]), "%02x", (unsigned int) (digest[i]));
meillo@15 571 digest_string[32] = '\0';
meillo@10 572
meillo@10 573 DEBUG(5) debugf("digest = %s\n", digest_string);
meillo@10 574
meillo@10 575 reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
meillo@10 576 DEBUG(5) debugf("unencoded reply = %s\n", reply);
meillo@10 577
meillo@10 578 reply64 = base64_encode(reply, strlen(reply));
meillo@10 579 DEBUG(5) debugf("encoded reply = %s\n", reply64);
meillo@10 580
meillo@10 581 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 582 fflush(psb->out);
meillo@10 583 DEBUG(4) debugf("%s\n", reply64);
meillo@10 584
meillo@10 585 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 586 ok = check_response(psb, FALSE);
meillo@10 587
meillo@10 588 g_free(reply64);
meillo@10 589 g_free(reply);
meillo@10 590 g_free(chall);
meillo@10 591 g_free(chall64);
meillo@10 592 }
meillo@10 593 }
meillo@10 594 return ok;
meillo@0 595 }
meillo@0 596
meillo@10 597 static gboolean
meillo@10 598 smtp_out_auth_login(smtp_base * psb)
meillo@0 599 {
meillo@10 600 gboolean ok = FALSE;
meillo@10 601 fprintf(psb->out, "AUTH LOGIN\r\n");
meillo@10 602 fflush(psb->out);
meillo@10 603 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 604 if ((ok = check_response(psb, TRUE))) {
meillo@10 605 gchar *resp64;
meillo@10 606 guchar *resp;
meillo@10 607 gint resp_size;
meillo@10 608 gchar *reply64;
meillo@10 609
meillo@10 610 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@10 611 DEBUG(5) debugf("encoded response = %s\n", resp64);
meillo@10 612 resp = base64_decode(resp64, &resp_size);
meillo@10 613 g_free(resp64);
meillo@10 614 DEBUG(5) debugf("decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 615 g_free(resp);
meillo@10 616 reply64 = base64_encode(psb->auth_login, strlen(psb->auth_login));
meillo@10 617 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 618 fflush(psb->out);
meillo@10 619 g_free(reply64);
meillo@10 620 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 621 if ((ok = check_response(psb, TRUE))) {
meillo@10 622 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@10 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@10 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_secret, strlen(psb->auth_secret));
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 ok = check_response(psb, FALSE);
meillo@10 634 }
meillo@10 635 }
meillo@10 636 }
meillo@0 637 }
meillo@10 638 return ok;
meillo@0 639 }
meillo@0 640
meillo@10 641 gboolean
meillo@10 642 smtp_out_auth(smtp_base * psb)
meillo@0 643 {
meillo@10 644 gboolean ok = FALSE;
meillo@10 645 gint i = 0;
meillo@10 646 while (psb->auth_names[i]) {
meillo@10 647 if (strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
meillo@10 648 break;
meillo@10 649 i++;
meillo@10 650 }
meillo@10 651 if (psb->auth_names[i]) {
meillo@10 652 if (strcasecmp(psb->auth_name, "cram-md5") == 0) {
meillo@10 653 smtp_out_auth_cram_md5(psb);
meillo@10 654 } else if (strcasecmp(psb->auth_name, "login") == 0) {
meillo@10 655 smtp_out_auth_login(psb);
meillo@10 656 } else {
meillo@10 657 logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name);
meillo@10 658 }
meillo@10 659 } else {
meillo@10 660 logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
meillo@10 661 }
meillo@10 662 return ok;
meillo@0 663 }
meillo@0 664
meillo@0 665 #endif
meillo@0 666
meillo@10 667 gboolean
meillo@10 668 smtp_out_init(smtp_base * psb)
meillo@0 669 {
meillo@10 670 gboolean ok;
meillo@0 671
meillo@10 672 if ((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))) {
meillo@10 673 if ((ok = check_init_response(psb))) {
meillo@10 674
meillo@10 675 if ((ok = smtp_helo(psb, psb->helo_name))) {
meillo@0 676 #ifdef ENABLE_AUTH
meillo@10 677 if (psb->auth_name && psb->use_auth) {
meillo@10 678 /* we completely disregard the response of server here. If
meillo@10 679 authentication fails, the server will complain later
meillo@10 680 anyway. I know, this is not polite... */
meillo@10 681 smtp_out_auth(psb);
meillo@10 682 }
meillo@10 683 #endif
meillo@10 684 }
meillo@10 685 }
meillo@0 686 }
meillo@10 687 if (!ok)
meillo@10 688 smtp_out_log_failure(psb, NULL);
meillo@10 689 return ok;
meillo@0 690 }
meillo@0 691
meillo@10 692 gint
meillo@10 693 smtp_out_msg(smtp_base * psb, message * msg, address * return_path, GList * rcpt_list, GList * hdr_list)
meillo@0 694 {
meillo@10 695 gint i, size;
meillo@10 696 gboolean ok = TRUE;
meillo@10 697 int rcpt_cnt;
meillo@10 698 int rcpt_accept = 0;
meillo@0 699
meillo@10 700 DEBUG(5) debugf("smtp_out_msg entered\n");
meillo@0 701
meillo@10 702 /* defaults: */
meillo@10 703 if (return_path == NULL)
meillo@10 704 return_path = msg->return_path;
meillo@10 705 if (hdr_list == NULL)
meillo@10 706 hdr_list = msg->hdr_list;
meillo@10 707 if (rcpt_list == NULL)
meillo@10 708 rcpt_list = msg->rcpt_list;
meillo@10 709 rcpt_cnt = g_list_length(rcpt_list);
meillo@0 710
meillo@10 711 size = msg_calc_size(msg, TRUE);
meillo@0 712
meillo@10 713 /* respect maximum size given by server: */
meillo@10 714 if ((psb->max_size > 0) && (size > psb->max_size)) {
meillo@10 715 logwrite(LOG_WARNING, "%s == host=%s message size (%d) > fixed maximum message size of server (%d)",
meillo@10 716 msg->uid, psb->remote_host, size, psb->max_size);
meillo@10 717 psb->error = smtp_cancel;
meillo@10 718 ok = FALSE;
meillo@10 719 }
meillo@0 720
meillo@10 721 if (ok) {
meillo@10 722 smtp_cmd_mailfrom(psb, return_path, psb->use_size ? size + SMTP_SIZE_ADD : 0);
meillo@0 723
meillo@10 724 if (!psb->use_pipelining) {
meillo@10 725 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 726 ok = check_response(psb, FALSE);
meillo@10 727 }
meillo@10 728 }
meillo@10 729 if (ok) {
meillo@10 730 GList *rcpt_node;
meillo@10 731 rcpt_accept = 0;
meillo@0 732
meillo@10 733 for (rcpt_node = g_list_first(rcpt_list); rcpt_node != NULL; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 734 address *rcpt = (address *) (rcpt_node->data);
meillo@10 735 smtp_cmd_rcptto(psb, rcpt);
meillo@10 736 if (!psb->use_pipelining) {
meillo@10 737 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 738 if (check_response(psb, FALSE)) {
meillo@10 739 rcpt_accept++;
meillo@10 740 addr_mark_delivered(rcpt);
meillo@10 741 } else {
meillo@10 742 /* if server returned an error for one recp. we
meillo@10 743 may still try the others. But if it is a timeout, eof
meillo@10 744 or unexpected response, it is more serious and we should
meillo@10 745 give up. */
meillo@10 746 if ((psb->error != smtp_trylater) && (psb->error != smtp_fail)) {
meillo@10 747 ok = FALSE;
meillo@10 748 break;
meillo@10 749 } else {
meillo@15 750 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s",
meillo@15 751 msg->uid, addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 752 if (psb->error == smtp_trylater) {
meillo@10 753 addr_mark_defered(rcpt);
meillo@10 754 } else {
meillo@10 755 addr_mark_failed(rcpt);
meillo@10 756 }
meillo@10 757 }
meillo@10 758 } else
meillo@10 759 break;
meillo@10 760 }
meillo@10 761 }
meillo@0 762
meillo@10 763 /* There is no point in going on if no recp.s were accpted.
meillo@10 764 But we can check that at this point only if not pipelining: */
meillo@10 765 ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
meillo@10 766 if (ok) {
meillo@0 767
meillo@10 768 fprintf(psb->out, "DATA\r\n");
meillo@10 769 fflush(psb->out);
meillo@0 770
meillo@10 771 DEBUG(4) debugf("DATA\r\n");
meillo@10 772
meillo@10 773 if (psb->use_pipelining) {
meillo@10 774 /* the first pl'ed command was MAIL FROM
meillo@10 775 the last was DATA, whose response can be handled by the 'normal' code
meillo@10 776 all in between were RCPT TO:
meillo@10 777 */
meillo@10 778 /* response to MAIL FROM: */
meillo@10 779 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 780 if ((ok = check_response(psb, FALSE))) {
meillo@10 781
meillo@10 782 /* response(s) to RCPT TO:
meillo@10 783 this is very similar to the sequence above for no pipeline
meillo@10 784 */
meillo@10 785 for (i = 0; i < rcpt_cnt; i++) {
meillo@10 786 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 787 address *rcpt = g_list_nth_data(rcpt_list, i);
meillo@10 788 if (check_response(psb, FALSE)) {
meillo@10 789 rcpt_accept++;
meillo@10 790 addr_mark_delivered(rcpt);
meillo@10 791 } else {
meillo@10 792 /* if server returned an error 4xx or 5xx for one recp. we
meillo@10 793 may still try the others. But if it is a timeout, eof
meillo@10 794 or unexpected response, it is more serious and we
meillo@10 795 should give up. */
meillo@10 796 if ((psb->error != smtp_trylater) &&
meillo@10 797 (psb->error != smtp_fail)) {
meillo@10 798 ok = FALSE;
meillo@10 799 break;
meillo@10 800 } else {
meillo@10 801 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s", msg->uid,
meillo@10 802 addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 803 if (psb->error == smtp_trylater) {
meillo@10 804 addr_mark_defered(rcpt);
meillo@10 805 } else {
meillo@10 806 addr_mark_failed(rcpt);
meillo@10 807 }
meillo@10 808 }
meillo@10 809 }
meillo@10 810 } else {
meillo@10 811 DEBUG(5) debugf("check_response failed after RCPT TO\n");
meillo@10 812 break;
meillo@10 813 }
meillo@10 814 }
meillo@10 815 if (rcpt_accept == 0)
meillo@10 816 ok = FALSE;
meillo@10 817 } else {
meillo@10 818 DEBUG(5) debugf("check_response failed after MAIL FROM\n");
meillo@10 819 }
meillo@10 820 } else {
meillo@10 821 DEBUG(5)
meillo@10 822 debugf("read_response failed after MAIL FROM\n");
meillo@10 823 }
meillo@10 824 }
meillo@10 825
meillo@10 826 /* if(psb->use_pipelining) */
meillo@10 827 /* response to the DATA cmd */
meillo@10 828 if (ok) {
meillo@10 829 if (read_response(psb, SMTP_DATA_TIMEOUT)) {
meillo@10 830 if (check_response(psb, TRUE)) {
meillo@10 831 send_header(psb, hdr_list);
meillo@10 832 send_data(psb, msg);
meillo@10 833
meillo@10 834 if (read_response(psb, SMTP_FINAL_TIMEOUT))
meillo@10 835 ok = check_response(psb, FALSE);
meillo@10 836 }
meillo@10 837 }
meillo@10 838 }
meillo@0 839 }
meillo@10 840 }
meillo@10 841
meillo@10 842 DEBUG(5) {
meillo@10 843 debugf("psb->error = %d\n", psb->error);
meillo@10 844 debugf("ok = %d\n", ok);
meillo@10 845 debugf("rcpt_accept = %d\n", rcpt_accept);
meillo@10 846 }
meillo@10 847
meillo@10 848 if (psb->error == smtp_ok) {
meillo@10 849 GList *rcpt_node;
meillo@10 850 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 851 address *rcpt = (address *) (rcpt_node->data);
meillo@10 852 if (addr_is_delivered(rcpt))
meillo@10 853 logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n", msg->uid, addr_string(rcpt),
meillo@10 854 psb->remote_host, psb->use_esmtp ? "esmtp" : "smtp");
meillo@0 855 }
meillo@10 856 } else {
meillo@10 857 /* if something went wrong,
meillo@10 858 we have to unmark the rcpts prematurely marked as delivered
meillo@10 859 and mark the status */
meillo@10 860 smtp_out_mark_rcpts(psb, rcpt_list);
meillo@10 861
meillo@10 862 /* log the failure: */
meillo@10 863 smtp_out_log_failure(psb, msg);
meillo@0 864 }
meillo@10 865 return rcpt_accept;
meillo@0 866 }
meillo@0 867
meillo@10 868 gboolean
meillo@10 869 smtp_out_quit(smtp_base * psb)
meillo@0 870 {
meillo@10 871 fprintf(psb->out, "QUIT\r\n");
meillo@10 872 fflush(psb->out);
meillo@0 873
meillo@10 874 DEBUG(4) debugf("QUIT\n");
meillo@0 875
meillo@10 876 signal(SIGALRM, SIG_DFL);
meillo@10 877
meillo@10 878 return TRUE;
meillo@0 879 }
meillo@10 880
meillo@10 881 gint
meillo@10 882 smtp_deliver(gchar * host, gint port, GList * resolve_list, message * msg, address * return_path, GList * rcpt_list)
meillo@0 883 {
meillo@10 884 smtp_base *psb;
meillo@10 885 smtp_error err;
meillo@0 886
meillo@10 887 DEBUG(5) debugf("smtp_deliver entered\n");
meillo@0 888
meillo@10 889 if (return_path == NULL)
meillo@10 890 return_path = msg->return_path;
meillo@0 891
meillo@10 892 if ((psb = smtp_out_open(host, port, resolve_list))) {
meillo@10 893 set_heloname(psb, return_path->domain, TRUE);
meillo@10 894 /* initiate connection, send message and quit: */
meillo@10 895 if (smtp_out_init(psb)) {
meillo@10 896 smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
meillo@10 897 if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater)
meillo@10 898 || (psb->error == smtp_syntax) || (psb->error == smtp_cancel))
meillo@10 899 smtp_out_quit(psb);
meillo@10 900 }
meillo@10 901
meillo@10 902 err = psb->error;
meillo@10 903 destroy_smtpbase(psb);
meillo@10 904
meillo@10 905 return err;
meillo@10 906 }
meillo@10 907 return -1;
meillo@0 908 }