Logo Search packages:      
Sourcecode: masqmail version File versions

smtp_out.c

/* smtp_out.c, Copyright (C) 1999-2001 Oliver Kurth,
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 send bugs to: kurth@innominate.de
*/

/*
  I always forget these rfc numbers:
  RFC 821  (SMTP)
  RFC 1869 (ESMTP)
  RFC 1870 (ESMTP SIZE)
  RFC 2197 (ESMTP PIPELINE)
  RFC 2554 (ESMTP AUTH)
*/

#include "masqmail.h"
#include "smtp_out.h"
#include "readsock.h"

#ifdef ENABLE_AUTH

#ifdef USE_LIB_CRYPTO
#include <openssl/hmac.h>
#include <openssl/md5.h>
#include <openssl/evp.h>
#else
#include "md5/global.h"
#include "md5/md5.h"
#include "md5/hmac_md5.h"
#endif

#include "base64/base64.h"
#endif

void destroy_smtpbase(smtp_base *psb)
{
  fclose(psb->in);
  fclose(psb->out);

  close(psb->sock);

  if(psb->helo_name) g_free(psb->helo_name);
  if(psb->buffer) g_free(psb->buffer);
  if(psb->auth_names) g_strfreev(psb->auth_names);

  if(psb->auth_name) g_free(psb->auth_name);
  if(psb->auth_login) g_free(psb->auth_login);
  if(psb->auth_secret) g_free(psb->auth_secret);
}

gchar *set_heloname(smtp_base *psb, gchar *default_name, gboolean do_correct)
{
  struct sockaddr_in sname;
  int len = sizeof(struct sockaddr_in);
  struct hostent *host_entry;

  if(do_correct){
    getsockname(psb->sock, (struct sockaddr *)(&sname), &len);
    DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr));
    host_entry =
      gethostbyaddr((const char *)&(sname.sin_addr),
                sizeof(sname.sin_addr), AF_INET);
    if(host_entry){
      psb->helo_name = g_strdup(host_entry->h_name);
    }else{
      /* we failed to look up our own name. Instead of giving our local hostname,
       we may give our IP number to show the server that we are at least
       willing to be honest. For the really picky ones.*/
      DEBUG(5) debugf("failed to look up own host name.\n");
      psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr));
    }
    DEBUG(5) debugf("helo_name = %s\n", psb->helo_name);
  }
  if(psb->helo_name == NULL){
    psb->helo_name = g_strdup(default_name);
  }
  return psb->helo_name;
} 

#ifdef ENABLE_AUTH

gboolean set_auth(smtp_base *psb, gchar *name, gchar *login, gchar *secret)
{
  if((strcasecmp(name, "CRAM-MD5") == 0) ||
     (strcasecmp(name, "LOGIN") == 0)) {
    psb->auth_name = g_strdup(name);
    psb->auth_login = g_strdup(login);
    psb->auth_secret = g_strdup(secret);
    
    return TRUE;
  }
  return FALSE;
}

#endif

static
smtp_base *create_smtpbase(gint sock)
{
  gint dup_sock;

  smtp_base *psb = (smtp_base *)g_malloc(sizeof(smtp_base));

  psb->sock = sock;

  psb->use_esmtp = FALSE;
  psb->use_size = FALSE;
  psb->use_pipelining = FALSE;
  psb->use_auth = FALSE;

  psb->max_size = 0;
  psb->auth_names = NULL;

  psb->buffer = (gchar *)g_malloc(SMTP_BUF_LEN);

  dup_sock = dup(sock);
  psb->out = fdopen(sock, "w");
  psb->in = fdopen(dup_sock, "r");

  psb->error = smtp_ok;

  psb->helo_name = NULL;
  
  psb->auth_name = psb->auth_login = psb->auth_secret = NULL;

  return psb;
}

static
gboolean read_response(smtp_base *psb, int timeout)
{
  gint buf_pos = 0;
  gchar code[5];
  gint i, len;

  do{
    len = read_sockline(psb->in, &(psb->buffer[buf_pos]),
                  SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG);
    if(len == -3){
      psb->error = smtp_timeout;
      return FALSE;
    }
    else if(len == -2){
      psb->error = smtp_syntax;
      return FALSE;
    }
    else if(len == -1){
      psb->error = smtp_eof;
      return FALSE;
    }
    for(i = 0; i < 4; i++)
      code[i] = psb->buffer[buf_pos+i];
    code[i] = 0;
    psb->last_code = atoi(code);

    buf_pos += len;

  }while(code[3] == '-');

  return TRUE;
}

