meillo@0: /* smtp_out.c, Copyright (C) 1999-2001 Oliver Kurth, meillo@0: * meillo@0: * This program is free software; you can redistribute it and/or modify meillo@0: * it under the terms of the GNU General Public License as published by meillo@0: * the Free Software Foundation; either version 2 of the License, or meillo@0: * (at your option) any later version. meillo@10: * meillo@0: * This program is distributed in the hope that it will be useful, meillo@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@0: * GNU General Public License for more details. meillo@0: * meillo@0: * You should have received a copy of the GNU General Public License meillo@0: * along with this program; if not, write to the Free Software meillo@0: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. meillo@0: */ meillo@0: meillo@0: /* meillo@0: send bugs to: kurth@innominate.de meillo@0: */ meillo@0: meillo@0: /* meillo@0: I always forget these rfc numbers: meillo@0: RFC 821 (SMTP) meillo@0: RFC 1869 (ESMTP) meillo@0: RFC 1870 (ESMTP SIZE) meillo@0: RFC 2197 (ESMTP PIPELINE) meillo@0: RFC 2554 (ESMTP AUTH) meillo@0: */ meillo@0: meillo@0: #include "masqmail.h" meillo@0: #include "smtp_out.h" meillo@0: #include "readsock.h" meillo@0: meillo@0: #ifdef ENABLE_AUTH meillo@0: meillo@0: #ifdef USE_LIB_CRYPTO meillo@0: #include meillo@0: #include meillo@0: #include meillo@0: #else meillo@0: #include "md5/global.h" meillo@0: #include "md5/md5.h" meillo@0: #include "md5/hmac_md5.h" meillo@0: #endif meillo@0: meillo@0: #include "base64/base64.h" meillo@0: #endif meillo@0: meillo@10: void meillo@10: destroy_smtpbase(smtp_base * psb) meillo@0: { meillo@10: fclose(psb->in); meillo@10: fclose(psb->out); meillo@0: meillo@10: close(psb->sock); meillo@0: meillo@10: if (psb->helo_name) meillo@10: g_free(psb->helo_name); meillo@10: if (psb->buffer) meillo@10: g_free(psb->buffer); meillo@10: if (psb->auth_names) meillo@10: g_strfreev(psb->auth_names); meillo@0: meillo@10: if (psb->auth_name) meillo@10: g_free(psb->auth_name); meillo@10: if (psb->auth_login) meillo@10: g_free(psb->auth_login); meillo@10: if (psb->auth_secret) meillo@10: g_free(psb->auth_secret); meillo@0: } meillo@0: meillo@10: gchar* meillo@10: set_heloname(smtp_base * psb, gchar * default_name, gboolean do_correct) meillo@0: { meillo@10: struct sockaddr_in sname; meillo@10: int len = sizeof(struct sockaddr_in); meillo@10: struct hostent *host_entry; meillo@0: meillo@10: if (do_correct) { meillo@10: getsockname(psb->sock, (struct sockaddr *) (&sname), &len); meillo@10: DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr)); meillo@10: host_entry = gethostbyaddr((const char *) &(sname.sin_addr), sizeof(sname.sin_addr), AF_INET); meillo@10: if (host_entry) { meillo@10: psb->helo_name = g_strdup(host_entry->h_name); meillo@10: } else { meillo@10: /* we failed to look up our own name. Instead of giving our local hostname, meillo@10: we may give our IP number to show the server that we are at least meillo@10: willing to be honest. For the really picky ones. */ meillo@10: DEBUG(5) debugf("failed to look up own host name.\n"); meillo@10: psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr)); meillo@10: } meillo@10: DEBUG(5) debugf("helo_name = %s\n", psb->helo_name); meillo@10: } meillo@10: if (psb->helo_name == NULL) { meillo@10: psb->helo_name = g_strdup(default_name); meillo@10: } meillo@10: return psb->helo_name; meillo@10: } meillo@0: meillo@0: #ifdef ENABLE_AUTH meillo@0: meillo@10: gboolean meillo@10: set_auth(smtp_base * psb, gchar * name, gchar * login, gchar * secret) meillo@0: { meillo@10: if ((strcasecmp(name, "CRAM-MD5") == 0) || (strcasecmp(name, "LOGIN") == 0)) { meillo@10: psb->auth_name = g_strdup(name); meillo@10: psb->auth_login = g_strdup(login); meillo@10: psb->auth_secret = g_strdup(secret); meillo@10: meillo@10: return TRUE; meillo@10: } meillo@10: return FALSE; meillo@0: } meillo@0: meillo@0: #endif meillo@0: meillo@10: static smtp_base* meillo@10: create_smtpbase(gint sock) meillo@0: { meillo@10: gint dup_sock; meillo@0: meillo@10: smtp_base *psb = (smtp_base *) g_malloc(sizeof(smtp_base)); meillo@0: meillo@10: psb->sock = sock; meillo@0: meillo@10: psb->use_esmtp = FALSE; meillo@10: psb->use_size = FALSE; meillo@10: psb->use_pipelining = FALSE; meillo@10: psb->use_auth = FALSE; meillo@0: meillo@10: psb->max_size = 0; meillo@10: psb->auth_names = NULL; meillo@0: meillo@10: psb->buffer = (gchar *) g_malloc(SMTP_BUF_LEN); meillo@0: meillo@10: dup_sock = dup(sock); meillo@10: psb->out = fdopen(sock, "w"); meillo@10: psb->in = fdopen(dup_sock, "r"); meillo@0: meillo@10: psb->error = smtp_ok; meillo@0: meillo@10: psb->helo_name = NULL; meillo@0: meillo@10: psb->auth_name = psb->auth_login = psb->auth_secret = NULL; meillo@10: meillo@10: return psb; meillo@0: } meillo@0: meillo@10: static gboolean meillo@10: read_response(smtp_base * psb, int timeout) meillo@0: { meillo@10: gint buf_pos = 0; meillo@10: gchar code[5]; meillo@10: gint i, len; meillo@0: meillo@10: do { meillo@10: len = read_sockline(psb->in, &(psb->buffer[buf_pos]), SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG); meillo@10: if (len == -3) { meillo@10: psb->error = smtp_timeout; meillo@10: return FALSE; meillo@10: } else if (len == -2) { meillo@10: psb->error = smtp_syntax; meillo@10: return FALSE; meillo@10: } else if (len == -1) { meillo@10: psb->error = smtp_eof; meillo@10: return FALSE; meillo@10: } meillo@10: for (i = 0; i < 4; i++) meillo@10: code[i] = psb->buffer[buf_pos + i]; meillo@15: code[i] = '\0'; meillo@10: psb->last_code = atoi(code); meillo@0: meillo@10: buf_pos += len; meillo@0: meillo@10: } while (code[3] == '-'); meillo@0: meillo@10: return TRUE; meillo@0: } meillo@0: meillo@10: static gboolean meillo@10: check_response(smtp_base * psb, gboolean after_data) meillo@0: { meillo@10: char c = psb->buffer[0]; meillo@0: meillo@10: if (((c == '2') && !after_data) || ((c == '3') && after_data)) { meillo@10: psb->error = smtp_ok; meillo@10: DEBUG(6) debugf("response OK:'%s' after_date = %d\n", psb->buffer, (int) after_data); meillo@10: return TRUE; meillo@10: } else { meillo@10: if (c == '4') meillo@10: psb->error = smtp_trylater; meillo@10: else if (c == '5') meillo@10: psb->error = smtp_fail; meillo@10: else meillo@10: psb->error = smtp_syntax; meillo@10: DEBUG(6) debugf("response failure:'%s' after_date = %d\n", psb->buffer, (int) after_data); meillo@10: return FALSE; meillo@10: } meillo@0: } meillo@0: meillo@10: static gboolean meillo@10: check_init_response(smtp_base * psb) meillo@0: { meillo@10: if (check_response(psb, FALSE)) { meillo@10: psb->use_esmtp = (strstr(psb->buffer, "ESMTP") != NULL); meillo@0: meillo@10: DEBUG(4) debugf(psb->use_esmtp ? "uses esmtp\n" : "no esmtp\n"); meillo@0: meillo@10: return TRUE; meillo@10: } meillo@10: return FALSE; meillo@0: } meillo@0: meillo@10: static gchar* meillo@10: get_response_arg(gchar * response) meillo@0: { meillo@10: gchar buf[SMTP_BUF_LEN]; meillo@10: gchar *p = response, *q = buf; meillo@0: meillo@10: while (*p && (*p != '\n') && isspace(*p)) meillo@10: p++; meillo@10: if (*p && (*p != '\n')) { meillo@10: while (*p && (*p != '\n') && (*p != '\r') && (q < buf + SMTP_BUF_LEN - 1)) meillo@10: *(q++) = *(p++); meillo@15: *q = '\0'; meillo@10: return g_strdup(buf); meillo@10: } meillo@10: return NULL; meillo@0: } meillo@0: meillo@10: static gboolean meillo@10: check_helo_response(smtp_base * psb) meillo@0: { meillo@10: gchar *ptr = psb->buffer; meillo@0: meillo@10: if (!check_response(psb, FALSE)) meillo@10: return FALSE; meillo@0: meillo@10: while (*ptr) { meillo@10: if (strncasecmp(&(ptr[4]), "SIZE", 4) == 0) { meillo@10: gchar *arg; meillo@10: psb->use_size = TRUE; meillo@10: arg = get_response_arg(&(ptr[8])); meillo@10: if (arg) { meillo@10: psb->max_size = atoi(arg); meillo@10: g_free(arg); meillo@10: } meillo@10: } meillo@0: meillo@10: if (strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0) meillo@10: psb->use_pipelining = TRUE; meillo@0: meillo@10: if (strncasecmp(&(ptr[4]), "AUTH", 4) == 0) { meillo@10: if ((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')) { /* not sure about '\t' */ meillo@10: gchar *arg; meillo@10: psb->use_auth = TRUE; meillo@10: arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */ meillo@10: if (arg) { meillo@10: psb->auth_names = g_strsplit(arg, " ", 0); meillo@10: g_free(arg); meillo@10: meillo@10: DEBUG(4) { meillo@10: gint i = 0; meillo@10: while (psb->auth_names[i]) { meillo@10: debugf("offered AUTH %s\n", psb->auth_names[i]); meillo@10: i++; meillo@10: } meillo@10: } meillo@10: } meillo@10: } meillo@10: } meillo@10: meillo@10: while (*ptr != '\n') meillo@10: ptr++; meillo@10: ptr++; meillo@0: } meillo@0: meillo@10: DEBUG(4) { meillo@10: debugf(psb->use_size ? "uses SIZE\n" : "no size\n"); meillo@10: debugf(psb->use_pipelining ? "uses PIPELINING\n" : "no pipelining\n"); meillo@10: debugf(psb->use_auth ? "uses AUTH\n" : "no auth\n"); meillo@10: } meillo@0: meillo@10: return TRUE; meillo@0: } meillo@0: meillo@10: static gboolean meillo@10: smtp_helo(smtp_base * psb, gchar * helo) meillo@0: { meillo@10: while (TRUE) { meillo@10: if (psb->use_esmtp) { meillo@10: fprintf(psb->out, "EHLO %s\r\n", helo); meillo@10: fflush(psb->out); meillo@0: meillo@10: DEBUG(4) debugf("EHLO %s\r\n", helo); meillo@0: meillo@10: } else { meillo@10: fprintf(psb->out, "HELO %s\r\n", helo); meillo@10: fflush(psb->out); meillo@0: meillo@10: DEBUG(4) debugf("HELO %s\r\n", helo); meillo@0: meillo@10: } meillo@0: meillo@10: if (!read_response(psb, SMTP_CMD_TIMEOUT)) meillo@10: return FALSE; meillo@10: meillo@10: if (check_helo_response(psb)) meillo@10: return TRUE; meillo@10: else { meillo@10: if (psb->error == smtp_fail) { meillo@10: if (psb->use_esmtp) { meillo@10: /* our guess that server understands EHLO was wrong, try again with HELO */ meillo@10: psb->use_esmtp = FALSE; meillo@10: } else { meillo@10: /* what sort of server ist THAT ?! give up... */ meillo@10: return FALSE; meillo@10: } meillo@10: } else meillo@10: return FALSE; meillo@10: } meillo@0: } meillo@0: } meillo@0: meillo@10: static void meillo@10: smtp_cmd_mailfrom(smtp_base * psb, address * return_path, guint size) meillo@0: { meillo@10: if (psb->use_size) { meillo@10: fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size); meillo@10: fflush(psb->out); meillo@0: meillo@10: DEBUG(4) debugf("MAIL FROM:%s SIZE=%d\r\n", addr_string(return_path), size); meillo@0: meillo@10: } else { meillo@10: fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path)); meillo@10: fflush(psb->out); meillo@0: meillo@10: DEBUG(4) debugf("MAIL FROM:%s\r\n", addr_string(return_path)); meillo@10: } meillo@0: } meillo@0: meillo@10: static void meillo@10: smtp_cmd_rcptto(smtp_base * psb, address * rcpt) meillo@0: { meillo@10: fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt)); meillo@10: fflush(psb->out); meillo@10: DEBUG(4) debugf("RCPT TO:%s\n", addr_string(rcpt)); meillo@0: } meillo@0: meillo@10: static void meillo@10: send_data_line(smtp_base * psb, gchar * data) meillo@0: { meillo@10: /* According to RFC 821 each line should be terminated with CRLF. meillo@10: Since a dot on a line itself marks the end of data, each line meillo@10: beginning with a dot is prepended with another dot. meillo@10: */ meillo@10: gchar *ptr; meillo@15: gboolean new_line = TRUE; /* previous versions assumed that each item was exactly one line. meillo@15: This is no longer the case */ meillo@0: meillo@10: ptr = data; meillo@10: while (*ptr) { meillo@10: int c = (int) (*ptr); meillo@10: if (c == '.') meillo@10: if (new_line) meillo@10: putc('.', psb->out); meillo@10: if (c == '\n') { meillo@10: putc('\r', psb->out); meillo@10: putc('\n', psb->out); meillo@10: new_line = TRUE; meillo@10: } else { meillo@10: putc(c, psb->out); meillo@10: new_line = FALSE; meillo@10: } meillo@10: ptr++; meillo@10: } meillo@0: } meillo@0: meillo@10: static void meillo@10: send_header(smtp_base * psb, GList * hdr_list) meillo@0: { meillo@10: GList *node; meillo@10: gint num_hdrs = 0; meillo@0: meillo@10: /* header */ meillo@10: if (hdr_list) { meillo@10: foreach(hdr_list, node) { meillo@10: if (node->data) { meillo@10: header *hdr = (header *) (node->data); meillo@10: if (hdr->header) { meillo@10: send_data_line(psb, hdr->header); meillo@10: num_hdrs++; meillo@10: } meillo@10: } meillo@10: } meillo@0: } meillo@0: meillo@10: /* empty line separating headers from data: */ meillo@10: putc('\r', psb->out); meillo@10: putc('\n', psb->out); meillo@0: meillo@10: DEBUG(4) debugf("sent %d headers\n", num_hdrs); meillo@0: } meillo@0: meillo@10: static void meillo@10: send_data(smtp_base * psb, message * msg) meillo@0: { meillo@10: GList *node; meillo@10: gint num_lines = 0; meillo@0: meillo@10: /* data */ meillo@10: if (msg->data_list) { meillo@10: for (node = g_list_first(msg->data_list); node; node = g_list_next(node)) { meillo@10: if (node->data) { meillo@10: send_data_line(psb, node->data); meillo@10: num_lines++; meillo@10: } meillo@10: } meillo@10: } meillo@0: meillo@10: DEBUG(4) debugf("sent %d lines of data\n", num_lines); meillo@0: meillo@10: fprintf(psb->out, ".\r\n"); meillo@10: fflush(psb->out); meillo@0: } meillo@0: meillo@10: void meillo@10: smtp_out_mark_rcpts(smtp_base * psb, GList * rcpt_list) meillo@0: { meillo@10: GList *rcpt_node; meillo@10: for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) { meillo@10: address *rcpt = (address *) (rcpt_node->data); meillo@0: meillo@10: addr_unmark_delivered(rcpt); meillo@0: meillo@10: if ((psb->error == smtp_trylater) || (psb->error == smtp_timeout) || (psb->error == smtp_eof)) { meillo@10: addr_mark_defered(rcpt); meillo@10: } else { meillo@10: addr_mark_failed(rcpt); meillo@10: } meillo@10: } meillo@0: } meillo@0: meillo@10: void meillo@10: smtp_out_log_failure(smtp_base * psb, message * msg) meillo@0: { meillo@10: gchar *err_str; meillo@0: meillo@10: if (psb->error == smtp_timeout) meillo@10: err_str = g_strdup("connection timed out."); meillo@10: else if (psb->error == smtp_eof) meillo@10: err_str = g_strdup("connection terminated prematurely."); meillo@10: else if (psb->error == smtp_syntax) meillo@10: err_str = g_strdup_printf("got unexpected response: %s", psb->buffer); meillo@10: else if (psb->error == smtp_cancel) meillo@10: err_str = g_strdup("delivery was canceled.\n"); meillo@10: else meillo@10: /* error message should still be in the buffer */ meillo@10: err_str = g_strdup_printf("failed: %s\n", psb->buffer); meillo@0: meillo@10: if (msg == NULL) meillo@10: logwrite(LOG_NOTICE, "host=%s %s\n", psb->remote_host, err_str); meillo@10: else meillo@10: logwrite(LOG_NOTICE, "%s == host=%s %s\n", msg->uid, psb->remote_host, err_str); meillo@0: meillo@10: g_free(err_str); meillo@0: } meillo@0: meillo@10: smtp_base* meillo@10: smtp_out_open(gchar * host, gint port, GList * resolve_list) meillo@0: { meillo@10: smtp_base *psb; meillo@10: gint sock; meillo@10: mxip_addr *addr; meillo@0: meillo@10: DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host); meillo@0: meillo@10: if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) { meillo@10: /* create structure to hold status data: */ meillo@10: psb = create_smtpbase(sock); meillo@10: psb->remote_host = addr->name; meillo@0: meillo@10: DEBUG(5) { meillo@10: struct sockaddr_in name; meillo@10: int len = sizeof(struct sockaddr); meillo@10: getsockname(sock, (struct sockaddr *) (&name), &len); meillo@10: debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr)); meillo@10: } meillo@10: return psb; meillo@10: } else { meillo@10: DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno)); meillo@10: } meillo@0: meillo@10: return NULL; meillo@0: } meillo@0: meillo@10: smtp_base* meillo@10: smtp_out_open_child(gchar * cmd) meillo@0: { meillo@10: smtp_base *psb; meillo@10: gint sock; meillo@0: meillo@10: DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd); meillo@0: meillo@10: sock = child(cmd); meillo@0: meillo@10: if (sock > 0) { meillo@10: psb = create_smtpbase(sock); meillo@10: psb->remote_host = NULL; meillo@0: meillo@10: return psb; meillo@10: } meillo@0: meillo@10: return NULL; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: smtp_out_rset(smtp_base * psb) meillo@0: { meillo@10: gboolean ok; meillo@0: meillo@10: fprintf(psb->out, "RSET\r\n"); meillo@10: fflush(psb->out); meillo@10: DEBUG(4) debugf("RSET\n"); meillo@0: meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) meillo@10: if (check_response(psb, FALSE)) meillo@10: return TRUE; meillo@0: meillo@10: smtp_out_log_failure(psb, NULL); meillo@10: meillo@10: return FALSE; meillo@0: } meillo@0: meillo@0: #ifdef ENABLE_AUTH meillo@0: meillo@10: static gboolean meillo@10: smtp_out_auth_cram_md5(smtp_base * psb) meillo@0: { meillo@10: gboolean ok = FALSE; meillo@0: meillo@10: fprintf(psb->out, "AUTH CRAM-MD5\r\n"); meillo@10: fflush(psb->out); meillo@10: DEBUG(4) debugf("AUTH CRAM-MD5\n"); meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) { meillo@10: if ((ok = check_response(psb, TRUE))) { meillo@10: gchar *chall64 = get_response_arg(&(psb->buffer[4])); meillo@10: gint chall_size; meillo@10: gchar *chall = base64_decode(chall64, &chall_size); meillo@10: guchar digest[16], *reply64, *reply; meillo@10: gchar digest_string[33]; meillo@10: gint i; meillo@0: #ifdef USE_LIB_CRYPTO meillo@10: unsigned int digest_len; meillo@0: #endif meillo@10: meillo@10: DEBUG(5) debugf("encoded challenge = %s\n", chall64); meillo@10: DEBUG(5) debugf("decoded challenge = %s, size = %d\n", chall, chall_size); meillo@10: meillo@10: DEBUG(5) debugf("secret = %s\n", psb->auth_secret); meillo@10: meillo@0: #ifdef USE_LIB_CRYPTO meillo@10: HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len); meillo@0: #else meillo@10: hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest); meillo@0: #endif meillo@10: meillo@10: for (i = 0; i < 16; i++) meillo@10: sprintf(&(digest_string[i + i]), "%02x", (unsigned int) (digest[i])); meillo@15: digest_string[32] = '\0'; meillo@10: meillo@10: DEBUG(5) debugf("digest = %s\n", digest_string); meillo@10: meillo@10: reply = g_strdup_printf("%s %s", psb->auth_login, digest_string); meillo@10: DEBUG(5) debugf("unencoded reply = %s\n", reply); meillo@10: meillo@10: reply64 = base64_encode(reply, strlen(reply)); meillo@10: DEBUG(5) debugf("encoded reply = %s\n", reply64); meillo@10: meillo@10: fprintf(psb->out, "%s\r\n", reply64); meillo@10: fflush(psb->out); meillo@10: DEBUG(4) debugf("%s\n", reply64); meillo@10: meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) meillo@10: ok = check_response(psb, FALSE); meillo@10: meillo@10: g_free(reply64); meillo@10: g_free(reply); meillo@10: g_free(chall); meillo@10: g_free(chall64); meillo@10: } meillo@10: } meillo@10: return ok; meillo@0: } meillo@0: meillo@10: static gboolean meillo@10: smtp_out_auth_login(smtp_base * psb) meillo@0: { meillo@10: gboolean ok = FALSE; meillo@10: fprintf(psb->out, "AUTH LOGIN\r\n"); meillo@10: fflush(psb->out); meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) { meillo@10: if ((ok = check_response(psb, TRUE))) { meillo@10: gchar *resp64; meillo@10: guchar *resp; meillo@10: gint resp_size; meillo@10: gchar *reply64; meillo@10: meillo@10: resp64 = get_response_arg(&(psb->buffer[4])); meillo@10: DEBUG(5) debugf("encoded response = %s\n", resp64); meillo@10: resp = base64_decode(resp64, &resp_size); meillo@10: g_free(resp64); meillo@10: DEBUG(5) debugf("decoded response = %s, size = %d\n", resp, resp_size); meillo@10: g_free(resp); meillo@10: reply64 = base64_encode(psb->auth_login, strlen(psb->auth_login)); meillo@10: fprintf(psb->out, "%s\r\n", reply64); meillo@10: fflush(psb->out); meillo@10: g_free(reply64); meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) { meillo@10: if ((ok = check_response(psb, TRUE))) { meillo@10: resp64 = get_response_arg(&(psb->buffer[4])); meillo@10: DEBUG(5) debugf("encoded response = %s\n", resp64); meillo@10: resp = base64_decode(resp64, &resp_size); meillo@10: g_free(resp64); meillo@10: DEBUG(5) debugf("decoded response = %s, size = %d\n", resp, resp_size); meillo@10: g_free(resp); meillo@10: reply64 = base64_encode(psb->auth_secret, strlen(psb->auth_secret)); meillo@10: fprintf(psb->out, "%s\r\n", reply64); meillo@10: fflush(psb->out); meillo@10: g_free(reply64); meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) meillo@10: ok = check_response(psb, FALSE); meillo@10: } meillo@10: } meillo@10: } meillo@0: } meillo@10: return ok; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: smtp_out_auth(smtp_base * psb) meillo@0: { meillo@10: gboolean ok = FALSE; meillo@10: gint i = 0; meillo@10: while (psb->auth_names[i]) { meillo@10: if (strcasecmp(psb->auth_names[i], psb->auth_name) == 0) meillo@10: break; meillo@10: i++; meillo@10: } meillo@10: if (psb->auth_names[i]) { meillo@10: if (strcasecmp(psb->auth_name, "cram-md5") == 0) { meillo@10: smtp_out_auth_cram_md5(psb); meillo@10: } else if (strcasecmp(psb->auth_name, "login") == 0) { meillo@10: smtp_out_auth_login(psb); meillo@10: } else { meillo@10: logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name); meillo@10: } meillo@10: } else { meillo@10: logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name); meillo@10: } meillo@10: return ok; meillo@0: } meillo@0: meillo@0: #endif meillo@0: meillo@10: gboolean meillo@10: smtp_out_init(smtp_base * psb) meillo@0: { meillo@10: gboolean ok; meillo@0: meillo@10: if ((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))) { meillo@10: if ((ok = check_init_response(psb))) { meillo@10: meillo@10: if ((ok = smtp_helo(psb, psb->helo_name))) { meillo@0: #ifdef ENABLE_AUTH meillo@10: if (psb->auth_name && psb->use_auth) { meillo@10: /* we completely disregard the response of server here. If meillo@10: authentication fails, the server will complain later meillo@10: anyway. I know, this is not polite... */ meillo@10: smtp_out_auth(psb); meillo@10: } meillo@10: #endif meillo@10: } meillo@10: } meillo@0: } meillo@10: if (!ok) meillo@10: smtp_out_log_failure(psb, NULL); meillo@10: return ok; meillo@0: } meillo@0: meillo@10: gint meillo@10: smtp_out_msg(smtp_base * psb, message * msg, address * return_path, GList * rcpt_list, GList * hdr_list) meillo@0: { meillo@10: gint i, size; meillo@10: gboolean ok = TRUE; meillo@10: int rcpt_cnt; meillo@10: int rcpt_accept = 0; meillo@0: meillo@10: DEBUG(5) debugf("smtp_out_msg entered\n"); meillo@0: meillo@10: /* defaults: */ meillo@10: if (return_path == NULL) meillo@10: return_path = msg->return_path; meillo@10: if (hdr_list == NULL) meillo@10: hdr_list = msg->hdr_list; meillo@10: if (rcpt_list == NULL) meillo@10: rcpt_list = msg->rcpt_list; meillo@10: rcpt_cnt = g_list_length(rcpt_list); meillo@0: meillo@10: size = msg_calc_size(msg, TRUE); meillo@0: meillo@10: /* respect maximum size given by server: */ meillo@10: if ((psb->max_size > 0) && (size > psb->max_size)) { meillo@10: logwrite(LOG_WARNING, "%s == host=%s message size (%d) > fixed maximum message size of server (%d)", meillo@10: msg->uid, psb->remote_host, size, psb->max_size); meillo@10: psb->error = smtp_cancel; meillo@10: ok = FALSE; meillo@10: } meillo@0: meillo@10: if (ok) { meillo@10: smtp_cmd_mailfrom(psb, return_path, psb->use_size ? size + SMTP_SIZE_ADD : 0); meillo@0: meillo@10: if (!psb->use_pipelining) { meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) meillo@10: ok = check_response(psb, FALSE); meillo@10: } meillo@10: } meillo@10: if (ok) { meillo@10: GList *rcpt_node; meillo@10: rcpt_accept = 0; meillo@0: meillo@10: for (rcpt_node = g_list_first(rcpt_list); rcpt_node != NULL; rcpt_node = g_list_next(rcpt_node)) { meillo@10: address *rcpt = (address *) (rcpt_node->data); meillo@10: smtp_cmd_rcptto(psb, rcpt); meillo@10: if (!psb->use_pipelining) { meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) meillo@10: if (check_response(psb, FALSE)) { meillo@10: rcpt_accept++; meillo@10: addr_mark_delivered(rcpt); meillo@10: } else { meillo@10: /* if server returned an error for one recp. we meillo@10: may still try the others. But if it is a timeout, eof meillo@10: or unexpected response, it is more serious and we should meillo@10: give up. */ meillo@10: if ((psb->error != smtp_trylater) && (psb->error != smtp_fail)) { meillo@10: ok = FALSE; meillo@10: break; meillo@10: } else { meillo@15: logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s", meillo@15: msg->uid, addr_string(rcpt), psb->remote_host, psb->buffer); meillo@10: if (psb->error == smtp_trylater) { meillo@10: addr_mark_defered(rcpt); meillo@10: } else { meillo@10: addr_mark_failed(rcpt); meillo@10: } meillo@10: } meillo@10: } else meillo@10: break; meillo@10: } meillo@10: } meillo@0: meillo@10: /* There is no point in going on if no recp.s were accpted. meillo@10: But we can check that at this point only if not pipelining: */ meillo@10: ok = (ok && (psb->use_pipelining || (rcpt_accept > 0))); meillo@10: if (ok) { meillo@0: meillo@10: fprintf(psb->out, "DATA\r\n"); meillo@10: fflush(psb->out); meillo@0: meillo@10: DEBUG(4) debugf("DATA\r\n"); meillo@10: meillo@10: if (psb->use_pipelining) { meillo@10: /* the first pl'ed command was MAIL FROM meillo@10: the last was DATA, whose response can be handled by the 'normal' code meillo@10: all in between were RCPT TO: meillo@10: */ meillo@10: /* response to MAIL FROM: */ meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) { meillo@10: if ((ok = check_response(psb, FALSE))) { meillo@10: meillo@10: /* response(s) to RCPT TO: meillo@10: this is very similar to the sequence above for no pipeline meillo@10: */ meillo@10: for (i = 0; i < rcpt_cnt; i++) { meillo@10: if ((ok = read_response(psb, SMTP_CMD_TIMEOUT))) { meillo@10: address *rcpt = g_list_nth_data(rcpt_list, i); meillo@10: if (check_response(psb, FALSE)) { meillo@10: rcpt_accept++; meillo@10: addr_mark_delivered(rcpt); meillo@10: } else { meillo@10: /* if server returned an error 4xx or 5xx for one recp. we meillo@10: may still try the others. But if it is a timeout, eof meillo@10: or unexpected response, it is more serious and we meillo@10: should give up. */ meillo@10: if ((psb->error != smtp_trylater) && meillo@10: (psb->error != smtp_fail)) { meillo@10: ok = FALSE; meillo@10: break; meillo@10: } else { meillo@10: logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s", msg->uid, meillo@10: addr_string(rcpt), psb->remote_host, psb->buffer); meillo@10: if (psb->error == smtp_trylater) { meillo@10: addr_mark_defered(rcpt); meillo@10: } else { meillo@10: addr_mark_failed(rcpt); meillo@10: } meillo@10: } meillo@10: } meillo@10: } else { meillo@10: DEBUG(5) debugf("check_response failed after RCPT TO\n"); meillo@10: break; meillo@10: } meillo@10: } meillo@10: if (rcpt_accept == 0) meillo@10: ok = FALSE; meillo@10: } else { meillo@10: DEBUG(5) debugf("check_response failed after MAIL FROM\n"); meillo@10: } meillo@10: } else { meillo@10: DEBUG(5) meillo@10: debugf("read_response failed after MAIL FROM\n"); meillo@10: } meillo@10: } meillo@10: meillo@10: /* if(psb->use_pipelining) */ meillo@10: /* response to the DATA cmd */ meillo@10: if (ok) { meillo@10: if (read_response(psb, SMTP_DATA_TIMEOUT)) { meillo@10: if (check_response(psb, TRUE)) { meillo@10: send_header(psb, hdr_list); meillo@10: send_data(psb, msg); meillo@10: meillo@10: if (read_response(psb, SMTP_FINAL_TIMEOUT)) meillo@10: ok = check_response(psb, FALSE); meillo@10: } meillo@10: } meillo@10: } meillo@0: } meillo@10: } meillo@10: meillo@10: DEBUG(5) { meillo@10: debugf("psb->error = %d\n", psb->error); meillo@10: debugf("ok = %d\n", ok); meillo@10: debugf("rcpt_accept = %d\n", rcpt_accept); meillo@10: } meillo@10: meillo@10: if (psb->error == smtp_ok) { meillo@10: GList *rcpt_node; meillo@10: for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) { meillo@10: address *rcpt = (address *) (rcpt_node->data); meillo@10: if (addr_is_delivered(rcpt)) meillo@10: logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n", msg->uid, addr_string(rcpt), meillo@10: psb->remote_host, psb->use_esmtp ? "esmtp" : "smtp"); meillo@0: } meillo@10: } else { meillo@10: /* if something went wrong, meillo@10: we have to unmark the rcpts prematurely marked as delivered meillo@10: and mark the status */ meillo@10: smtp_out_mark_rcpts(psb, rcpt_list); meillo@10: meillo@10: /* log the failure: */ meillo@10: smtp_out_log_failure(psb, msg); meillo@0: } meillo@10: return rcpt_accept; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: smtp_out_quit(smtp_base * psb) meillo@0: { meillo@10: fprintf(psb->out, "QUIT\r\n"); meillo@10: fflush(psb->out); meillo@0: meillo@10: DEBUG(4) debugf("QUIT\n"); meillo@0: meillo@10: signal(SIGALRM, SIG_DFL); meillo@10: meillo@10: return TRUE; meillo@0: } meillo@10: meillo@10: gint meillo@10: smtp_deliver(gchar * host, gint port, GList * resolve_list, message * msg, address * return_path, GList * rcpt_list) meillo@0: { meillo@10: smtp_base *psb; meillo@10: smtp_error err; meillo@0: meillo@10: DEBUG(5) debugf("smtp_deliver entered\n"); meillo@0: meillo@10: if (return_path == NULL) meillo@10: return_path = msg->return_path; meillo@0: meillo@10: if ((psb = smtp_out_open(host, port, resolve_list))) { meillo@10: set_heloname(psb, return_path->domain, TRUE); meillo@10: /* initiate connection, send message and quit: */ meillo@10: if (smtp_out_init(psb)) { meillo@10: smtp_out_msg(psb, msg, return_path, rcpt_list, NULL); meillo@10: if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater) meillo@10: || (psb->error == smtp_syntax) || (psb->error == smtp_cancel)) meillo@10: smtp_out_quit(psb); meillo@10: } meillo@10: meillo@10: err = psb->error; meillo@10: destroy_smtpbase(psb); meillo@10: meillo@10: return err; meillo@10: } meillo@10: return -1; meillo@0: }