Mercurial > masqmail-0.2
diff src/smtp_out.c @ 0:08114f7dcc23 0.2.21
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 wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/smtp_out.c Fri Sep 26 17:05:23 2008 +0200 @@ -0,0 +1,918 @@ +/* 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; +}