static
gboolean check_response(smtp_base *psb, gboolean after_data)
{
  char c = psb->buffer[0];

  if(((c == '2') && !after_data) || ((c == '3') && after_data)){
    psb->error = smtp_ok;
    DEBUG(6) debugf("response OK:'%s' after_date = %d\n", psb->buffer, (int)after_data);
    return TRUE;
  }else{
    if(c == '4')
      psb->error = smtp_trylater;
    else if(c == '5')
      psb->error = smtp_fail;
    else
      psb->error = smtp_syntax;
    DEBUG(6) debugf("response failure:'%s' after_date = %d\n", psb->buffer, (int)after_data);
    return FALSE;
  }
}

static
gboolean check_init_response(smtp_base *psb)
{
  if(check_response(psb, FALSE)){
    psb->use_esmtp = (strstr(psb->buffer, "ESMTP") != NULL);

    DEBUG(4) debugf(psb->use_esmtp ? "uses esmtp\n" : "no esmtp\n");

    return TRUE;
  }
  return FALSE;
}

static
gchar *get_response_arg(gchar *response)
{
  gchar buf[SMTP_BUF_LEN];
  gchar *p = response, *q = buf;

  while(*p && (*p != '\n') && isspace(*p)) p++;
  if(*p && (*p != '\n')){
    while(*p && (*p != '\n') && (*p != '\r') && (q < buf+SMTP_BUF_LEN-1)) *(q++) = *(p++);
    *q = 0;
    return g_strdup(buf);
  }
  return NULL;
}

static
gboolean check_helo_response(smtp_base *psb)
{
  gchar *ptr = psb->buffer;

  if(!check_response(psb, FALSE))
    return FALSE;

  while(*ptr){
    if(strncasecmp(&(ptr[4]), "SIZE", 4) == 0){
      gchar *arg;
      psb->use_size = TRUE;
      arg = get_response_arg(&(ptr[8]));
      if(arg){
      psb->max_size = atoi(arg);
      g_free(arg);
      }
    }

    if(strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0)
      psb->use_pipelining = TRUE;

    if(strncasecmp(&(ptr[4]), "AUTH", 4) == 0){
      if((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')){ /* not sure about '\t' */
      gchar *arg;
      psb->use_auth = TRUE;
      arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */
      if(arg){
        psb->auth_names = g_strsplit(arg, " " , 0);
        g_free(arg);
        
        DEBUG(4){
          gint i = 0;
          while(psb->auth_names[i]){
            debugf("offered AUTH %s\n", psb->auth_names[i]);
            i++;
          }
        }
      }
      }
    }

    while(*ptr != '\n') ptr++;
    ptr++;
  }

  DEBUG(4){
    debugf(psb->use_size ? "uses SIZE\n" : "no size\n");
    debugf(psb->use_pipelining ? "uses PIPELINING\n" : "no pipelining\n");
    debugf(psb->use_auth ? "uses AUTH\n" : "no auth\n");
  }

  return TRUE;
}

static
gboolean smtp_helo(smtp_base *psb, gchar *helo)
{
  while(TRUE){
    if(psb->use_esmtp){
      fprintf(psb->out, "EHLO %s\r\n", helo); fflush(psb->out);

      DEBUG(4) debugf("EHLO %s\r\n", helo);

    }else{
      fprintf(psb->out, "HELO %s\r\n", helo); fflush(psb->out);

      DEBUG(4) debugf("HELO %s\r\n", helo);

    }
    
    if(!read_response(psb, SMTP_CMD_TIMEOUT))
      return FALSE;

    if(check_helo_response(psb))
      return TRUE;
    else{
      if(psb->error == smtp_fail){
      if(psb->use_esmtp){
        /* our guess that server understands EHLO was wrong,
           try again with HELO
        */
        psb->use_esmtp = FALSE;
      }else{
        /* what sort of server ist THAT ?!
           give up...
        */
        return FALSE;
      }
      }else
      return FALSE;
    }
  }
}

static
void smtp_cmd_mailfrom(smtp_base *psb, address *return_path, guint size)
{
  if(psb->use_size){
    fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n",
          addr_string(return_path), size);
    fflush(psb->out);

    DEBUG(4) debugf("MAIL FROM:%s SIZE=%d\r\n",
                addr_string(return_path), size);

  }else{
    fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path));
    fflush(psb->out);

    DEBUG(4) debugf("MAIL FROM:%s\r\n", addr_string(return_path));
  }
}

