masqmail-0.2

annotate src/smtp_out.c @ 162:52c82d755215

replaced the MD5 implementation with the one of Solar Designer Until now, the sample code of RFC 1321 was used. It had an ugly license. Now we use the implementation of Solar Designer, which is in the Public Domain. http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
author meillo@marmaro.de
date Sun, 18 Jul 2010 22:01:04 +0200
parents 1e2fd87d58ea
children 349518b940db
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@10 234 gchar *ptr = psb->buffer;
meillo@0 235
meillo@10 236 if (!check_response(psb, FALSE))
meillo@10 237 return FALSE;
meillo@0 238
meillo@10 239 while (*ptr) {
meillo@10 240 if (strncasecmp(&(ptr[4]), "SIZE", 4) == 0) {
meillo@10 241 gchar *arg;
meillo@10 242 psb->use_size = TRUE;
meillo@10 243 arg = get_response_arg(&(ptr[8]));
meillo@10 244 if (arg) {
meillo@10 245 psb->max_size = atoi(arg);
meillo@10 246 g_free(arg);
meillo@10 247 }
meillo@10 248 }
meillo@0 249
meillo@10 250 if (strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0)
meillo@10 251 psb->use_pipelining = TRUE;
meillo@0 252
meillo@10 253 if (strncasecmp(&(ptr[4]), "AUTH", 4) == 0) {
meillo@10 254 if ((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')) { /* not sure about '\t' */
meillo@10 255 gchar *arg;
meillo@10 256 psb->use_auth = TRUE;
meillo@10 257 arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */
meillo@10 258 if (arg) {
meillo@10 259 psb->auth_names = g_strsplit(arg, " ", 0);
meillo@10 260 g_free(arg);
meillo@10 261
meillo@10 262 DEBUG(4) {
meillo@10 263 gint i = 0;
meillo@114 264 debugf("in check_helo_response()\n");
meillo@10 265 while (psb->auth_names[i]) {
meillo@114 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@114 280 debugf(" %s\n", psb->use_size ? "uses SIZE" : "no size");
meillo@114 281 debugf(" %s\n", psb->use_pipelining ? "uses PIPELINING" : "no pipelining");
meillo@114 282 debugf(" %s\n", psb->use_auth ? "uses AUTH" : "no auth");
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@119 365 if (c == '.' && new_line) {
meillo@119 366 /* dot-stuffing */
meillo@119 367 putc('.', psb->out);
meillo@119 368 }
meillo@10 369 if (c == '\n') {
meillo@119 370 /* CRLF line terminators */
meillo@10 371 putc('\r', psb->out);
meillo@10 372 putc('\n', psb->out);
meillo@10 373 new_line = TRUE;
meillo@10 374 } else {
meillo@10 375 putc(c, psb->out);
meillo@10 376 new_line = FALSE;
meillo@10 377 }
meillo@10 378 ptr++;
meillo@10 379 }
meillo@0 380 }
meillo@0 381
meillo@10 382 static void
meillo@10 383 send_header(smtp_base * psb, GList * hdr_list)
meillo@0 384 {
meillo@10 385 GList *node;
meillo@10 386 gint num_hdrs = 0;
meillo@0 387
meillo@10 388 /* header */
meillo@10 389 if (hdr_list) {
meillo@10 390 foreach(hdr_list, node) {
meillo@10 391 if (node->data) {
meillo@10 392 header *hdr = (header *) (node->data);
meillo@10 393 if (hdr->header) {
meillo@10 394 send_data_line(psb, hdr->header);
meillo@10 395 num_hdrs++;
meillo@10 396 }
meillo@10 397 }
meillo@10 398 }
meillo@0 399 }
meillo@0 400
meillo@10 401 /* empty line separating headers from data: */
meillo@10 402 putc('\r', psb->out);
meillo@10 403 putc('\n', psb->out);
meillo@0 404
meillo@10 405 DEBUG(4) debugf("sent %d headers\n", num_hdrs);
meillo@0 406 }
meillo@0 407
meillo@10 408 static void
meillo@10 409 send_data(smtp_base * psb, message * msg)
meillo@0 410 {
meillo@10 411 GList *node;
meillo@10 412 gint num_lines = 0;
meillo@0 413
meillo@10 414 /* data */
meillo@10 415 if (msg->data_list) {
meillo@10 416 for (node = g_list_first(msg->data_list); node; node = g_list_next(node)) {
meillo@10 417 if (node->data) {
meillo@10 418 send_data_line(psb, node->data);
meillo@10 419 num_lines++;
meillo@10 420 }
meillo@10 421 }
meillo@10 422 }
meillo@0 423
meillo@10 424 DEBUG(4) debugf("sent %d lines of data\n", num_lines);
meillo@0 425
meillo@10 426 fprintf(psb->out, ".\r\n");
meillo@10 427 fflush(psb->out);
meillo@0 428 }
meillo@0 429
meillo@10 430 void
meillo@10 431 smtp_out_mark_rcpts(smtp_base * psb, GList * rcpt_list)
meillo@0 432 {
meillo@10 433 GList *rcpt_node;
meillo@10 434 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 435 address *rcpt = (address *) (rcpt_node->data);
meillo@0 436
meillo@10 437 addr_unmark_delivered(rcpt);
meillo@0 438
meillo@10 439 if ((psb->error == smtp_trylater) || (psb->error == smtp_timeout) || (psb->error == smtp_eof)) {
meillo@10 440 addr_mark_defered(rcpt);
meillo@10 441 } else {
meillo@10 442 addr_mark_failed(rcpt);
meillo@10 443 }
meillo@10 444 }
meillo@0 445 }
meillo@0 446
meillo@10 447 void
meillo@10 448 smtp_out_log_failure(smtp_base * psb, message * msg)
meillo@0 449 {
meillo@10 450 gchar *err_str;
meillo@0 451
meillo@10 452 if (psb->error == smtp_timeout)
meillo@10 453 err_str = g_strdup("connection timed out.");
meillo@10 454 else if (psb->error == smtp_eof)
meillo@10 455 err_str = g_strdup("connection terminated prematurely.");
meillo@10 456 else if (psb->error == smtp_syntax)
meillo@10 457 err_str = g_strdup_printf("got unexpected response: %s", psb->buffer);
meillo@10 458 else if (psb->error == smtp_cancel)
meillo@10 459 err_str = g_strdup("delivery was canceled.\n");
meillo@10 460 else
meillo@10 461 /* error message should still be in the buffer */
meillo@10 462 err_str = g_strdup_printf("failed: %s\n", psb->buffer);
meillo@0 463
meillo@10 464 if (msg == NULL)
meillo@10 465 logwrite(LOG_NOTICE, "host=%s %s\n", psb->remote_host, err_str);
meillo@10 466 else
meillo@10 467 logwrite(LOG_NOTICE, "%s == host=%s %s\n", msg->uid, psb->remote_host, err_str);
meillo@0 468
meillo@10 469 g_free(err_str);
meillo@0 470 }
meillo@0 471
meillo@10 472 smtp_base*
meillo@10 473 smtp_out_open(gchar * host, gint port, GList * resolve_list)
meillo@0 474 {
meillo@10 475 smtp_base *psb;
meillo@10 476 gint sock;
meillo@10 477 mxip_addr *addr;
meillo@0 478
meillo@10 479 DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host);
meillo@0 480
meillo@10 481 if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) {
meillo@10 482 /* create structure to hold status data: */
meillo@10 483 psb = create_smtpbase(sock);
meillo@10 484 psb->remote_host = addr->name;
meillo@0 485
meillo@10 486 DEBUG(5) {
meillo@10 487 struct sockaddr_in name;
meillo@10 488 int len = sizeof(struct sockaddr);
meillo@10 489 getsockname(sock, (struct sockaddr *) (&name), &len);
meillo@10 490 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
meillo@10 491 }
meillo@10 492 return psb;
meillo@10 493 } else {
meillo@10 494 DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno));
meillo@10 495 }
meillo@0 496
meillo@10 497 return NULL;
meillo@0 498 }
meillo@0 499
meillo@10 500 smtp_base*
meillo@10 501 smtp_out_open_child(gchar * cmd)
meillo@0 502 {
meillo@10 503 smtp_base *psb;
meillo@10 504 gint sock;
meillo@0 505
meillo@10 506 DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd);
meillo@0 507
meillo@10 508 sock = child(cmd);
meillo@0 509
meillo@10 510 if (sock > 0) {
meillo@10 511 psb = create_smtpbase(sock);
meillo@10 512 psb->remote_host = NULL;
meillo@0 513
meillo@10 514 return psb;
meillo@10 515 }
meillo@0 516
meillo@10 517 return NULL;
meillo@0 518 }
meillo@0 519
meillo@10 520 gboolean
meillo@10 521 smtp_out_rset(smtp_base * psb)
meillo@0 522 {
meillo@10 523 gboolean ok;
meillo@0 524
meillo@10 525 fprintf(psb->out, "RSET\r\n");
meillo@10 526 fflush(psb->out);
meillo@10 527 DEBUG(4) debugf("RSET\n");
meillo@0 528
meillo@10 529 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 530 if (check_response(psb, FALSE))
meillo@10 531 return TRUE;
meillo@0 532
meillo@10 533 smtp_out_log_failure(psb, NULL);
meillo@10 534
meillo@10 535 return FALSE;
meillo@0 536 }
meillo@0 537
meillo@0 538 #ifdef ENABLE_AUTH
meillo@0 539
meillo@10 540 static gboolean
meillo@10 541 smtp_out_auth_cram_md5(smtp_base * psb)
meillo@0 542 {
meillo@10 543 gboolean ok = FALSE;
meillo@0 544
meillo@10 545 fprintf(psb->out, "AUTH CRAM-MD5\r\n");
meillo@10 546 fflush(psb->out);
meillo@10 547 DEBUG(4) debugf("AUTH CRAM-MD5\n");
meillo@10 548 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 549 if ((ok = check_response(psb, TRUE))) {
meillo@10 550 gchar *chall64 = get_response_arg(&(psb->buffer[4]));
meillo@10 551 gint chall_size;
meillo@10 552 gchar *chall = base64_decode(chall64, &chall_size);
meillo@10 553 guchar digest[16], *reply64, *reply;
meillo@10 554 gchar digest_string[33];
meillo@10 555 gint i;
meillo@0 556 #ifdef USE_LIB_CRYPTO
meillo@10 557 unsigned int digest_len;
meillo@0 558 #endif
meillo@10 559
meillo@114 560 DEBUG(5) debugf("smtp_out_auth_cram_md5():\n");
meillo@114 561 DEBUG(5) debugf(" encoded challenge = %s\n", chall64);
meillo@114 562 DEBUG(5) debugf(" decoded challenge = %s, size = %d\n", chall, chall_size);
meillo@114 563 DEBUG(5) debugf(" secret = %s\n", psb->auth_secret);
meillo@10 564
meillo@0 565 #ifdef USE_LIB_CRYPTO
meillo@10 566 HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
meillo@0 567 #else
meillo@10 568 hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
meillo@0 569 #endif
meillo@10 570
meillo@10 571 for (i = 0; i < 16; i++)
meillo@10 572 sprintf(&(digest_string[i + i]), "%02x", (unsigned int) (digest[i]));
meillo@15 573 digest_string[32] = '\0';
meillo@10 574
meillo@114 575 DEBUG(5) debugf(" digest = %s\n", digest_string);
meillo@10 576
meillo@10 577 reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
meillo@114 578 DEBUG(5) debugf(" unencoded reply = %s\n", reply);
meillo@10 579
meillo@10 580 reply64 = base64_encode(reply, strlen(reply));
meillo@114 581 DEBUG(5) debugf(" encoded reply = %s\n", reply64);
meillo@10 582
meillo@10 583 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 584 fflush(psb->out);
meillo@114 585 DEBUG(4) debugf(" reply64 = %s\n", reply64);
meillo@10 586
meillo@10 587 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 588 ok = check_response(psb, FALSE);
meillo@10 589
meillo@10 590 g_free(reply64);
meillo@10 591 g_free(reply);
meillo@10 592 g_free(chall);
meillo@10 593 g_free(chall64);
meillo@10 594 }
meillo@10 595 }
meillo@10 596 return ok;
meillo@0 597 }
meillo@0 598
meillo@10 599 static gboolean
meillo@10 600 smtp_out_auth_login(smtp_base * psb)
meillo@0 601 {
meillo@10 602 gboolean ok = FALSE;
meillo@10 603 fprintf(psb->out, "AUTH LOGIN\r\n");
meillo@10 604 fflush(psb->out);
meillo@10 605 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 606 if ((ok = check_response(psb, TRUE))) {
meillo@10 607 gchar *resp64;
meillo@10 608 guchar *resp;
meillo@10 609 gint resp_size;
meillo@10 610 gchar *reply64;
meillo@10 611
meillo@114 612 DEBUG(5) debugf("smtp_out_auth_login():\n");
meillo@10 613 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@114 614 DEBUG(5) debugf(" encoded response = %s\n", resp64);
meillo@10 615 resp = base64_decode(resp64, &resp_size);
meillo@10 616 g_free(resp64);
meillo@114 617 DEBUG(5) debugf(" decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 618 g_free(resp);
meillo@10 619 reply64 = base64_encode(psb->auth_login, strlen(psb->auth_login));
meillo@10 620 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 621 fflush(psb->out);
meillo@10 622 g_free(reply64);
meillo@10 623 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 624 if ((ok = check_response(psb, TRUE))) {
meillo@10 625 resp64 = get_response_arg(&(psb->buffer[4]));
meillo@114 626 DEBUG(5) debugf(" encoded response = %s\n", resp64);
meillo@10 627 resp = base64_decode(resp64, &resp_size);
meillo@10 628 g_free(resp64);
meillo@114 629 DEBUG(5) debugf(" decoded response = %s, size = %d\n", resp, resp_size);
meillo@10 630 g_free(resp);
meillo@10 631 reply64 = base64_encode(psb->auth_secret, strlen(psb->auth_secret));
meillo@10 632 fprintf(psb->out, "%s\r\n", reply64);
meillo@10 633 fflush(psb->out);
meillo@10 634 g_free(reply64);
meillo@10 635 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 636 ok = check_response(psb, FALSE);
meillo@10 637 }
meillo@10 638 }
meillo@10 639 }
meillo@0 640 }
meillo@10 641 return ok;
meillo@0 642 }
meillo@0 643
meillo@10 644 gboolean
meillo@10 645 smtp_out_auth(smtp_base * psb)
meillo@0 646 {
meillo@10 647 gboolean ok = FALSE;
meillo@10 648 gint i = 0;
meillo@10 649 while (psb->auth_names[i]) {
meillo@10 650 if (strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
meillo@10 651 break;
meillo@10 652 i++;
meillo@10 653 }
meillo@10 654 if (psb->auth_names[i]) {
meillo@10 655 if (strcasecmp(psb->auth_name, "cram-md5") == 0) {
meillo@10 656 smtp_out_auth_cram_md5(psb);
meillo@10 657 } else if (strcasecmp(psb->auth_name, "login") == 0) {
meillo@10 658 smtp_out_auth_login(psb);
meillo@10 659 } else {
meillo@10 660 logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name);
meillo@10 661 }
meillo@10 662 } else {
meillo@10 663 logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
meillo@10 664 }
meillo@10 665 return ok;
meillo@0 666 }
meillo@0 667
meillo@0 668 #endif
meillo@0 669
meillo@10 670 gboolean
meillo@10 671 smtp_out_init(smtp_base * psb)
meillo@0 672 {
meillo@10 673 gboolean ok;
meillo@0 674
meillo@10 675 if ((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))) {
meillo@10 676 if ((ok = check_init_response(psb))) {
meillo@10 677
meillo@10 678 if ((ok = smtp_helo(psb, psb->helo_name))) {
meillo@0 679 #ifdef ENABLE_AUTH
meillo@10 680 if (psb->auth_name && psb->use_auth) {
meillo@10 681 /* we completely disregard the response of server here. If
meillo@10 682 authentication fails, the server will complain later
meillo@10 683 anyway. I know, this is not polite... */
meillo@10 684 smtp_out_auth(psb);
meillo@10 685 }
meillo@10 686 #endif
meillo@10 687 }
meillo@10 688 }
meillo@0 689 }
meillo@10 690 if (!ok)
meillo@10 691 smtp_out_log_failure(psb, NULL);
meillo@10 692 return ok;
meillo@0 693 }
meillo@0 694
meillo@10 695 gint
meillo@10 696 smtp_out_msg(smtp_base * psb, message * msg, address * return_path, GList * rcpt_list, GList * hdr_list)
meillo@0 697 {
meillo@10 698 gint i, size;
meillo@10 699 gboolean ok = TRUE;
meillo@10 700 int rcpt_cnt;
meillo@10 701 int rcpt_accept = 0;
meillo@0 702
meillo@10 703 DEBUG(5) debugf("smtp_out_msg entered\n");
meillo@0 704
meillo@10 705 /* defaults: */
meillo@10 706 if (return_path == NULL)
meillo@10 707 return_path = msg->return_path;
meillo@10 708 if (hdr_list == NULL)
meillo@10 709 hdr_list = msg->hdr_list;
meillo@10 710 if (rcpt_list == NULL)
meillo@10 711 rcpt_list = msg->rcpt_list;
meillo@10 712 rcpt_cnt = g_list_length(rcpt_list);
meillo@0 713
meillo@10 714 size = msg_calc_size(msg, TRUE);
meillo@0 715
meillo@10 716 /* respect maximum size given by server: */
meillo@10 717 if ((psb->max_size > 0) && (size > psb->max_size)) {
meillo@114 718 logwrite(LOG_WARNING, "%s == host=%s message size (%d) > "
meillo@114 719 "fixed maximum message size of server (%d)",
meillo@10 720 msg->uid, psb->remote_host, size, psb->max_size);
meillo@10 721 psb->error = smtp_cancel;
meillo@10 722 ok = FALSE;
meillo@10 723 }
meillo@0 724
meillo@10 725 if (ok) {
meillo@119 726 /* pretend the message is a bit larger,
meillo@119 727 just in case the size calculation is buggy */
meillo@119 728 smtp_cmd_mailfrom(psb, return_path, psb->use_size ? size+SMTP_SIZE_ADD : 0);
meillo@0 729
meillo@10 730 if (!psb->use_pipelining) {
meillo@10 731 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 732 ok = check_response(psb, FALSE);
meillo@10 733 }
meillo@10 734 }
meillo@10 735 if (ok) {
meillo@10 736 GList *rcpt_node;
meillo@10 737 rcpt_accept = 0;
meillo@0 738
meillo@10 739 for (rcpt_node = g_list_first(rcpt_list); rcpt_node != NULL; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 740 address *rcpt = (address *) (rcpt_node->data);
meillo@10 741 smtp_cmd_rcptto(psb, rcpt);
meillo@10 742 if (!psb->use_pipelining) {
meillo@10 743 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
meillo@10 744 if (check_response(psb, FALSE)) {
meillo@10 745 rcpt_accept++;
meillo@10 746 addr_mark_delivered(rcpt);
meillo@10 747 } else {
meillo@10 748 /* if server returned an error for one recp. we
meillo@10 749 may still try the others. But if it is a timeout, eof
meillo@10 750 or unexpected response, it is more serious and we should
meillo@10 751 give up. */
meillo@10 752 if ((psb->error != smtp_trylater) && (psb->error != smtp_fail)) {
meillo@10 753 ok = FALSE;
meillo@10 754 break;
meillo@10 755 } else {
meillo@114 756 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n",
meillo@15 757 msg->uid, addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 758 if (psb->error == smtp_trylater) {
meillo@10 759 addr_mark_defered(rcpt);
meillo@10 760 } else {
meillo@10 761 addr_mark_failed(rcpt);
meillo@10 762 }
meillo@10 763 }
meillo@10 764 } else
meillo@10 765 break;
meillo@10 766 }
meillo@10 767 }
meillo@0 768
meillo@10 769 /* There is no point in going on if no recp.s were accpted.
meillo@10 770 But we can check that at this point only if not pipelining: */
meillo@10 771 ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
meillo@10 772 if (ok) {
meillo@0 773
meillo@10 774 fprintf(psb->out, "DATA\r\n");
meillo@10 775 fflush(psb->out);
meillo@0 776
meillo@10 777 DEBUG(4) debugf("DATA\r\n");
meillo@10 778
meillo@10 779 if (psb->use_pipelining) {
meillo@10 780 /* the first pl'ed command was MAIL FROM
meillo@10 781 the last was DATA, whose response can be handled by the 'normal' code
meillo@10 782 all in between were RCPT TO:
meillo@10 783 */
meillo@10 784 /* response to MAIL FROM: */
meillo@10 785 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 786 if ((ok = check_response(psb, FALSE))) {
meillo@10 787
meillo@10 788 /* response(s) to RCPT TO:
meillo@10 789 this is very similar to the sequence above for no pipeline
meillo@10 790 */
meillo@10 791 for (i = 0; i < rcpt_cnt; i++) {
meillo@10 792 if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
meillo@10 793 address *rcpt = g_list_nth_data(rcpt_list, i);
meillo@10 794 if (check_response(psb, FALSE)) {
meillo@10 795 rcpt_accept++;
meillo@10 796 addr_mark_delivered(rcpt);
meillo@10 797 } else {
meillo@10 798 /* if server returned an error 4xx or 5xx for one recp. we
meillo@10 799 may still try the others. But if it is a timeout, eof
meillo@10 800 or unexpected response, it is more serious and we
meillo@10 801 should give up. */
meillo@10 802 if ((psb->error != smtp_trylater) &&
meillo@10 803 (psb->error != smtp_fail)) {
meillo@10 804 ok = FALSE;
meillo@10 805 break;
meillo@10 806 } else {
meillo@114 807 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s\n", msg->uid,
meillo@10 808 addr_string(rcpt), psb->remote_host, psb->buffer);
meillo@10 809 if (psb->error == smtp_trylater) {
meillo@10 810 addr_mark_defered(rcpt);
meillo@10 811 } else {
meillo@10 812 addr_mark_failed(rcpt);
meillo@10 813 }
meillo@10 814 }
meillo@10 815 }
meillo@10 816 } else {
meillo@10 817 DEBUG(5) debugf("check_response failed after RCPT TO\n");
meillo@10 818 break;
meillo@10 819 }
meillo@10 820 }
meillo@10 821 if (rcpt_accept == 0)
meillo@10 822 ok = FALSE;
meillo@10 823 } else {
meillo@10 824 DEBUG(5) debugf("check_response failed after MAIL FROM\n");
meillo@10 825 }
meillo@10 826 } else {
meillo@10 827 DEBUG(5)
meillo@10 828 debugf("read_response failed after MAIL FROM\n");
meillo@10 829 }
meillo@10 830 }
meillo@10 831
meillo@10 832 /* if(psb->use_pipelining) */
meillo@10 833 /* response to the DATA cmd */
meillo@10 834 if (ok) {
meillo@10 835 if (read_response(psb, SMTP_DATA_TIMEOUT)) {
meillo@10 836 if (check_response(psb, TRUE)) {
meillo@10 837 send_header(psb, hdr_list);
meillo@10 838 send_data(psb, msg);
meillo@10 839
meillo@10 840 if (read_response(psb, SMTP_FINAL_TIMEOUT))
meillo@10 841 ok = check_response(psb, FALSE);
meillo@10 842 }
meillo@10 843 }
meillo@10 844 }
meillo@0 845 }
meillo@10 846 }
meillo@10 847
meillo@10 848 DEBUG(5) {
meillo@114 849 debugf("smtp_out_msg():\n");
meillo@114 850 debugf(" psb->error = %d\n", psb->error);
meillo@114 851 debugf(" ok = %d\n", ok);
meillo@114 852 debugf(" rcpt_accept = %d\n", rcpt_accept);
meillo@10 853 }
meillo@10 854
meillo@10 855 if (psb->error == smtp_ok) {
meillo@10 856 GList *rcpt_node;
meillo@10 857 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
meillo@10 858 address *rcpt = (address *) (rcpt_node->data);
meillo@10 859 if (addr_is_delivered(rcpt))
meillo@10 860 logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n", msg->uid, addr_string(rcpt),
meillo@10 861 psb->remote_host, psb->use_esmtp ? "esmtp" : "smtp");
meillo@0 862 }
meillo@10 863 } else {
meillo@10 864 /* if something went wrong,
meillo@10 865 we have to unmark the rcpts prematurely marked as delivered
meillo@10 866 and mark the status */
meillo@10 867 smtp_out_mark_rcpts(psb, rcpt_list);
meillo@10 868
meillo@10 869 /* log the failure: */
meillo@10 870 smtp_out_log_failure(psb, msg);
meillo@0 871 }
meillo@10 872 return rcpt_accept;
meillo@0 873 }
meillo@0 874
meillo@10 875 gboolean
meillo@10 876 smtp_out_quit(smtp_base * psb)
meillo@0 877 {
meillo@10 878 fprintf(psb->out, "QUIT\r\n");
meillo@10 879 fflush(psb->out);
meillo@0 880
meillo@10 881 DEBUG(4) debugf("QUIT\n");
meillo@0 882
meillo@10 883 signal(SIGALRM, SIG_DFL);
meillo@10 884
meillo@10 885 return TRUE;
meillo@0 886 }
meillo@10 887
meillo@10 888 gint
meillo@10 889 smtp_deliver(gchar * host, gint port, GList * resolve_list, message * msg, address * return_path, GList * rcpt_list)
meillo@0 890 {
meillo@10 891 smtp_base *psb;
meillo@10 892 smtp_error err;
meillo@0 893
meillo@10 894 DEBUG(5) debugf("smtp_deliver entered\n");
meillo@0 895
meillo@10 896 if (return_path == NULL)
meillo@10 897 return_path = msg->return_path;
meillo@0 898
meillo@10 899 if ((psb = smtp_out_open(host, port, resolve_list))) {
meillo@10 900 set_heloname(psb, return_path->domain, TRUE);
meillo@10 901 /* initiate connection, send message and quit: */
meillo@10 902 if (smtp_out_init(psb)) {
meillo@10 903 smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
meillo@10 904 if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater)
meillo@10 905 || (psb->error == smtp_syntax) || (psb->error == smtp_cancel))
meillo@10 906 smtp_out_quit(psb);
meillo@10 907 }
meillo@10 908
meillo@10 909 err = psb->error;
meillo@10 910 destroy_smtpbase(psb);
meillo@10 911
meillo@10 912 return err;
meillo@10 913 }
meillo@10 914 return -1;
meillo@0 915 }