masqmail

annotate src/smtp_out.c @ 216:84bf7a6b6ccd

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