static
void smtp_cmd_rcptto(smtp_base *psb, address *rcpt)
{
  fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt));
  fflush(psb->out);
  DEBUG(4) debugf("RCPT TO:%s\n", addr_string(rcpt));
}

static
void send_data_line(smtp_base *psb, gchar *data)
{
  /* According to RFC 821 each line should be terminated with CRLF.
     Since a dot on a line itself marks the end of data, each line
     beginning with a dot is prepended with another dot.
  */
  gchar *ptr;
  gboolean new_line = TRUE; /* previous versions assumed that each item was
                         exactly one line. This is no longer the case */

  ptr = data;
  while(*ptr){
    int c = (int)(*ptr);
    if(c == '.')
      if(new_line)
      putc('.', psb->out);
    if(c == '\n'){
      putc('\r', psb->out);
      putc('\n', psb->out);
      new_line = TRUE;
    }else{
      putc(c, psb->out);
      new_line = FALSE;
    }
    ptr++;
  }
}

static
void send_header(smtp_base *psb, GList *hdr_list)
{
  GList *node;
  gint num_hdrs = 0;

  /* header */
  if(hdr_list){
    foreach(hdr_list, node){
      if(node->data){
      header *hdr = (header *)(node->data);
      if(hdr->header){
        send_data_line(psb, hdr->header);
        num_hdrs++;
      }
      }
    }
  }

  /* empty line separating headers from data: */
  putc('\r', psb->out);
  putc('\n', psb->out);

  DEBUG(4) debugf("sent %d headers\n", num_hdrs);
}

static
void send_data(smtp_base *psb, message *msg)
{
  GList *node;
  gint num_lines = 0;

  /* data */
  if(msg->data_list){
    for(node = g_list_first(msg->data_list); node; node = g_list_next(node)){
      if(node->data){
      send_data_line(psb, node->data);
      num_lines++;
      }
    }
  }

  DEBUG(4) debugf("sent %d lines of data\n", num_lines);

  fprintf(psb->out, ".\r\n");
  fflush(psb->out);
}

void smtp_out_mark_rcpts(smtp_base *psb, GList *rcpt_list)
{
  GList *rcpt_node;
  for(rcpt_node = g_list_first(rcpt_list);
      rcpt_node;
      rcpt_node = g_list_next(rcpt_node)){
    address *rcpt = (address *)(rcpt_node->data);

    addr_unmark_delivered(rcpt);

    if((psb->error == smtp_trylater) || (psb->error == smtp_timeout) ||
       (psb->error == smtp_eof)){
      addr_mark_defered(rcpt);
    }else{
      addr_mark_failed(rcpt);
    }
  }
}

void smtp_out_log_failure(smtp_base *psb, message *msg)
{
  gchar *err_str;

  if(psb->error == smtp_timeout)
    err_str = g_strdup("connection timed out.");
  else if(psb->error == smtp_eof)
    err_str = g_strdup("connection terminated prematurely.");
  else if(psb->error == smtp_syntax)
    err_str = g_strdup_printf("got unexpected response: %s", psb->buffer);
  else if(psb->error == smtp_cancel)
    err_str = g_strdup("delivery was canceled.\n");
  else
    /* error message should still be in the buffer */
    err_str = g_strdup_printf("failed: %s\n", psb->buffer);

  if(msg == NULL)
    logwrite(LOG_NOTICE, "host=%s %s\n",
           psb->remote_host, err_str);
  else
    logwrite(LOG_NOTICE, "%s == host=%s %s\n",
           msg->uid, psb->remote_host, err_str);

  g_free(err_str);
}

