/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2002 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
*
* 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
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
+#include "claws-features.h"
#endif
#include <glib.h>
+#ifdef ENABLE_NLS
+#include <glib/gi18n.h>
+#else
+#define _(a) (a)
+#define N_(a) (a)
+#endif
#include <stdio.h>
#include <string.h>
-#include "intl.h"
#include "smtp.h"
-#include "socket.h"
#include "md5.h"
#include "base64.h"
#include "utils.h"
#include "log.h"
-static gint verbose = 1;
-
-#define UI_UPDATE(session, phase) \
-{ \
- if (SESSION(session)->ui_func) \
- SESSION(session)->ui_func(SESSION(session), phase); \
-}
+static void smtp_session_destroy(Session *session);
+static gint smtp_auth(SMTPSession *session);
+#ifdef USE_GNUTLS
static gint smtp_starttls(SMTPSession *session);
-static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len);
-static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len);
+#endif
+static gint smtp_auth_cram_md5(SMTPSession *session);
+static gint smtp_auth_login(SMTPSession *session);
+static gint smtp_auth_plain(SMTPSession *session);
-static gint smtp_ok(SockInfo *sock, gchar *buf, gint len);
+static gint smtp_ehlo(SMTPSession *session);
+static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg);
-Session *smtp_session_new(void)
+static gint smtp_helo(SMTPSession *session);
+static gint smtp_rcpt(SMTPSession *session);
+static gint smtp_data(SMTPSession *session);
+static gint smtp_send_data(SMTPSession *session);
+static gint smtp_make_ready(SMTPSession *session);
+static gint smtp_eom(SMTPSession *session);
+
+static gint smtp_session_recv_msg(Session *session, const gchar *msg);
+static gint smtp_session_send_data_finished(Session *session, guint len);
+
+
+Session *smtp_session_new(void *prefs_account)
{
SMTPSession *session;
session = g_new0(SMTPSession, 1);
- SESSION(session)->type = SESSION_SMTP;
- SESSION(session)->server = NULL;
- SESSION(session)->sock = NULL;
- SESSION(session)->connected = FALSE;
- SESSION(session)->phase = SESSION_READY;
- SESSION(session)->last_access_time = 0;
- SESSION(session)->data = NULL;
-
- SESSION(session)->destroy = smtp_session_destroy;
- SESSION(session)->ui_func = NULL;
- session->avail_auth_type = 0;
- session->user = NULL;
- session->pass = NULL;
+ session_init(SESSION(session), prefs_account, TRUE);
- return SESSION(session);
-}
+ SESSION(session)->type = SESSION_SMTP;
-void smtp_session_destroy(Session *session)
-{
- sock_close(session->sock);
- session->sock = NULL;
+ SESSION(session)->recv_msg = smtp_session_recv_msg;
- g_free(SMTP_SESSION(session)->user);
- g_free(SMTP_SESSION(session)->pass);
-}
+ SESSION(session)->recv_data_finished = NULL;
+ SESSION(session)->send_data_finished = smtp_session_send_data_finished;
-#if USE_OPENSSL
-gint smtp_connect(SMTPSession *session, const gchar *server, gushort port,
- const gchar *domain, const gchar *user, const gchar *pass,
- SSLType ssl_type)
-#else
-gint smtp_connect(SMTPSession *session, const gchar *server, gushort port,
- const gchar *domain, const gchar *user, const gchar *pass)
-#endif
-{
- SockInfo *sock;
- gboolean use_esmtp;
- SMTPAuthType avail_auth_type = 0;
- gint val;
+ SESSION(session)->destroy = smtp_session_destroy;
- g_return_val_if_fail(session != NULL, SM_ERROR);
- g_return_val_if_fail(server != NULL, SM_ERROR);
+ session->state = SMTP_READY;
-#if USE_OPENSSL
- use_esmtp = user != NULL || ssl_type == SSL_STARTTLS;
-#else
- use_esmtp = user != NULL;
+#ifdef USE_GNUTLS
+ session->tls_init_done = FALSE;
#endif
- SESSION(session)->server = g_strdup(server);
- session->user = user ? g_strdup(user) : NULL;
- session->pass = pass ? g_strdup(pass) : user ? g_strdup("") : NULL;
+ session->hostname = NULL;
+ session->user = NULL;
+ session->pass = NULL;
- UI_UPDATE(session, SMTP_CONNECT);
+ session->from = NULL;
+ session->to_list = NULL;
+ session->cur_to = NULL;
- if ((sock = sock_connect(server, port)) == NULL) {
- log_warning(_("Can't connect to SMTP server: %s:%d\n"),
- server, port);
- return SM_ERROR;
- }
+ session->send_data = NULL;
+ session->send_data_len = 0;
-#if USE_OPENSSL
- if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
- log_warning(_("SSL connection failed"));
- sock_close(sock);
- return SM_ERROR;
- }
-#endif
+ session->max_message_size = -1;
- if (smtp_ok(sock, NULL, 0) != SM_OK) {
- log_warning(_("Error occurred while connecting to %s:%d\n"),
- server, port);
- sock_close(sock);
- return SM_ERROR;
- }
+ session->avail_auth_type = 0;
+ session->forced_auth_type = 0;
+ session->auth_type = 0;
+ session->esmtp_flags = 0;
- SESSION(session)->sock = sock;
- SESSION(session)->connected = TRUE;
+ session->error_val = SM_OK;
+ session->error_msg = NULL;
- if (!domain)
- domain = get_domain_name();
+ return SESSION(session);
+}
- if (use_esmtp)
- val = smtp_ehlo(session, domain, &avail_auth_type);
- else
- val = smtp_helo(session, domain);
- if (val != SM_OK) {
- log_warning(use_esmtp? _("Error occurred while sending EHLO\n"):
- _("Error occurred while sending HELO\n"));
- return val;
- }
+static void smtp_session_destroy(Session *session)
+{
+ SMTPSession *smtp_session = SMTP_SESSION(session);
-#if USE_OPENSSL
- /* if we have a user to authenticate and no auth methods, but starttls,
- try to starttls */
- if (ssl_type == SSL_NONE && avail_auth_type == SMTPAUTH_TLS_AVAILABLE
- && user != NULL)
- ssl_type = SSL_STARTTLS;
-
- if (ssl_type == SSL_STARTTLS) {
- val = smtp_starttls(session);
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending STARTTLS\n"));
- return val;
- }
- if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
- return SM_ERROR;
- }
- val = smtp_ehlo(session, domain, &avail_auth_type);
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending EHLO\n"));
- return val;
- }
- }
-#endif
+ g_free(smtp_session->hostname);
+ g_free(smtp_session->user);
+ g_free(smtp_session->pass);
+ g_free(smtp_session->from);
- session->avail_auth_type = avail_auth_type;
+ g_free(smtp_session->send_data);
- return 0;
+ g_free(smtp_session->error_msg);
}
-gint smtp_from(SMTPSession *session, const gchar *from)
+gint smtp_from(SMTPSession *session)
{
- gchar buf[MSGBUFSIZE];
+ gchar buf[MESSAGEBUFSIZE];
+ gchar *mail_size = NULL;
- g_return_val_if_fail(session != NULL, SM_ERROR);
- g_return_val_if_fail(from != NULL, SM_ERROR);
+ cm_return_val_if_fail(session->from != NULL, SM_ERROR);
- UI_UPDATE(session, SMTP_FROM);
+ session->state = SMTP_FROM;
+
+ if (session->is_esmtp && (session->esmtp_flags & ESMTP_SIZE)!=0)
+ mail_size = g_strdup_printf(" SIZE=%d", session->send_data_len);
+ else
+ mail_size = g_strdup("");
+
- if (strchr(from, '<'))
- g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", from);
+ if (strchr(session->from, '<'))
+ g_snprintf(buf, sizeof(buf), "MAIL FROM:%s%s", session->from,
+ mail_size);
else
- g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", from);
+ g_snprintf(buf, sizeof(buf), "MAIL FROM:<%s>%s", session->from,
+ mail_size);
+
+ g_free(mail_size);
- sock_printf(SESSION(session)->sock, "%s\r\n", buf);
- if (verbose)
- log_print("SMTP> %s\n", buf);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "%sSMTP> %s\n", (session->is_esmtp?"E":""), buf);
- return smtp_ok(SESSION(session)->sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_auth(SMTPSession *session, SMTPAuthType forced_auth_type)
+static gint smtp_auth(SMTPSession *session)
{
- gchar buf[MSGBUFSIZE];
- SMTPAuthType authtype = 0;
- guchar hexdigest[33];
- gchar *challenge, *response, *response64;
- gint challengelen;
- SockInfo *sock;
-
- g_return_val_if_fail(session != NULL, SM_ERROR);
- g_return_val_if_fail(session->user != NULL, SM_ERROR);
-
- UI_UPDATE(session, SMTP_AUTH);
-
- sock = SESSION(session)->sock;
-
- if ((forced_auth_type == SMTPAUTH_CRAM_MD5 ||
- (forced_auth_type == 0 &&
- (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0)) &&
- smtp_auth_cram_md5(session, buf, sizeof(buf)) == SM_OK)
- authtype = SMTPAUTH_CRAM_MD5;
- else if ((forced_auth_type == SMTPAUTH_LOGIN ||
- (forced_auth_type == 0 &&
- (session->avail_auth_type & SMTPAUTH_LOGIN) != 0)) &&
- smtp_auth_login(session, buf, sizeof(buf)) == SM_OK)
- authtype = SMTPAUTH_LOGIN;
- else {
- log_warning(_("SMTP AUTH not available\n"));
+
+ cm_return_val_if_fail(session->user != NULL, SM_ERROR);
+
+ session->state = SMTP_AUTH;
+
+ if ((session->forced_auth_type == SMTPAUTH_CRAM_MD5
+ || session->forced_auth_type == 0)
+ &&
+ (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0)
+ smtp_auth_cram_md5(session);
+ else if ((session->forced_auth_type == SMTPAUTH_LOGIN
+ || session->forced_auth_type == 0)
+ &&
+ (session->avail_auth_type & SMTPAUTH_LOGIN) != 0)
+ smtp_auth_login(session);
+ else if ((session->forced_auth_type == SMTPAUTH_PLAIN
+ || session->forced_auth_type == 0)
+ &&
+ (session->avail_auth_type & SMTPAUTH_PLAIN) != 0)
+ smtp_auth_plain(session);
+ else if (session->forced_auth_type == 0) {
+ log_warning(LOG_PROTOCOL, _("No SMTP AUTH method available\n"));
+ return SM_AUTHFAIL;
+ } else {
+ log_warning(LOG_PROTOCOL, _("Selected SMTP AUTH method not available\n"));
return SM_AUTHFAIL;
}
- switch (authtype) {
- case SMTPAUTH_LOGIN:
- if (!strncmp(buf, "334 ", 4))
- base64_encode(buf, session->user, strlen(session->user));
- else
- /* Server rejects AUTH */
- g_snprintf(buf, sizeof(buf), "*");
+ return SM_OK;
+}
- sock_printf(sock, "%s\r\n", buf);
- if (verbose) log_print("ESMTP> [USERID]\n");
+static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
+{
+ gchar buf[MESSAGEBUFSIZE];
- smtp_ok(sock, buf, sizeof(buf));
+ switch (session->auth_type) {
+ case SMTPAUTH_LOGIN:
+ session->state = SMTP_AUTH_LOGIN_USER;
- if (!strncmp(buf, "334 ", 4))
- base64_encode(buf, session->pass, strlen(session->pass));
- else
- /* Server rejects AUTH */
- g_snprintf(buf, sizeof(buf), "*");
+ if (!strncmp(msg, "334 ", 4)) {
+ base64_encode(buf, session->user, strlen(session->user));
- sock_printf(sock, "%s\r\n", buf);
- if (verbose) log_print("ESMTP> [PASSWORD]\n");
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ buf) < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> [USERID]\n");
+ } else {
+ /* Server rejects AUTH */
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ "*") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> *\n");
+ }
break;
case SMTPAUTH_CRAM_MD5:
- if (!strncmp(buf, "334 ", 4)) {
- challenge = g_malloc(strlen(buf + 4) + 1);
- challengelen = base64_decode(challenge, buf + 4, -1);
+ session->state = SMTP_AUTH_CRAM_MD5;
+
+ if (!strncmp(msg, "334 ", 4)) {
+ gchar *response;
+ gchar *response64;
+ gchar *challenge;
+ gint challengelen;
+ guchar hexdigest[33];
+
+ challenge = g_malloc(strlen(msg + 4) + 1);
+ challengelen = base64_decode(challenge, msg + 4, -1);
challenge[challengelen] = '\0';
- if (verbose)
- log_print("ESMTP< [Decoded: %s]\n", challenge);
+ log_print(LOG_PROTOCOL, "ESMTP< [Decoded: %s]\n", challenge);
g_snprintf(buf, sizeof(buf), "%s", session->pass);
md5_hex_hmac(hexdigest, challenge, challengelen,
response = g_strdup_printf
("%s %s", session->user, hexdigest);
- if (verbose)
- log_print("ESMTP> [Encoded: %s]\n", response);
+ log_print(LOG_PROTOCOL, "ESMTP> [Encoded: %s]\n", response);
response64 = g_malloc((strlen(response) + 3) * 2 + 1);
base64_encode(response64, response, strlen(response));
g_free(response);
- sock_printf(sock, "%s\r\n", response64);
- if (verbose) log_print("ESMTP> %s\n", response64);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ response64) < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> %s\n", response64);
g_free(response64);
} else {
/* Server rejects AUTH */
- g_snprintf(buf, sizeof(buf), "*");
- sock_printf(sock, "%s\r\n", buf);
- if (verbose)
- log_print("ESMTP> %s\n", buf);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+ "*") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> *\n");
}
break;
case SMTPAUTH_DIGEST_MD5:
default:
/* stop smtp_auth when no correct authtype */
- g_snprintf(buf, sizeof(buf), "*");
- sock_printf(sock, "%s\r\n", buf);
- if (verbose) log_print("ESMTP> %s\n", buf);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> *\n");
break;
}
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_ehlo(SMTPSession *session, const gchar *hostname,
- SMTPAuthType *avail_auth_type)
+static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
{
- SockInfo *sock;
- gchar buf[MSGBUFSIZE];
-
- UI_UPDATE(session, SMTP_EHLO);
+ gchar buf[MESSAGEBUFSIZE];
- sock = SESSION(session)->sock;
+ session->state = SMTP_AUTH_LOGIN_PASS;
- *avail_auth_type = 0;
+ if (!strncmp(msg, "334 ", 4))
+ base64_encode(buf, session->pass, strlen(session->pass));
+ else
+ /* Server rejects AUTH */
+ g_snprintf(buf, sizeof(buf), "*");
- sock_printf(sock, "EHLO %s\r\n", hostname);
- if (verbose)
- log_print("ESMTP> EHLO %s\n", hostname);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+ return SM_ERROR;
- while ((sock_gets(sock, buf, sizeof(buf) - 1)) != -1) {
- if (strlen(buf) < 4)
- return SM_ERROR;
- strretchomp(buf);
-
- if (verbose)
- log_print("ESMTP< %s\n", buf);
-
- if (strncmp(buf, "250-", 4) == 0) {
- gchar *p = buf;
- p += 4;
- if (g_strncasecmp(p, "AUTH", 4) == 0) {
- p += 5;
- if (strcasestr(p, "LOGIN"))
- *avail_auth_type |= SMTPAUTH_LOGIN;
- if (strcasestr(p, "CRAM-MD5"))
- *avail_auth_type |= SMTPAUTH_CRAM_MD5;
- if (strcasestr(p, "DIGEST-MD5"))
- *avail_auth_type |= SMTPAUTH_DIGEST_MD5;
- } else if (g_strncasecmp(p, "STARTTLS", 8) == 0) {
- p += 9;
- *avail_auth_type |= SMTPAUTH_TLS_AVAILABLE;
- }
- } else if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
- (buf[3] == ' ' || buf[3] == '\0'))
- return SM_OK;
- else if (buf[3] != '-')
- return SM_ERROR;
- else if (buf[0] == '5' && buf[1] == '0' &&
- (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
- return SM_ERROR;
- }
+ log_print(LOG_PROTOCOL, "ESMTP> [PASSWORD]\n");
- return SM_UNRECOVERABLE;
+ return SM_OK;
}
-static gint smtp_starttls(SMTPSession *session)
+static gint smtp_ehlo(SMTPSession *session)
{
- SockInfo *sock;
+ gchar buf[MESSAGEBUFSIZE];
- UI_UPDATE(session, SMTP_STARTTLS);
+ session->state = SMTP_EHLO;
- sock = SESSION(session)->sock;
+ session->avail_auth_type = 0;
- sock_printf(sock, "STARTTLS\r\n");
- if (verbose)
- log_print("ESMTP> STARTTLS\n");
+ g_snprintf(buf, sizeof(buf), "EHLO %s",
+ session->hostname ? session->hostname : get_domain_name());
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> %s\n", buf);
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len)
+static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
{
- SockInfo *sock;
+ if (strncmp(msg, "250", 3) == 0) {
+ const gchar *p = msg;
+ p += 3;
+ if (*p == '-' || *p == ' ') p++;
+ if (g_ascii_strncasecmp(p, "AUTH", 4) == 0) {
+ p += 5;
+ if (strcasestr(p, "PLAIN"))
+ session->avail_auth_type |= SMTPAUTH_PLAIN;
+ if (strcasestr(p, "LOGIN"))
+ session->avail_auth_type |= SMTPAUTH_LOGIN;
+ if (strcasestr(p, "CRAM-MD5"))
+ session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
+ if (strcasestr(p, "DIGEST-MD5"))
+ session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
+ }
+ if (g_ascii_strncasecmp(p, "SIZE", 4) == 0) {
+ p += 5;
+ session->max_message_size = atoi(p);
+ session->esmtp_flags |= ESMTP_SIZE;
+ }
+ if (g_ascii_strncasecmp(p, "STARTTLS", 8) == 0) {
+ p += 9;
+ session->avail_auth_type |= SMTPAUTH_TLS_AVAILABLE;
+ }
+ return SM_OK;
+ } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
+ (msg[3] == ' ' || msg[3] == '\0'))
+ return SM_OK;
+ else if (msg[0] == '5' && msg[1] == '0' &&
+ (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
+ return SM_ERROR;
+
+ return SM_ERROR;
+}
+
+#ifdef USE_GNUTLS
+static gint smtp_starttls(SMTPSession *session)
+{
+ session->state = SMTP_STARTTLS;
- UI_UPDATE(session, SMTP_AUTH);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> STARTTLS\n");
+
+ return SM_OK;
+}
+#endif
- sock = SESSION(session)->sock;
+static gint smtp_auth_cram_md5(SMTPSession *session)
+{
+ session->state = SMTP_AUTH;
+ session->auth_type = SMTPAUTH_CRAM_MD5;
- sock_printf(sock, "AUTH CRAM-MD5\r\n");
- if (verbose)
- log_print("ESMTP> AUTH CRAM-MD5\n");
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> AUTH CRAM-MD5\n");
- return smtp_ok(sock, buf, len);
+ return SM_OK;
}
-static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len)
+static gint smtp_auth_plain(SMTPSession *session)
{
- SockInfo *sock;
+ gchar buf[MESSAGEBUFSIZE];
+
+ /*
+ * +1 +1 +1
+ * \0<user>\0<pass>\0
+ */
+ int b64len = (1 + strlen(session->user) + 1 + strlen(session->pass) + 1);
+ gchar *b64buf = g_malloc(b64len);
+
+ /* use the char *ptr to walk the base64 string with embedded \0 */
+ char *a = b64buf;
+ int b64cnt = 0;
+
+ session->state = SMTP_AUTH_PLAIN;
+ session->auth_type = SMTPAUTH_PLAIN;
+
+ memset(buf, 0, sizeof buf);
+
+ /*
+ * have to construct the string bit by bit. sprintf can't do it in one.
+ * first field is null, so string is \0<user>\0<password>
+ */
+ *a = 0;
+ a++;
+
+ g_snprintf (a, b64len - 1, "%s", session->user);
+
+ b64cnt = strlen(session->user)+1;
+ a += b64cnt;
- UI_UPDATE(session, SMTP_AUTH);
+ g_snprintf (a, b64len - b64cnt - 1, "%s", session->pass);
+ b64cnt += strlen(session->pass) + 1;
- sock = SESSION(session)->sock;
+ /*
+ * reuse the char *ptr to offset into the textbuf to meld
+ * the plaintext ESMTP message and the base64 string value
+ */
+ strcpy(buf, "AUTH PLAIN ");
+ a = buf + strlen(buf);
+ base64_encode(a, b64buf, b64cnt);
- sock_printf(sock, "AUTH LOGIN\r\n");
- if (verbose)
- log_print("ESMTP> AUTH LOGIN\n");
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+ return SM_ERROR;
+
+ log_print(LOG_PROTOCOL, "ESMTP> [AUTH PLAIN]\n");
+
+ g_free(b64buf);
- return smtp_ok(sock, buf, len);
+ return SM_OK;
}
-gint smtp_helo(SMTPSession *session, const gchar *hostname)
+static gint smtp_auth_login(SMTPSession *session)
{
- SockInfo *sock;
+ session->state = SMTP_AUTH;
+ session->auth_type = SMTPAUTH_LOGIN;
+
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "ESMTP> AUTH LOGIN\n");
- UI_UPDATE(session, SMTP_HELO);
+ return SM_OK;
+}
- sock = SESSION(session)->sock;
+static gint smtp_helo(SMTPSession *session)
+{
+ gchar buf[MESSAGEBUFSIZE];
- sock_printf(sock, "HELO %s\r\n", hostname);
- if (verbose)
- log_print("SMTP> HELO %s\n", hostname);
+ session->state = SMTP_HELO;
- return smtp_ok(sock, NULL, 0);
+ g_snprintf(buf, sizeof(buf), "HELO %s",
+ session->hostname ? session->hostname : get_domain_name());
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "SMTP> %s\n", buf);
+
+ return SM_OK;
}
-gint smtp_rcpt(SMTPSession *session, const gchar *to)
+static gint smtp_rcpt(SMTPSession *session)
{
- SockInfo *sock;
- gchar buf[MSGBUFSIZE];
+ gchar buf[MESSAGEBUFSIZE];
+ gchar *to;
+
+ cm_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
- UI_UPDATE(session, SMTP_RCPT);
+ session->state = SMTP_RCPT;
- sock = SESSION(session)->sock;
+ to = (gchar *)session->cur_to->data;
if (strchr(to, '<'))
- g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
+ g_snprintf(buf, sizeof(buf), "RCPT TO:%s", to);
else
- g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
+ g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>", to);
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "SMTP> %s\n", buf);
- sock_printf(sock, "%s\r\n", buf);
- if (verbose)
- log_print("SMTP> %s\n", buf);
+ session->cur_to = session->cur_to->next;
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_data(SMTPSession *session)
+static gint smtp_data(SMTPSession *session)
{
- SockInfo *sock;
-
- UI_UPDATE(session, SMTP_DATA);
-
- sock = SESSION(session)->sock;
+ session->state = SMTP_DATA;
- sock_printf(sock, "DATA\r\n");
- if (verbose)
- log_print("SMTP> DATA\n");
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "SMTP> DATA\n");
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_rset(SMTPSession *session)
+static gint smtp_send_data(SMTPSession *session)
{
- SockInfo *sock;
+ session->state = SMTP_SEND_DATA;
- UI_UPDATE(session, SMTP_RSET);
+ session_send_data(SESSION(session), session->send_data,
+ session->send_data_len);
- sock = SESSION(session)->sock;
+ return SM_OK;
+}
- sock_printf(sock, "RSET\r\n");
- if (verbose)
- log_print("SMTP> RSET\n");
+static gint smtp_make_ready(SMTPSession *session)
+{
+ session->state = SMTP_MAIL_SENT_OK;
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
gint smtp_quit(SMTPSession *session)
{
- SockInfo *sock;
+ session->state = SMTP_QUIT;
- UI_UPDATE(session, SMTP_QUIT);
+ session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
+ log_print(LOG_PROTOCOL, "SMTP> QUIT\n");
- sock = SESSION(session)->sock;
+ return SM_OK;
+}
- sock_printf(sock, "QUIT\r\n");
- if (verbose)
- log_print("SMTP> QUIT\n");
+static gint smtp_eom(SMTPSession *session)
+{
+ session->state = SMTP_EOM;
+
+ if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".") < 0)
+ return SM_ERROR;
+ log_print(LOG_PROTOCOL, "SMTP> . (EOM)\n");
- return smtp_ok(sock, NULL, 0);
+ return SM_OK;
}
-gint smtp_eom(SMTPSession *session)
+static gint smtp_session_recv_msg(Session *session, const gchar *msg)
{
- SockInfo *sock;
+ SMTPSession *smtp_session = SMTP_SESSION(session);
+ gboolean cont = FALSE;
+ gint ret = 0;
+
+ if (strlen(msg) < 4) {
+ log_warning(LOG_PROTOCOL, _("bad SMTP response\n"));
+ return -1;
+ }
- UI_UPDATE(session, SMTP_EOM);
+ switch (smtp_session->state) {
+ case SMTP_EHLO:
+ case SMTP_STARTTLS:
+ case SMTP_AUTH:
+ case SMTP_AUTH_PLAIN:
+ case SMTP_AUTH_LOGIN_USER:
+ case SMTP_AUTH_LOGIN_PASS:
+ case SMTP_AUTH_CRAM_MD5:
+ log_print(LOG_PROTOCOL, "ESMTP< %s\n", msg);
+ break;
+ default:
+ log_print(LOG_PROTOCOL, "SMTP< %s\n", msg);
+ break;
+ }
- sock = SESSION(session)->sock;
+ /* ignore all multiline responses except for EHLO */
+ if (msg[3] == '-' && smtp_session->state != SMTP_EHLO)
+ return session_recv_msg(session);
+
+ if (msg[0] == '5' && msg[1] == '0' &&
+ (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
+ log_warning(LOG_PROTOCOL, _("error occurred on SMTP session\n"));
+ smtp_session->state = SMTP_ERROR;
+ smtp_session->error_val = SM_ERROR;
+ g_free(smtp_session->error_msg);
+ smtp_session->error_msg = g_strdup(msg);
+ return -1;
+ }
- sock_printf(sock, ".\r\n");
- if (verbose)
- log_print("SMTP> . (EOM)\n");
+ if (!strncmp(msg, "535", 3)) {
+ log_warning(LOG_PROTOCOL, _("error occurred on authentication\n"));
+ smtp_session->state = SMTP_ERROR;
+ smtp_session->error_val = SM_AUTHFAIL;
+ g_free(smtp_session->error_msg);
+ smtp_session->error_msg = g_strdup(msg);
+ return -1;
+ }
- return smtp_ok(sock, NULL, 0);
-}
+ if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
+ log_warning(LOG_PROTOCOL, _("error occurred on SMTP session\n"));
+ smtp_session->state = SMTP_ERROR;
+ smtp_session->error_val = SM_ERROR;
+ g_free(smtp_session->error_msg);
+ smtp_session->error_msg = g_strdup(msg);
+ return -1;
+ }
-static gint smtp_ok(SockInfo *sock, gchar *buf, gint len)
-{
- gchar tmpbuf[MSGBUFSIZE];
+ if (msg[3] == '-')
+ cont = TRUE;
+ else if (msg[3] != ' ' && msg[3] != '\0') {
+ log_warning(LOG_PROTOCOL, _("bad SMTP response\n"));
+ smtp_session->state = SMTP_ERROR;
+ smtp_session->error_val = SM_UNRECOVERABLE;
+ return -1;
+ }
- if (!buf) {
- buf = tmpbuf;
- len = sizeof(tmpbuf);
+ switch (smtp_session->state) {
+ case SMTP_READY:
+ if (strstr(msg, "ESMTP"))
+ smtp_session->is_esmtp = TRUE;
+ case SMTP_CONNECTED:
+#ifdef USE_GNUTLS
+ if (smtp_session->user || session->ssl_type != SSL_NONE ||
+ smtp_session->is_esmtp)
+#else
+ if (smtp_session->user || smtp_session->is_esmtp)
+#endif
+ ret = smtp_ehlo(smtp_session);
+ else
+ ret = smtp_helo(smtp_session);
+ break;
+ case SMTP_HELO:
+ ret = smtp_from(smtp_session);
+ break;
+ case SMTP_EHLO:
+ ret = smtp_ehlo_recv(smtp_session, msg);
+ if (cont == TRUE)
+ break;
+ if (smtp_session->max_message_size > 0
+ && smtp_session->max_message_size <
+ smtp_session->send_data_len) {
+ log_warning(LOG_PROTOCOL, _("Message is too big "
+ "(Maximum size is %s)\n"),
+ to_human_readable(
+ (goffset)(smtp_session->max_message_size)));
+ smtp_session->state = SMTP_ERROR;
+ smtp_session->error_val = SM_ERROR;
+ return -1;
+ }
+#ifdef USE_GNUTLS
+ if (session->ssl_type == SSL_STARTTLS &&
+ smtp_session->tls_init_done == FALSE) {
+ ret = smtp_starttls(smtp_session);
+ break;
+ }
+#endif
+ if (smtp_session->user) {
+ if (smtp_auth(smtp_session) != SM_OK) {
+#ifdef USE_GNUTLS
+ if (session->ssl_type == SSL_NONE
+ && smtp_session->tls_init_done == FALSE
+ && (smtp_session->avail_auth_type & SMTPAUTH_TLS_AVAILABLE))
+ ret = smtp_starttls(smtp_session);
+ else
+#endif
+ ret = smtp_from(smtp_session);
+ }
+ } else
+ ret = smtp_from(smtp_session);
+ break;
+ case SMTP_STARTTLS:
+#ifdef USE_GNUTLS
+ if (session_start_tls(session) < 0) {
+ log_warning(LOG_PROTOCOL, _("couldn't start TLS session\n"));
+ smtp_session->state = SMTP_ERROR;
+ smtp_session->error_val = SM_ERROR;
+ return -1;
+ }
+ smtp_session->tls_init_done = TRUE;
+ ret = smtp_ehlo(smtp_session);
+#endif
+ break;
+ case SMTP_AUTH:
+ ret = smtp_auth_recv(smtp_session, msg);
+ break;
+ case SMTP_AUTH_LOGIN_USER:
+ ret = smtp_auth_login_user_recv(smtp_session, msg);
+ break;
+ case SMTP_AUTH_PLAIN:
+ case SMTP_AUTH_LOGIN_PASS:
+ case SMTP_AUTH_CRAM_MD5:
+ ret = smtp_from(smtp_session);
+ break;
+ case SMTP_FROM:
+ if (smtp_session->cur_to)
+ ret = smtp_rcpt(smtp_session);
+ break;
+ case SMTP_RCPT:
+ if (smtp_session->cur_to)
+ ret = smtp_rcpt(smtp_session);
+ else
+ ret = smtp_data(smtp_session);
+ break;
+ case SMTP_DATA:
+ ret = smtp_send_data(smtp_session);
+ break;
+ case SMTP_EOM:
+ smtp_make_ready(smtp_session);
+ break;
+ case SMTP_QUIT:
+ session_disconnect(session);
+ break;
+ case SMTP_ERROR:
+ default:
+ log_warning(LOG_PROTOCOL, _("error occurred on SMTP session\n"));
+ smtp_session->error_val = SM_ERROR;
+ return -1;
}
- while ((sock_gets(sock, buf, len - 1)) != -1) {
- if (strlen(buf) < 4)
- return SM_ERROR;
- strretchomp(buf);
+ if (cont && ret == SM_OK)
+ return session_recv_msg(session);
- if (verbose)
- log_print("SMTP< %s\n", buf);
+ if (ret != SM_OK)
+ smtp_session->error_val = SM_ERROR;
- if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
- (buf[3] == ' ' || buf[3] == '\0'))
- return SM_OK;
- else if (buf[3] != '-')
- return SM_ERROR;
- else if (buf[0] == '5' && buf[1] == '0' &&
- (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
- return SM_ERROR;
- }
+ return ret == SM_OK ? 0 : -1;
+}
- return SM_UNRECOVERABLE;
+static gint smtp_session_send_data_finished(Session *session, guint len)
+{
+ return smtp_eom(SMTP_SESSION(session));
}