masqmail-0.2

diff src/smtp_out.c @ 0:08114f7dcc23

this is masqmail-0.2.21 from oliver kurth
author meillo@marmaro.de
date Fri, 26 Sep 2008 17:05:23 +0200
parents
children 26e34ae9a3e3
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/smtp_out.c	Fri Sep 26 17:05:23 2008 +0200
     1.3 @@ -0,0 +1,918 @@
     1.4 +/* smtp_out.c, Copyright (C) 1999-2001 Oliver Kurth,
     1.5 + *
     1.6 + * This program is free software; you can redistribute it and/or modify
     1.7 + * it under the terms of the GNU General Public License as published by
     1.8 + * the Free Software Foundation; either version 2 of the License, or
     1.9 + * (at your option) any later version.
    1.10 + * 
    1.11 + * This program is distributed in the hope that it will be useful,
    1.12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.14 + * GNU General Public License for more details.
    1.15 + *
    1.16 + * You should have received a copy of the GNU General Public License
    1.17 + * along with this program; if not, write to the Free Software
    1.18 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    1.19 + */
    1.20 +
    1.21 +/*
    1.22 + send bugs to: kurth@innominate.de
    1.23 +*/
    1.24 +
    1.25 +/*
    1.26 +  I always forget these rfc numbers:
    1.27 +  RFC 821  (SMTP)
    1.28 +  RFC 1869 (ESMTP)
    1.29 +  RFC 1870 (ESMTP SIZE)
    1.30 +  RFC 2197 (ESMTP PIPELINE)
    1.31 +  RFC 2554 (ESMTP AUTH)
    1.32 +*/
    1.33 +
    1.34 +#include "masqmail.h"
    1.35 +#include "smtp_out.h"
    1.36 +#include "readsock.h"
    1.37 +
    1.38 +#ifdef ENABLE_AUTH
    1.39 +
    1.40 +#ifdef USE_LIB_CRYPTO
    1.41 +#include <openssl/hmac.h>
    1.42 +#include <openssl/md5.h>
    1.43 +#include <openssl/evp.h>
    1.44 +#else
    1.45 +#include "md5/global.h"
    1.46 +#include "md5/md5.h"
    1.47 +#include "md5/hmac_md5.h"
    1.48 +#endif
    1.49 +
    1.50 +#include "base64/base64.h"
    1.51 +#endif
    1.52 +
    1.53 +void destroy_smtpbase(smtp_base *psb)
    1.54 +{
    1.55 +  fclose(psb->in);
    1.56 +  fclose(psb->out);
    1.57 +
    1.58 +  close(psb->sock);
    1.59 +
    1.60 +  if(psb->helo_name) g_free(psb->helo_name);
    1.61 +  if(psb->buffer) g_free(psb->buffer);
    1.62 +  if(psb->auth_names) g_strfreev(psb->auth_names);
    1.63 +
    1.64 +  if(psb->auth_name) g_free(psb->auth_name);
    1.65 +  if(psb->auth_login) g_free(psb->auth_login);
    1.66 +  if(psb->auth_secret) g_free(psb->auth_secret);
    1.67 +}
    1.68 +
    1.69 +gchar *set_heloname(smtp_base *psb, gchar *default_name, gboolean do_correct)
    1.70 +{
    1.71 +  struct sockaddr_in sname;
    1.72 +  int len = sizeof(struct sockaddr_in);
    1.73 +  struct hostent *host_entry;
    1.74 +
    1.75 +  if(do_correct){
    1.76 +    getsockname(psb->sock, (struct sockaddr *)(&sname), &len);
    1.77 +    DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr));
    1.78 +    host_entry =
    1.79 +      gethostbyaddr((const char *)&(sname.sin_addr),
    1.80 +		    sizeof(sname.sin_addr), AF_INET);
    1.81 +    if(host_entry){
    1.82 +      psb->helo_name = g_strdup(host_entry->h_name);
    1.83 +    }else{
    1.84 +      /* we failed to look up our own name. Instead of giving our local hostname,
    1.85 +	 we may give our IP number to show the server that we are at least
    1.86 +	 willing to be honest. For the really picky ones.*/
    1.87 +      DEBUG(5) debugf("failed to look up own host name.\n");
    1.88 +      psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr));
    1.89 +    }
    1.90 +    DEBUG(5) debugf("helo_name = %s\n", psb->helo_name);
    1.91 +  }
    1.92 +  if(psb->helo_name == NULL){
    1.93 +    psb->helo_name = g_strdup(default_name);
    1.94 +  }
    1.95 +  return psb->helo_name;
    1.96 +} 
    1.97 +
    1.98 +#ifdef ENABLE_AUTH
    1.99 +
   1.100 +gboolean set_auth(smtp_base *psb, gchar *name, gchar *login, gchar *secret)
   1.101 +{
   1.102 +  if((strcasecmp(name, "CRAM-MD5") == 0) ||
   1.103 +     (strcasecmp(name, "LOGIN") == 0)) {
   1.104 +    psb->auth_name = g_strdup(name);
   1.105 +    psb->auth_login = g_strdup(login);
   1.106 +    psb->auth_secret = g_strdup(secret);
   1.107 +    
   1.108 +    return TRUE;
   1.109 +  }
   1.110 +  return FALSE;
   1.111 +}
   1.112 +
   1.113 +#endif
   1.114 +
   1.115 +static
   1.116 +smtp_base *create_smtpbase(gint sock)
   1.117 +{
   1.118 +  gint dup_sock;
   1.119 +
   1.120 +  smtp_base *psb = (smtp_base *)g_malloc(sizeof(smtp_base));
   1.121 +
   1.122 +  psb->sock = sock;
   1.123 +
   1.124 +  psb->use_esmtp = FALSE;
   1.125 +  psb->use_size = FALSE;
   1.126 +  psb->use_pipelining = FALSE;
   1.127 +  psb->use_auth = FALSE;
   1.128 +
   1.129 +  psb->max_size = 0;
   1.130 +  psb->auth_names = NULL;
   1.131 +
   1.132 +  psb->buffer = (gchar *)g_malloc(SMTP_BUF_LEN);
   1.133 +
   1.134 +  dup_sock = dup(sock);
   1.135 +  psb->out = fdopen(sock, "w");
   1.136 +  psb->in = fdopen(dup_sock, "r");
   1.137 +
   1.138 +  psb->error = smtp_ok;
   1.139 +
   1.140 +  psb->helo_name = NULL;
   1.141 +  
   1.142 +  psb->auth_name = psb->auth_login = psb->auth_secret = NULL;
   1.143 +
   1.144 +  return psb;
   1.145 +}
   1.146 +
   1.147 +static
   1.148 +gboolean read_response(smtp_base *psb, int timeout)
   1.149 +{
   1.150 +  gint buf_pos = 0;
   1.151 +  gchar code[5];
   1.152 +  gint i, len;
   1.153 +
   1.154 +  do{
   1.155 +    len = read_sockline(psb->in, &(psb->buffer[buf_pos]),
   1.156 +			SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG);
   1.157 +    if(len == -3){
   1.158 +      psb->error = smtp_timeout;
   1.159 +      return FALSE;
   1.160 +    }
   1.161 +    else if(len == -2){
   1.162 +      psb->error = smtp_syntax;
   1.163 +      return FALSE;
   1.164 +    }
   1.165 +    else if(len == -1){
   1.166 +      psb->error = smtp_eof;
   1.167 +      return FALSE;
   1.168 +    }
   1.169 +    for(i = 0; i < 4; i++)
   1.170 +      code[i] = psb->buffer[buf_pos+i];
   1.171 +    code[i] = 0;
   1.172 +    psb->last_code = atoi(code);
   1.173 +
   1.174 +    buf_pos += len;
   1.175 +
   1.176 +  }while(code[3] == '-');
   1.177 +
   1.178 +  return TRUE;
   1.179 +}
   1.180 +
   1.181 +static
   1.182 +gboolean check_response(smtp_base *psb, gboolean after_data)
   1.183 +{
   1.184 +  char c = psb->buffer[0];
   1.185 +
   1.186 +  if(((c == '2') && !after_data) || ((c == '3') && after_data)){
   1.187 +    psb->error = smtp_ok;
   1.188 +    DEBUG(6) debugf("response OK:'%s' after_date = %d\n", psb->buffer, (int)after_data);
   1.189 +    return TRUE;
   1.190 +  }else{
   1.191 +    if(c == '4')
   1.192 +      psb->error = smtp_trylater;
   1.193 +    else if(c == '5')
   1.194 +      psb->error = smtp_fail;
   1.195 +    else
   1.196 +      psb->error = smtp_syntax;
   1.197 +    DEBUG(6) debugf("response failure:'%s' after_date = %d\n", psb->buffer, (int)after_data);
   1.198 +    return FALSE;
   1.199 +  }
   1.200 +}
   1.201 +
   1.202 +static
   1.203 +gboolean check_init_response(smtp_base *psb)
   1.204 +{
   1.205 +  if(check_response(psb, FALSE)){
   1.206 +    psb->use_esmtp = (strstr(psb->buffer, "ESMTP") != NULL);
   1.207 +
   1.208 +    DEBUG(4) debugf(psb->use_esmtp ? "uses esmtp\n" : "no esmtp\n");
   1.209 +
   1.210 +    return TRUE;
   1.211 +  }
   1.212 +  return FALSE;
   1.213 +}
   1.214 +
   1.215 +static
   1.216 +gchar *get_response_arg(gchar *response)
   1.217 +{
   1.218 +  gchar buf[SMTP_BUF_LEN];
   1.219 +  gchar *p = response, *q = buf;
   1.220 +
   1.221 +  while(*p && (*p != '\n') && isspace(*p)) p++;
   1.222 +  if(*p && (*p != '\n')){
   1.223 +    while(*p && (*p != '\n') && (*p != '\r') && (q < buf+SMTP_BUF_LEN-1)) *(q++) = *(p++);
   1.224 +    *q = 0;
   1.225 +    return g_strdup(buf);
   1.226 +  }
   1.227 +  return NULL;
   1.228 +}
   1.229 +
   1.230 +static
   1.231 +gboolean check_helo_response(smtp_base *psb)
   1.232 +{
   1.233 +  gchar *ptr = psb->buffer;
   1.234 +
   1.235 +  if(!check_response(psb, FALSE))
   1.236 +    return FALSE;
   1.237 +
   1.238 +  while(*ptr){
   1.239 +    if(strncasecmp(&(ptr[4]), "SIZE", 4) == 0){
   1.240 +      gchar *arg;
   1.241 +      psb->use_size = TRUE;
   1.242 +      arg = get_response_arg(&(ptr[8]));
   1.243 +      if(arg){
   1.244 +	psb->max_size = atoi(arg);
   1.245 +	g_free(arg);
   1.246 +      }
   1.247 +    }
   1.248 +
   1.249 +    if(strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0)
   1.250 +      psb->use_pipelining = TRUE;
   1.251 +
   1.252 +    if(strncasecmp(&(ptr[4]), "AUTH", 4) == 0){
   1.253 +      if((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')){ /* not sure about '\t' */
   1.254 +	gchar *arg;
   1.255 +	psb->use_auth = TRUE;
   1.256 +	arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */
   1.257 +	if(arg){
   1.258 +	  psb->auth_names = g_strsplit(arg, " " , 0);
   1.259 +	  g_free(arg);
   1.260 +	  
   1.261 +	  DEBUG(4){
   1.262 +	    gint i = 0;
   1.263 +	    while(psb->auth_names[i]){
   1.264 +	      debugf("offered AUTH %s\n", psb->auth_names[i]);
   1.265 +	      i++;
   1.266 +	    }
   1.267 +	  }
   1.268 +	}
   1.269 +      }
   1.270 +    }
   1.271 +
   1.272 +    while(*ptr != '\n') ptr++;
   1.273 +    ptr++;
   1.274 +  }
   1.275 +
   1.276 +  DEBUG(4){
   1.277 +    debugf(psb->use_size ? "uses SIZE\n" : "no size\n");
   1.278 +    debugf(psb->use_pipelining ? "uses PIPELINING\n" : "no pipelining\n");
   1.279 +    debugf(psb->use_auth ? "uses AUTH\n" : "no auth\n");
   1.280 +  }
   1.281 +
   1.282 +  return TRUE;
   1.283 +}
   1.284 +
   1.285 +static
   1.286 +gboolean smtp_helo(smtp_base *psb, gchar *helo)
   1.287 +{
   1.288 +  while(TRUE){
   1.289 +    if(psb->use_esmtp){
   1.290 +      fprintf(psb->out, "EHLO %s\r\n", helo); fflush(psb->out);
   1.291 +
   1.292 +      DEBUG(4) debugf("EHLO %s\r\n", helo);
   1.293 +
   1.294 +    }else{
   1.295 +      fprintf(psb->out, "HELO %s\r\n", helo); fflush(psb->out);
   1.296 +
   1.297 +      DEBUG(4) debugf("HELO %s\r\n", helo);
   1.298 +
   1.299 +    }
   1.300 +    
   1.301 +    if(!read_response(psb, SMTP_CMD_TIMEOUT))
   1.302 +      return FALSE;
   1.303 +
   1.304 +    if(check_helo_response(psb))
   1.305 +      return TRUE;
   1.306 +    else{
   1.307 +      if(psb->error == smtp_fail){
   1.308 +	if(psb->use_esmtp){
   1.309 +	  /* our guess that server understands EHLO was wrong,
   1.310 +	     try again with HELO
   1.311 +	  */
   1.312 +	  psb->use_esmtp = FALSE;
   1.313 +	}else{
   1.314 +	  /* what sort of server ist THAT ?!
   1.315 +	     give up...
   1.316 +	  */
   1.317 +	  return FALSE;
   1.318 +	}
   1.319 +      }else
   1.320 +	return FALSE;
   1.321 +    }
   1.322 +  }
   1.323 +}
   1.324 +
   1.325 +static
   1.326 +void smtp_cmd_mailfrom(smtp_base *psb, address *return_path, guint size)
   1.327 +{
   1.328 +  if(psb->use_size){
   1.329 +    fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n",
   1.330 +	    addr_string(return_path), size);
   1.331 +    fflush(psb->out);
   1.332 +
   1.333 +    DEBUG(4) debugf("MAIL FROM:%s SIZE=%d\r\n",
   1.334 +		    addr_string(return_path), size);
   1.335 +
   1.336 +  }else{
   1.337 +    fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path));
   1.338 +    fflush(psb->out);
   1.339 +
   1.340 +    DEBUG(4) debugf("MAIL FROM:%s\r\n", addr_string(return_path));
   1.341 +  }
   1.342 +}
   1.343 +
   1.344 +static
   1.345 +void smtp_cmd_rcptto(smtp_base *psb, address *rcpt)
   1.346 +{
   1.347 +  fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt));
   1.348 +  fflush(psb->out);
   1.349 +  DEBUG(4) debugf("RCPT TO:%s\n", addr_string(rcpt));
   1.350 +}
   1.351 +
   1.352 +static
   1.353 +void send_data_line(smtp_base *psb, gchar *data)
   1.354 +{
   1.355 +  /* According to RFC 821 each line should be terminated with CRLF.
   1.356 +     Since a dot on a line itself marks the end of data, each line
   1.357 +     beginning with a dot is prepended with another dot.
   1.358 +  */
   1.359 +  gchar *ptr;
   1.360 +  gboolean new_line = TRUE; /* previous versions assumed that each item was
   1.361 +			       exactly one line. This is no longer the case */
   1.362 +
   1.363 +  ptr = data;
   1.364 +  while(*ptr){
   1.365 +    int c = (int)(*ptr);
   1.366 +    if(c == '.')
   1.367 +      if(new_line)
   1.368 +	putc('.', psb->out);
   1.369 +    if(c == '\n'){
   1.370 +      putc('\r', psb->out);
   1.371 +      putc('\n', psb->out);
   1.372 +      new_line = TRUE;
   1.373 +    }else{
   1.374 +      putc(c, psb->out);
   1.375 +      new_line = FALSE;
   1.376 +    }
   1.377 +    ptr++;
   1.378 +  }
   1.379 +}
   1.380 +
   1.381 +static
   1.382 +void send_header(smtp_base *psb, GList *hdr_list)
   1.383 +{
   1.384 +  GList *node;
   1.385 +  gint num_hdrs = 0;
   1.386 +
   1.387 +  /* header */
   1.388 +  if(hdr_list){
   1.389 +    foreach(hdr_list, node){
   1.390 +      if(node->data){
   1.391 +	header *hdr = (header *)(node->data);
   1.392 +	if(hdr->header){
   1.393 +	  send_data_line(psb, hdr->header);
   1.394 +	  num_hdrs++;
   1.395 +	}
   1.396 +      }
   1.397 +    }
   1.398 +  }
   1.399 +
   1.400 +  /* empty line separating headers from data: */
   1.401 +  putc('\r', psb->out);
   1.402 +  putc('\n', psb->out);
   1.403 +
   1.404 +  DEBUG(4) debugf("sent %d headers\n", num_hdrs);
   1.405 +}
   1.406 +
   1.407 +static
   1.408 +void send_data(smtp_base *psb, message *msg)
   1.409 +{
   1.410 +  GList *node;
   1.411 +  gint num_lines = 0;
   1.412 +
   1.413 +  /* data */
   1.414 +  if(msg->data_list){
   1.415 +    for(node = g_list_first(msg->data_list); node; node = g_list_next(node)){
   1.416 +      if(node->data){
   1.417 +	send_data_line(psb, node->data);
   1.418 +	num_lines++;
   1.419 +      }
   1.420 +    }
   1.421 +  }
   1.422 +
   1.423 +  DEBUG(4) debugf("sent %d lines of data\n", num_lines);
   1.424 +
   1.425 +  fprintf(psb->out, ".\r\n");
   1.426 +  fflush(psb->out);
   1.427 +}
   1.428 +
   1.429 +void smtp_out_mark_rcpts(smtp_base *psb, GList *rcpt_list)
   1.430 +{
   1.431 +  GList *rcpt_node;
   1.432 +  for(rcpt_node = g_list_first(rcpt_list);
   1.433 +      rcpt_node;
   1.434 +      rcpt_node = g_list_next(rcpt_node)){
   1.435 +    address *rcpt = (address *)(rcpt_node->data);
   1.436 +
   1.437 +    addr_unmark_delivered(rcpt);
   1.438 +
   1.439 +    if((psb->error == smtp_trylater) || (psb->error == smtp_timeout) ||
   1.440 +       (psb->error == smtp_eof)){
   1.441 +      addr_mark_defered(rcpt);
   1.442 +    }else{
   1.443 +      addr_mark_failed(rcpt);
   1.444 +    }
   1.445 +  }
   1.446 +}
   1.447 +
   1.448 +void smtp_out_log_failure(smtp_base *psb, message *msg)
   1.449 +{
   1.450 +  gchar *err_str;
   1.451 +
   1.452 +  if(psb->error == smtp_timeout)
   1.453 +    err_str = g_strdup("connection timed out.");
   1.454 +  else if(psb->error == smtp_eof)
   1.455 +    err_str = g_strdup("connection terminated prematurely.");
   1.456 +  else if(psb->error == smtp_syntax)
   1.457 +    err_str = g_strdup_printf("got unexpected response: %s", psb->buffer);
   1.458 +  else if(psb->error == smtp_cancel)
   1.459 +    err_str = g_strdup("delivery was canceled.\n");
   1.460 +  else
   1.461 +    /* error message should still be in the buffer */
   1.462 +    err_str = g_strdup_printf("failed: %s\n", psb->buffer);
   1.463 +
   1.464 +  if(msg == NULL)
   1.465 +    logwrite(LOG_NOTICE, "host=%s %s\n",
   1.466 +	     psb->remote_host, err_str);
   1.467 +  else
   1.468 +    logwrite(LOG_NOTICE, "%s == host=%s %s\n",
   1.469 +	     msg->uid, psb->remote_host, err_str);
   1.470 +
   1.471 +  g_free(err_str);
   1.472 +}
   1.473 +
   1.474 +smtp_base *smtp_out_open(gchar *host, gint port, GList *resolve_list)
   1.475 +{
   1.476 +  smtp_base *psb;
   1.477 +  gint sock;
   1.478 +  mxip_addr *addr;
   1.479 +
   1.480 +  DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host);
   1.481 +
   1.482 +  if((addr = connect_resolvelist(&sock, host, port, resolve_list))){
   1.483 +    /* create structure to hold status data: */
   1.484 +    psb = create_smtpbase(sock);
   1.485 +    psb->remote_host = addr->name;
   1.486 +
   1.487 +    DEBUG(5){
   1.488 +      struct sockaddr_in name;
   1.489 +      int len = sizeof(struct sockaddr);
   1.490 +      getsockname(sock, (struct sockaddr *)(&name), &len);
   1.491 +      debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
   1.492 +    }
   1.493 +    return psb;
   1.494 +  }else{
   1.495 +    DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno));
   1.496 +  }
   1.497 +
   1.498 +  return NULL;
   1.499 +}
   1.500 +
   1.501 +smtp_base *smtp_out_open_child(gchar *cmd)
   1.502 +{
   1.503 +  smtp_base *psb;
   1.504 +  gint sock;
   1.505 +
   1.506 +  DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd);
   1.507 +
   1.508 +  sock = child(cmd);
   1.509 +
   1.510 +  if(sock > 0){
   1.511 +    psb = create_smtpbase(sock);
   1.512 +    psb->remote_host = NULL;
   1.513 +
   1.514 +    return psb;
   1.515 +  }
   1.516 +
   1.517 +  return NULL;
   1.518 +}
   1.519 +
   1.520 +gboolean smtp_out_rset(smtp_base *psb)
   1.521 +{
   1.522 +  gboolean ok;
   1.523 +  
   1.524 +  fprintf(psb->out, "RSET\r\n"); fflush(psb->out);
   1.525 +  DEBUG(4) debugf("RSET\n");
   1.526 +
   1.527 +  if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
   1.528 +    if(check_response(psb, FALSE))
   1.529 +      return TRUE;
   1.530 +
   1.531 +  smtp_out_log_failure(psb, NULL);
   1.532 +
   1.533 +  return FALSE;
   1.534 +}
   1.535 +
   1.536 +#ifdef ENABLE_AUTH
   1.537 +
   1.538 +static
   1.539 +gboolean smtp_out_auth_cram_md5(smtp_base *psb)
   1.540 +{
   1.541 +  gboolean ok = FALSE;
   1.542 +
   1.543 +  fprintf(psb->out, "AUTH CRAM-MD5\r\n"); fflush(psb->out);
   1.544 +  DEBUG(4) debugf("AUTH CRAM-MD5\n");
   1.545 +  if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
   1.546 +    if((ok = check_response(psb, TRUE))){
   1.547 +      gchar *chall64 = get_response_arg(&(psb->buffer[4]));
   1.548 +      gint chall_size;
   1.549 +      gchar *chall = base64_decode(chall64, &chall_size);
   1.550 +      guchar digest[16], *reply64, *reply;
   1.551 +      gchar digest_string[33];
   1.552 +      gint i;
   1.553 +#ifdef USE_LIB_CRYPTO
   1.554 +      unsigned int digest_len;
   1.555 +#endif
   1.556 +      
   1.557 +      DEBUG(5) debugf("encoded challenge = %s\n", chall64);
   1.558 +      DEBUG(5) debugf("decoded challenge = %s, size = %d\n", chall, chall_size);
   1.559 +      
   1.560 +      DEBUG(5) debugf("secret = %s\n", psb->auth_secret);
   1.561 +      
   1.562 +#ifdef USE_LIB_CRYPTO
   1.563 +      HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
   1.564 +#else
   1.565 +      hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
   1.566 +#endif
   1.567 +      
   1.568 +      for(i = 0; i < 16; i++)
   1.569 +	sprintf(&(digest_string[i+i]), "%02x", (unsigned int)(digest[i]));
   1.570 +      digest_string[32] = 0;
   1.571 +      
   1.572 +      DEBUG(5) debugf("digest = %s\n", digest_string);
   1.573 +      
   1.574 +      reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
   1.575 +      DEBUG(5) debugf("unencoded reply = %s\n", reply);
   1.576 +      
   1.577 +      reply64 = base64_encode(reply, strlen(reply));
   1.578 +      DEBUG(5) debugf("encoded reply = %s\n", reply64);
   1.579 +          
   1.580 +      fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out);
   1.581 +      DEBUG(4) debugf("%s\n", reply64);
   1.582 +          
   1.583 +      if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
   1.584 +	ok = check_response(psb, FALSE);
   1.585 +          
   1.586 +      g_free(reply64);
   1.587 +      g_free(reply);
   1.588 +      g_free(chall);
   1.589 +      g_free(chall64);
   1.590 +    }
   1.591 +  }
   1.592 +  return ok;
   1.593 +}
   1.594 +
   1.595 +static
   1.596 +gboolean smtp_out_auth_login(smtp_base *psb)
   1.597 +{
   1.598 +  gboolean ok = FALSE;
   1.599 +  fprintf(psb->out, "AUTH LOGIN\r\n"); fflush(psb->out);
   1.600 +  if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
   1.601 +    if((ok = check_response(psb, TRUE))){
   1.602 +      gchar *resp64;
   1.603 +      guchar *resp;
   1.604 +      gint resp_size;
   1.605 +      gchar *reply64;
   1.606 +      
   1.607 +      resp64 = get_response_arg(&(psb->buffer[4]));
   1.608 +      DEBUG(5) debugf("encoded response = %s\n", resp64);
   1.609 +      resp = base64_decode(resp64, &resp_size);
   1.610 +      g_free(resp64);
   1.611 +      DEBUG(5) debugf("decoded response = %s, size = %d\n", 
   1.612 +                                        resp, resp_size);
   1.613 +      g_free(resp);
   1.614 +      reply64 = base64_encode(psb->auth_login,
   1.615 +			      strlen(psb->auth_login));
   1.616 +      fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out);
   1.617 +      g_free(reply64);
   1.618 +      if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
   1.619 +	if ((ok = check_response(psb, TRUE))) {
   1.620 +	  resp64 = get_response_arg(&(psb->buffer[4]));
   1.621 +	  DEBUG(5) debugf("encoded response = %s\n", resp64);
   1.622 +	  resp = base64_decode(resp64, &resp_size);
   1.623 +	  g_free(resp64);
   1.624 +	  DEBUG(5) debugf("decoded response = %s, size = %d\n", 
   1.625 +			  resp, resp_size);
   1.626 +	  g_free(resp);
   1.627 +	  reply64 = base64_encode(psb->auth_secret,
   1.628 +				  strlen(psb->auth_secret));
   1.629 +	  fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out);
   1.630 +	  g_free(reply64);
   1.631 +	  if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
   1.632 +	    ok = check_response(psb, FALSE);
   1.633 +	}
   1.634 +      }          
   1.635 +    }
   1.636 +  }
   1.637 +  return ok;
   1.638 +}
   1.639 +
   1.640 +gboolean smtp_out_auth(smtp_base *psb)
   1.641 +{
   1.642 +  gboolean ok = FALSE;
   1.643 +  gint i = 0;
   1.644 +  while(psb->auth_names[i]){
   1.645 +    if(strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
   1.646 +      break;
   1.647 +    i++;
   1.648 +  }
   1.649 +  if(psb->auth_names[i]){
   1.650 +    if(strcasecmp(psb->auth_name, "cram-md5") == 0){
   1.651 +      smtp_out_auth_cram_md5(psb);
   1.652 +    }else if(strcasecmp(psb->auth_name, "login") == 0){
   1.653 +      smtp_out_auth_login(psb);
   1.654 +    }else{
   1.655 +      logwrite(LOG_ERR, "auth method %s not supported\n",  psb->auth_name);
   1.656 +    }
   1.657 +  }else{
   1.658 +    logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
   1.659 +  }
   1.660 +  return ok;
   1.661 +}
   1.662 +
   1.663 +#endif
   1.664 +
   1.665 +gboolean smtp_out_init(smtp_base *psb)
   1.666 +{
   1.667 +  gboolean ok;
   1.668 +
   1.669 +  if((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))){
   1.670 +    if((ok = check_init_response(psb))){
   1.671 + 
   1.672 +      if((ok = smtp_helo(psb, psb->helo_name))){
   1.673 +#ifdef ENABLE_AUTH
   1.674 +	if(psb->auth_name && psb->use_auth){
   1.675 +	  /* we completely disregard the response of server here. If
   1.676 +             authentication fails, the server will complain later
   1.677 +             anyway. I know, this is not polite... */
   1.678 +	  smtp_out_auth(psb);
   1.679 +	}
   1.680 +#endif
   1.681 +      }
   1.682 +    }
   1.683 +  }
   1.684 +  if(!ok)
   1.685 +    smtp_out_log_failure(psb, NULL);
   1.686 +  return ok;
   1.687 +}
   1.688 +
   1.689 +gint smtp_out_msg(smtp_base *psb,
   1.690 +		  message *msg, address *return_path, GList *rcpt_list,
   1.691 +		  GList *hdr_list)
   1.692 +{
   1.693 +  gint i, size;
   1.694 +  gboolean ok = TRUE;
   1.695 +  int rcpt_cnt;
   1.696 +  int rcpt_accept = 0;
   1.697 +
   1.698 +  DEBUG(5) debugf("smtp_out_msg entered\n");
   1.699 +
   1.700 +  /* defaults: */
   1.701 +  if(return_path == NULL)
   1.702 +    return_path = msg->return_path;
   1.703 +  if(hdr_list == NULL)
   1.704 +    hdr_list = msg->hdr_list;
   1.705 +  if(rcpt_list == NULL)
   1.706 +    rcpt_list = msg->rcpt_list;
   1.707 +  rcpt_cnt = g_list_length(rcpt_list);
   1.708 +
   1.709 +  size = msg_calc_size(msg, TRUE);
   1.710 +
   1.711 +  /* respect maximum size given by server: */
   1.712 +  if((psb->max_size > 0) && (size > psb->max_size)){
   1.713 +    logwrite(LOG_WARNING,
   1.714 +	     "%s == host=%s message size (%d) > fixed maximum message size of server (%d)",
   1.715 +	     msg->uid, psb->remote_host, size, psb->max_size);
   1.716 +    psb->error = smtp_cancel;
   1.717 +    ok = FALSE;
   1.718 +  }
   1.719 +
   1.720 +  if(ok){
   1.721 +    smtp_cmd_mailfrom(psb, return_path,
   1.722 +		      psb->use_size ? 
   1.723 +		      size + SMTP_SIZE_ADD : 0);
   1.724 +    
   1.725 +    if(!psb->use_pipelining){
   1.726 +      if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
   1.727 +	ok = check_response(psb, FALSE);
   1.728 +    }
   1.729 +  }
   1.730 +  if(ok){
   1.731 +    GList *rcpt_node;
   1.732 +    rcpt_accept = 0;
   1.733 +
   1.734 +    for(rcpt_node = g_list_first(rcpt_list);
   1.735 +	rcpt_node != NULL;
   1.736 +	rcpt_node = g_list_next(rcpt_node)){
   1.737 +      address *rcpt = (address *)(rcpt_node->data);
   1.738 +      smtp_cmd_rcptto(psb, rcpt);
   1.739 +      if(!psb->use_pipelining){
   1.740 +	if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
   1.741 +	  if(check_response(psb, FALSE)){
   1.742 +	    rcpt_accept++;
   1.743 +	    addr_mark_delivered(rcpt);
   1.744 +	  }
   1.745 +	  else{
   1.746 +	    /* if server returned an error for one recp. we
   1.747 +	       may still try the others. But if it is a timeout, eof
   1.748 +	       or unexpected response, it is more serious and we should
   1.749 +	       give up. */
   1.750 +	    if((psb->error != smtp_trylater) &&
   1.751 +	       (psb->error != smtp_fail)){
   1.752 +	      ok = FALSE;
   1.753 +	      break;
   1.754 +	    }else{
   1.755 +	      logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s",
   1.756 +		       msg->uid, addr_string(rcpt),
   1.757 +		       psb->remote_host, psb->buffer);
   1.758 +	      if(psb->error == smtp_trylater){
   1.759 +		addr_mark_defered(rcpt);
   1.760 +	      }else{
   1.761 +		addr_mark_failed(rcpt);
   1.762 +	      }
   1.763 +	    }
   1.764 +	  }
   1.765 +	else
   1.766 +	  break;
   1.767 +      }
   1.768 +    }
   1.769 +
   1.770 +    /* There is no point in going on if no recp.s were accpted.
   1.771 +       But we can check that at this point only if not pipelining: */
   1.772 +    ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
   1.773 +    if(ok){
   1.774 +
   1.775 +      fprintf(psb->out, "DATA\r\n"); fflush(psb->out);
   1.776 +
   1.777 +      DEBUG(4) debugf("DATA\r\n");
   1.778 +	
   1.779 +      if(psb->use_pipelining){
   1.780 +	/* the first pl'ed command was MAIL FROM
   1.781 +	   the last was DATA, whose response can be handled by the 'normal' code
   1.782 +	   all in between were RCPT TO:
   1.783 +	*/
   1.784 +	/* response to MAIL FROM: */
   1.785 +	if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
   1.786 +	  if((ok = check_response(psb, FALSE))){
   1.787 +
   1.788 +	    /* response(s) to RCPT TO:
   1.789 +	       this is very similar to the sequence above for no pipeline
   1.790 +	    */
   1.791 +	    for(i = 0; i < rcpt_cnt; i++){
   1.792 +	      if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
   1.793 +		address *rcpt = g_list_nth_data(rcpt_list, i);
   1.794 +		if(check_response(psb, FALSE)){
   1.795 +		  rcpt_accept++;
   1.796 +		  addr_mark_delivered(rcpt);
   1.797 +		}
   1.798 +		else{
   1.799 +		  /* if server returned an error 4xx or 5xx for one recp. we
   1.800 +		     may still try the others. But if it is a timeout, eof
   1.801 +		     or unexpected response, it is more serious and we
   1.802 +		     should give up. */
   1.803 +		  if((psb->error != smtp_trylater) &&
   1.804 +		     (psb->error != smtp_fail)){
   1.805 +		    ok = FALSE;
   1.806 +		    break;
   1.807 +		  }else{
   1.808 +		    logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s",
   1.809 +			     msg->uid, addr_string(rcpt),
   1.810 +			     psb->remote_host, psb->buffer);
   1.811 +		    if(psb->error == smtp_trylater){
   1.812 +		      addr_mark_defered(rcpt);
   1.813 +		    }else{
   1.814 +		      addr_mark_failed(rcpt);
   1.815 +		    }
   1.816 +		  }
   1.817 +		}
   1.818 +	      }else{
   1.819 +		DEBUG(5) debugf("check_response failed after RCPT TO\n");
   1.820 +		break;
   1.821 +	      }
   1.822 +	    }
   1.823 +	    if(rcpt_accept == 0)
   1.824 +	      ok = FALSE;
   1.825 +	  }else{
   1.826 +	    DEBUG(5) debugf("check_response failed after MAIL FROM\n");
   1.827 +	  }
   1.828 +	}else{
   1.829 +	  DEBUG(5) debugf("read_response failed after MAIL FROM\n");
   1.830 +	}
   1.831 +      } /* if(psb->use_pipelining) */
   1.832 +
   1.833 +      /* response to the DATA cmd */
   1.834 +      if(ok){
   1.835 +	if(read_response(psb, SMTP_DATA_TIMEOUT)){
   1.836 +	  if(check_response(psb, TRUE)){
   1.837 +	    send_header(psb, hdr_list);
   1.838 +	    send_data(psb, msg);
   1.839 +	      
   1.840 +	    if(read_response(psb, SMTP_FINAL_TIMEOUT))
   1.841 +	      ok = check_response(psb, FALSE);
   1.842 +	  }
   1.843 +	}
   1.844 +      }
   1.845 +    }
   1.846 +  }
   1.847 +
   1.848 +  DEBUG(5){
   1.849 +    debugf("psb->error = %d\n", psb->error);
   1.850 +    debugf("ok = %d\n", ok);
   1.851 +    debugf("rcpt_accept = %d\n", rcpt_accept);
   1.852 +  }
   1.853 +
   1.854 +  if(psb->error == smtp_ok){
   1.855 +    GList *rcpt_node;
   1.856 +    for(rcpt_node = g_list_first(rcpt_list);
   1.857 +	rcpt_node;
   1.858 +	rcpt_node = g_list_next(rcpt_node)){
   1.859 +      address *rcpt = (address *)(rcpt_node->data);
   1.860 +      if(addr_is_delivered(rcpt))
   1.861 +	logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n",
   1.862 +		 msg->uid, addr_string(rcpt), psb->remote_host,
   1.863 +		 psb->use_esmtp ? "esmtp" : "smtp");
   1.864 +    }
   1.865 +  }else{
   1.866 +    /* if something went wrong,
   1.867 +       we have to unmark the rcpts prematurely marked as delivered
   1.868 +       and mark the status */
   1.869 +    smtp_out_mark_rcpts(psb, rcpt_list);
   1.870 +
   1.871 +    /* log the failure: */
   1.872 +    smtp_out_log_failure(psb, msg);
   1.873 +  }
   1.874 +  return rcpt_accept;
   1.875 +}
   1.876 +
   1.877 +gboolean smtp_out_quit(smtp_base *psb)
   1.878 +{
   1.879 +  fprintf(psb->out, "QUIT\r\n"); fflush(psb->out);
   1.880 +  
   1.881 +  DEBUG(4) debugf("QUIT\n");
   1.882 +
   1.883 +  signal(SIGALRM, SIG_DFL);
   1.884 +
   1.885 +  return TRUE;
   1.886 +}
   1.887 +  
   1.888 +gint smtp_deliver(gchar *host, gint port, GList *resolve_list,
   1.889 +		  message *msg,
   1.890 +		  address *return_path,
   1.891 +		  GList *rcpt_list)
   1.892 +{
   1.893 +  smtp_base *psb;
   1.894 +  smtp_error err;
   1.895 +
   1.896 +  DEBUG(5) debugf("smtp_deliver entered\n");
   1.897 +
   1.898 +  if(return_path == NULL)
   1.899 +    return_path = msg->return_path;
   1.900 +
   1.901 +  if((psb = smtp_out_open(host, port, resolve_list))){
   1.902 +    set_heloname(psb, return_path->domain, TRUE);
   1.903 +    /* initiate connection, send message and quit: */
   1.904 +    if(smtp_out_init(psb)){
   1.905 +      smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
   1.906 +      if(psb->error == smtp_ok ||
   1.907 +	 (psb->error == smtp_fail) ||
   1.908 +	 (psb->error == smtp_trylater) ||
   1.909 +	 (psb->error == smtp_syntax) ||
   1.910 +	 (psb->error == smtp_cancel))
   1.911 +	
   1.912 +	smtp_out_quit(psb);
   1.913 +    }
   1.914 +    
   1.915 +    err = psb->error;
   1.916 +    destroy_smtpbase(psb);
   1.917 +    
   1.918 +    return err;
   1.919 +  }
   1.920 +  return -1;
   1.921 +}