smtp_base *smtp_out_open(gchar *host, gint port, GList *resolve_list)
{
  smtp_base *psb;
  gint sock;
  mxip_addr *addr;

  DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host);

  if((addr = connect_resolvelist(&sock, host, port, resolve_list))){
    /* create structure to hold status data: */
    psb = create_smtpbase(sock);
    psb->remote_host = addr->name;

    DEBUG(5){
      struct sockaddr_in name;
      int len = sizeof(struct sockaddr);
      getsockname(sock, (struct sockaddr *)(&name), &len);
      debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
    }
    return psb;
  }else{
    DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno));
  }

  return NULL;
}

smtp_base *smtp_out_open_child(gchar *cmd)
{
  smtp_base *psb;
  gint sock;

  DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd);

  sock = child(cmd);

  if(sock > 0){
    psb = create_smtpbase(sock);
    psb->remote_host = NULL;

    return psb;
  }

  return NULL;
}

gboolean smtp_out_rset(smtp_base *psb)
{
  gboolean ok;
  
  fprintf(psb->out, "RSET\r\n"); fflush(psb->out);
  DEBUG(4) debugf("RSET\n");

  if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
    if(check_response(psb, FALSE))
      return TRUE;

  smtp_out_log_failure(psb, NULL);

  return FALSE;
}

#ifdef ENABLE_AUTH

static
gboolean smtp_out_auth_cram_md5(smtp_base *psb)
{
  gboolean ok = FALSE;

  fprintf(psb->out, "AUTH CRAM-MD5\r\n"); fflush(psb->out);
  DEBUG(4) debugf("AUTH CRAM-MD5\n");
  if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
    if((ok = check_response(psb, TRUE))){
      gchar *chall64 = get_response_arg(&(psb->buffer[4]));
      gint chall_size;
      gchar *chall = base64_decode(chall64, &chall_size);
      guchar digest[16], *reply64, *reply;
      gchar digest_string[33];
      gint i;
#ifdef USE_LIB_CRYPTO
      unsigned int digest_len;
#endif
      
      DEBUG(5) debugf("encoded challenge = %s\n", chall64);
      DEBUG(5) debugf("decoded challenge = %s, size = %d\n", chall, chall_size);
      
      DEBUG(5) debugf("secret = %s\n", psb->auth_secret);
      
#ifdef USE_LIB_CRYPTO
      HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len);
#else
      hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest);
#endif
      
      for(i = 0; i < 16; i++)
      sprintf(&(digest_string[i+i]), "%02x", (unsigned int)(digest[i]));
      digest_string[32] = 0;
      
      DEBUG(5) debugf("digest = %s\n", digest_string);
      
      reply = g_strdup_printf("%s %s", psb->auth_login, digest_string);
      DEBUG(5) debugf("unencoded reply = %s\n", reply);
      
      reply64 = base64_encode(reply, strlen(reply));
      DEBUG(5) debugf("encoded reply = %s\n", reply64);
          
      fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out);
      DEBUG(4) debugf("%s\n", reply64);
          
      if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
      ok = check_response(psb, FALSE);
          
      g_free(reply64);
      g_free(reply);
      g_free(chall);
      g_free(chall64);
    }
  }
  return ok;
}

static
gboolean smtp_out_auth_login(smtp_base *psb)
{
  gboolean ok = FALSE;
  fprintf(psb->out, "AUTH LOGIN\r\n"); fflush(psb->out);
  if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
    if((ok = check_response(psb, TRUE))){
      gchar *resp64;
      guchar *resp;
      gint resp_size;
      gchar *reply64;
      
      resp64 = get_response_arg(&(psb->buffer[4]));
      DEBUG(5) debugf("encoded response = %s\n", resp64);
      resp = base64_decode(resp64, &resp_size);
      g_free(resp64);
      DEBUG(5) debugf("decoded response = %s, size = %d\n", 
                                        resp, resp_size);
      g_free(resp);
      reply64 = base64_encode(psb->auth_login,
                        strlen(psb->auth_login));
      fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out);
      g_free(reply64);
      if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) {
      if ((ok = check_response(psb, TRUE))) {
        resp64 = get_response_arg(&(psb->buffer[4]));
        DEBUG(5) debugf("encoded response = %s\n", resp64);
        resp = base64_decode(resp64, &resp_size);
        g_free(resp64);
        DEBUG(5) debugf("decoded response = %s, size = %d\n", 
                    resp, resp_size);
        g_free(resp);
        reply64 = base64_encode(psb->auth_secret,
                          strlen(psb->auth_secret));
        fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out);
        g_free(reply64);
        if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
          ok = check_response(psb, FALSE);
      }
      }          
    }
  }
  return ok;
}

gboolean smtp_out_auth(smtp_base *psb)
{
  gboolean ok = FALSE;
  gint i = 0;
  while(psb->auth_names[i]){
    if(strcasecmp(psb->auth_names[i], psb->auth_name) == 0)
      break;
    i++;
  }
  if(psb->auth_names[i]){
    if(strcasecmp(psb->auth_name, "cram-md5") == 0){
      smtp_out_auth_cram_md5(psb);
    }else if(strcasecmp(psb->auth_name, "login") == 0){
      smtp_out_auth_login(psb);
    }else{
      logwrite(LOG_ERR, "auth method %s not supported\n",  psb->auth_name);
    }
  }else{
    logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name);
  }
  return ok;
}

#endif

gboolean smtp_out_init(smtp_base *psb)
{
  gboolean ok;

  if((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))){
    if((ok = check_init_response(psb))){
 
      if((ok = smtp_helo(psb, psb->helo_name))){
#ifdef ENABLE_AUTH
      if(psb->auth_name && psb->use_auth){
        /* we completely disregard the response of server here. If
             authentication fails, the server will complain later
             anyway. I know, this is not polite... */
        smtp_out_auth(psb);
      }
#endif
      }
    }
  }
  if(!ok)
    smtp_out_log_failure(psb, NULL);
  return ok;
}

gint smtp_out_msg(smtp_base *psb,
              message *msg, address *return_path, GList *rcpt_list,
              GList *hdr_list)
{
  gint i, size;
  gboolean ok = TRUE;
  int rcpt_cnt;
  int rcpt_accept = 0;

  DEBUG(5) debugf("smtp_out_msg entered\n");

  /* defaults: */
  if(return_path == NULL)
    return_path = msg->return_path;
  if(hdr_list == NULL)
    hdr_list = msg->hdr_list;
  if(rcpt_list == NULL)
    rcpt_list = msg->rcpt_list;
  rcpt_cnt = g_list_length(rcpt_list);

  size = msg_calc_size(msg, TRUE);

  /* respect maximum size given by server: */
  if((psb->max_size > 0) && (size > psb->max_size)){
    logwrite(LOG_WARNING,
           "%s == host=%s message size (%d) > fixed maximum message size of server (%d)",
           msg->uid, psb->remote_host, size, psb->max_size);
    psb->error = smtp_cancel;
    ok = FALSE;
  }

  if(ok){
    smtp_cmd_mailfrom(psb, return_path,
                  psb->use_size ? 
                  size + SMTP_SIZE_ADD : 0);
    
    if(!psb->use_pipelining){
      if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
      ok = check_response(psb, FALSE);
    }
  }
  if(ok){
    GList *rcpt_node;
    rcpt_accept = 0;

    for(rcpt_node = g_list_first(rcpt_list);
      rcpt_node != NULL;
      rcpt_node = g_list_next(rcpt_node)){
      address *rcpt = (address *)(rcpt_node->data);
      smtp_cmd_rcptto(psb, rcpt);
      if(!psb->use_pipelining){
      if((ok = read_response(psb, SMTP_CMD_TIMEOUT)))
        if(check_response(psb, FALSE)){
          rcpt_accept++;
          addr_mark_delivered(rcpt);
        }
        else{
          /* if server returned an error for one recp. we
             may still try the others. But if it is a timeout, eof
             or unexpected response, it is more serious and we should
             give up. */
          if((psb->error != smtp_trylater) &&
             (psb->error != smtp_fail)){
            ok = FALSE;
            break;
          }else{
            logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s",
                   msg->uid, addr_string(rcpt),
                   psb->remote_host, psb->buffer);
            if(psb->error == smtp_trylater){
            addr_mark_defered(rcpt);
            }else{
            addr_mark_failed(rcpt);
            }
          }
        }
      else
        break;
      }
    }

    /* There is no point in going on if no recp.s were accpted.
       But we can check that at this point only if not pipelining: */
    ok = (ok && (psb->use_pipelining || (rcpt_accept > 0)));
    if(ok){

      fprintf(psb->out, "DATA\r\n"); fflush(psb->out);

      DEBUG(4) debugf("DATA\r\n");
      
      if(psb->use_pipelining){
      /* the first pl'ed command was MAIL FROM
         the last was DATA, whose response can be handled by the 'normal' code
         all in between were RCPT TO:
      */
      /* response to MAIL FROM: */
      if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
        if((ok = check_response(psb, FALSE))){

          /* response(s) to RCPT TO:
             this is very similar to the sequence above for no pipeline
          */
          for(i = 0; i < rcpt_cnt; i++){
            if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){
            address *rcpt = g_list_nth_data(rcpt_list, i);
            if(check_response(psb, FALSE)){
              rcpt_accept++;
              addr_mark_delivered(rcpt);
            }
            else{
              /* if server returned an error 4xx or 5xx for one recp. we
                 may still try the others. But if it is a timeout, eof
                 or unexpected response, it is more serious and we
                 should give up. */
              if((psb->error != smtp_trylater) &&
                 (psb->error != smtp_fail)){
                ok = FALSE;
                break;
              }else{
                logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s",
                       msg->uid, addr_string(rcpt),
                       psb->remote_host, psb->buffer);
                if(psb->error == smtp_trylater){
                  addr_mark_defered(rcpt);
                }else{
                  addr_mark_failed(rcpt);
                }
              }
            }
            }else{
            DEBUG(5) debugf("check_response failed after RCPT TO\n");
            break;
            }
          }
          if(rcpt_accept == 0)
            ok = FALSE;
        }else{
          DEBUG(5) debugf("check_response failed after MAIL FROM\n");
        }
      }else{
        DEBUG(5) debugf("read_response failed after MAIL FROM\n");
      }
      } /* if(psb->use_pipelining) */

      /* response to the DATA cmd */
      if(ok){
      if(read_response(psb, SMTP_DATA_TIMEOUT)){
        if(check_response(psb, TRUE)){
          send_header(psb, hdr_list);
          send_data(psb, msg);
            
          if(read_response(psb, SMTP_FINAL_TIMEOUT))
            ok = check_response(psb, FALSE);
        }
      }
      }
    }
  }

  DEBUG(5){
    debugf("psb->error = %d\n", psb->error);
    debugf("ok = %d\n", ok);
    debugf("rcpt_accept = %d\n", rcpt_accept);
  }

  if(psb->error == smtp_ok){
    GList *rcpt_node;
    for(rcpt_node = g_list_first(rcpt_list);
      rcpt_node;
      rcpt_node = g_list_next(rcpt_node)){
      address *rcpt = (address *)(rcpt_node->data);
      if(addr_is_delivered(rcpt))
      logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n",
             msg->uid, addr_string(rcpt), psb->remote_host,
             psb->use_esmtp ? "esmtp" : "smtp");
    }
  }else{
    /* if something went wrong,
       we have to unmark the rcpts prematurely marked as delivered
       and mark the status */
    smtp_out_mark_rcpts(psb, rcpt_list);

    /* log the failure: */
    smtp_out_log_failure(psb, msg);
  }
  return rcpt_accept;
}

gboolean smtp_out_quit(smtp_base *psb)
{
  fprintf(psb->out, "QUIT\r\n"); fflush(psb->out);
  
  DEBUG(4) debugf("QUIT\n");

  signal(SIGALRM, SIG_DFL);

  return TRUE;
}
  
gint smtp_deliver(gchar *host, gint port, GList *resolve_list,
              message *msg,
              address *return_path,
              GList *rcpt_list)
{
  smtp_base *psb;
  smtp_error err;

  DEBUG(5) debugf("smtp_deliver entered\n");

  if(return_path == NULL)
    return_path = msg->return_path;

  if((psb = smtp_out_open(host, port, resolve_list))){
    set_heloname(psb, return_path->domain, TRUE);
    /* initiate connection, send message and quit: */
    if(smtp_out_init(psb)){
      smtp_out_msg(psb, msg, return_path, rcpt_list, NULL);
      if(psb->error == smtp_ok ||
       (psb->error == smtp_fail) ||
       (psb->error == smtp_trylater) ||
       (psb->error == smtp_syntax) ||
       (psb->error == smtp_cancel))
      
      smtp_out_quit(psb);
    }
    
    err = psb->error;
    destroy_smtpbase(psb);
    
    return err;
  }
  return -1;
}

Generated by  Doxygen 1.6.0   Back to index