#include "socket.h"
#include "ssl.h"
#include "smtp.h"
-#include "esmtp.h"
#include "prefs_common.h"
#include "prefs_account.h"
#include "account.h"
gboolean cancelled;
};
-#if USE_SSL
-static SockInfo *send_smtp_open (const gchar *server, gushort port,
- const gchar *domain, gboolean use_smtp_auth,
- SSLSMTPType ssl_type);
-#else
-static SockInfo *send_smtp_open (const gchar *server, gushort port,
- const gchar *domain, gboolean use_smtp_auth);
-#endif
static gint send_message_data (SendProgressDialog *dialog, SockInfo *sock,
FILE *fp, gint size);
#define EXIT_IF_CANCELLED() \
{ \
if (dialog->cancelled) { \
- sock_close(smtp_sock); \
+ if (session) \
+ session_destroy(session); \
send_progress_dialog_destroy(dialog); \
return -1; \
} \
EXIT_IF_CANCELLED(); \
if (!(f)) { \
log_warning("Error occurred while %s\n", s); \
- sock_close(smtp_sock); \
+ if (session) \
+ session_destroy(session); \
send_progress_dialog_destroy(dialog); \
return -1; \
} \
if ((ok = (f)) != SM_OK) { \
log_warning("Error occurred while %s\n", s); \
if (ok == SM_AUTHFAIL) { \
- log_warning("SMTP AUTH failed\n"); \
+ log_warning(_("SMTP AUTH failed\n")); \
if (ac_prefs->tmp_pass) { \
g_free(ac_prefs->tmp_pass); \
ac_prefs->tmp_pass = NULL; \
ac_prefs->tmp_smtp_pass = NULL; \
} \
} \
- if (smtp_quit(smtp_sock) != SM_OK) \
- log_warning("Error occurred while sending QUIT\n"); \
- sock_close(smtp_sock); \
+ if (smtp_quit(session->sock) != SM_OK) \
+ log_warning(_("Error occurred while sending QUIT\n")); \
+ session_destroy(session); \
send_progress_dialog_destroy(dialog); \
return -1; \
} \
gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list,
FILE *fp)
{
- SockInfo *smtp_sock = NULL;
SendProgressDialog *dialog;
+ Session *session = NULL;
GtkCList *clist;
const gchar *text[3];
gchar buf[BUFFSIZE];
GTK_EVENTS_FLUSH();
#if USE_SSL
- SEND_EXIT_IF_ERROR((smtp_sock = send_smtp_open
+ SEND_EXIT_IF_ERROR((session = smtp_session_new
(ac_prefs->smtp_server, port, domain,
- ac_prefs->use_smtp_auth, ac_prefs->ssl_smtp)),
+ user, pass, ac_prefs->ssl_smtp)),
"connecting to server");
#else
- SEND_EXIT_IF_ERROR((smtp_sock = send_smtp_open
+ SEND_EXIT_IF_ERROR((session = smtp_session_new
(ac_prefs->smtp_server, port, domain,
- ac_prefs->use_smtp_auth)),
+ user, pass)),
"connecting to server");
#endif
GTK_EVENTS_FLUSH();
SEND_EXIT_IF_NOTOK
- (smtp_from(smtp_sock, ac_prefs->address, user, pass,
- ac_prefs->use_smtp_auth),
+ (smtp_from(SMTP_SESSION(session), ac_prefs->address),
"sending MAIL FROM");
progress_dialog_set_label(dialog->dialog, _("Sending RCPT TO..."));
GTK_EVENTS_FLUSH();
for (cur = to_list; cur != NULL; cur = cur->next)
- SEND_EXIT_IF_NOTOK(smtp_rcpt(smtp_sock, (gchar *)cur->data),
+ SEND_EXIT_IF_NOTOK(smtp_rcpt(session->sock, (gchar *)cur->data),
"sending RCPT TO");
progress_dialog_set_label(dialog->dialog, _("Sending DATA..."));
GTK_EVENTS_FLUSH();
- SEND_EXIT_IF_NOTOK(smtp_data(smtp_sock), "sending DATA");
+ SEND_EXIT_IF_NOTOK(smtp_data(session->sock), "sending DATA");
/* send main part */
- SEND_EXIT_IF_ERROR(send_message_data(dialog, smtp_sock, fp, size) == 0,
- "sending data");
+ SEND_EXIT_IF_ERROR
+ (send_message_data(dialog, session->sock, fp, size) == 0,
+ "sending data");
progress_dialog_set_label(dialog->dialog, _("Quitting..."));
GTK_EVENTS_FLUSH();
- SEND_EXIT_IF_NOTOK(smtp_eom(smtp_sock), "terminating data");
- SEND_EXIT_IF_NOTOK(smtp_quit(smtp_sock), "sending QUIT");
+ SEND_EXIT_IF_NOTOK(smtp_eom(session->sock), "terminating data");
+ SEND_EXIT_IF_NOTOK(smtp_quit(session->sock), "sending QUIT");
- sock_close(smtp_sock);
+ session_destroy(session);
send_progress_dialog_destroy(dialog);
return 0;
#undef SEND_EXIT_IF_ERROR
#undef SEND_DIALOG_UPDATE
-#if USE_SSL
-static SockInfo *send_smtp_open(const gchar *server, gushort port,
- const gchar *domain, gboolean use_smtp_auth,
- SSLSMTPType ssl_type)
-#else
-static SockInfo *send_smtp_open(const gchar *server, gushort port,
- const gchar *domain, gboolean use_smtp_auth)
-#endif
-{
- SockInfo *sock;
- gint val;
-
- g_return_val_if_fail(server != NULL, NULL);
-
- if ((sock = sock_connect(server, port)) == NULL) {
- log_warning(_("Can't connect to SMTP server: %s:%d\n"),
- server, port);
- return NULL;
- }
-
-#if USE_SSL
- if (ssl_type == SSL_SMTP_TUNNEL && !ssl_init_socket(sock)) {
- log_warning(_("SSL connection failed"));
- sock_close(sock);
- return NULL;
- }
-#endif
-
- if (smtp_ok(sock) != SM_OK) {
- log_warning(_("Error occurred while connecting to %s:%d\n"),
- server, port);
- sock_close(sock);
- return NULL;
- }
-
-#if USE_SSL
- val = smtp_helo(sock, domain ? domain : get_domain_name(),
- use_smtp_auth || ssl_type == SSL_SMTP_STARTTLS);
-#else
- val = smtp_helo(sock, domain ? domain : get_domain_name(),
- use_smtp_auth);
-#endif
-
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending HELO\n"));
- sock_close(sock);
- return NULL;
- }
-
-#if USE_SSL
- if (ssl_type == SSL_SMTP_STARTTLS) {
- val = esmtp_starttls(sock);
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending STARTTLS\n"));
- sock_close(sock);
- return NULL;
- }
- if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
- sock_close(sock);
- return NULL;
- }
- val = esmtp_ehlo(sock, domain ? domain : get_domain_name());
- if (val != SM_OK) {
- log_warning(_("Error occurred while sending EHLO\n"));
- sock_close(sock);
- return NULL;
- }
- }
-#endif
-
- return sock;
-}
-
-
static SendProgressDialog *send_progress_dialog_create(void)
{
SendProgressDialog *dialog;
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2002 Hiroyuki Yamamoto
*
* 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
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include <glib.h>
#include <stdio.h>
#include <string.h>
+#include "intl.h"
#include "smtp.h"
-#include "esmtp.h"
#include "socket.h"
+#include "md5.h"
+#include "base64.h"
#include "utils.h"
-#define MSGBUFSIZE 8192
-
static gint verbose = 1;
-static gchar smtp_response[MSGBUFSIZE];
-gint smtp_helo(SockInfo *sock, const gchar *hostname, gboolean esmtp)
+static gint smtp_starttls(SockInfo *sock);
+static gint smtp_auth_cram_md5(SockInfo *sock, gchar *buf, gint len);
+static gint smtp_auth_login(SockInfo *sock, gchar *buf, gint len);
+static gint smtp_ok(SockInfo *sock, gchar *buf, gint len);
+
+#if USE_SSL
+Session *smtp_session_new(const gchar *server, gushort port,
+ const gchar *domain,
+ const gchar *user, const gchar *pass,
+ SSLSMTPType ssl_type)
+#else
+Session *smtp_session_new(const gchar *server, gushort port,
+ const gchar *domain,
+ const gchar *user, const gchar *pass)
+#endif
{
- if (esmtp)
- return esmtp_ehlo(sock, hostname);
- else {
- sock_printf(sock, "HELO %s\r\n", hostname);
- if (verbose)
- log_print("SMTP> HELO %s\n", hostname);
+ SMTPSession *session;
+ SockInfo *sock;
+ gboolean use_esmtp;
+ SMTPAuthType avail_auth_type = 0;
+ gint val;
+
+ g_return_val_if_fail(server != NULL, NULL);
+
+#if USE_SSL
+ use_esmtp = user != NULL || ssl_type == SSL_SMTP_STARTTLS;
+#else
+ use_esmtp = user != NULL;
+#endif
+
+ if ((sock = sock_connect(server, port)) == NULL) {
+ log_warning(_("Can't connect to SMTP server: %s:%d\n"),
+ server, port);
+ return NULL;
+ }
+
+#if USE_SSL
+ if (ssl_type == SSL_SMTP_TUNNEL && !ssl_init_socket(sock)) {
+ log_warning(_("SSL connection failed"));
+ sock_close(sock);
+ return NULL;
+ }
+#endif
- return smtp_ok(sock);
+ if (smtp_ok(sock, NULL, 0) != SM_OK) {
+ log_warning(_("Error occurred while connecting to %s:%d\n"),
+ server, port);
+ sock_close(sock);
+ return NULL;
}
+
+ if (!domain)
+ domain = get_domain_name();
+
+ if (use_esmtp)
+ val = smtp_ehlo(sock, domain, &avail_auth_type);
+ else
+ val = smtp_helo(sock, domain);
+ if (val != SM_OK) {
+ log_warning(_("Error occurred while sending HELO\n"));
+ sock_close(sock);
+ return NULL;
+ }
+
+#if USE_SSL
+ if (ssl_type == SSL_SMTP_STARTTLS) {
+ val = smtp_starttls(sock);
+ if (val != SM_OK) {
+ log_warning(_("Error occurred while sending STARTTLS\n"));
+ sock_close(sock);
+ return NULL;
+ }
+ if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
+ sock_close(sock);
+ return NULL;
+ }
+ val = smtp_ehlo(sock, domain, &avail_auth_type);
+ if (val != SM_OK) {
+ log_warning(_("Error occurred while sending EHLO\n"));
+ sock_close(sock);
+ return NULL;
+ }
+ }
+#endif
+
+ session = g_new(SMTPSession, 1);
+ SESSION(session)->type = SESSION_SMTP;
+ SESSION(session)->server = g_strdup(server);
+ SESSION(session)->sock = sock;
+ SESSION(session)->connected = TRUE;
+ SESSION(session)->phase = SESSION_READY;
+ SESSION(session)->last_access_time = 0;
+ SESSION(session)->data = NULL;
+ session->avail_auth_type = avail_auth_type;
+ session->user = user ? g_strdup(user) : NULL;
+ session->pass = pass ? g_strdup(pass) :
+ user ? g_strdup("") : NULL;
+
+ return SESSION(session);
+}
+
+void smtp_session_destroy(SMTPSession *session)
+{
+ sock_close(SESSION(session)->sock);
+ SESSION(session)->sock = NULL;
+
+ g_free(session->user);
+ g_free(session->pass);
}
-gint smtp_from(SockInfo *sock, const gchar *from,
- const gchar *userid, const gchar *passwd,
- gboolean use_smtp_auth)
+gint smtp_from(SMTPSession *session, const gchar *from)
{
gchar buf[MSGBUFSIZE];
- SMTPAuthType authtype;
-
- if (use_smtp_auth) {
- /* exist AUTH-Type CRAM_MD5 */
- if (!smtp_auth_methods[SMTPAUTH_CRAM_MD5]
- || esmtp_auth_cram_md5(sock) == SM_ERROR) {
- /* exist AUTH-Type LOGIN */
- if (!smtp_auth_methods[SMTPAUTH_LOGIN]
- || esmtp_auth_login(sock) == SM_ERROR)
- return SM_ERROR;
- else
- authtype = SMTPAUTH_LOGIN;
- } else
- authtype = SMTPAUTH_CRAM_MD5;
-
- if (esmtp_auth(sock, authtype, userid, passwd) != SM_OK)
+
+ g_return_val_if_fail(session != NULL, SM_ERROR);
+ g_return_val_if_fail(from != NULL, SM_ERROR);
+
+ if (session->user) {
+ if (smtp_auth(session) != SM_OK)
return SM_AUTHFAIL;
}
else
g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", from);
- sock_printf(sock, "%s\r\n", buf);
+ sock_printf(SESSION(session)->sock, "%s\r\n", buf);
if (verbose)
log_print("SMTP> %s\n", buf);
- return smtp_ok(sock);
+ return smtp_ok(SESSION(session)->sock, NULL, 0);
+}
+
+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);
+
+ sock = SESSION(session)->sock;
+
+ if ((session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0 &&
+ smtp_auth_cram_md5(sock, buf, sizeof(buf)) == SM_OK)
+ authtype = SMTPAUTH_CRAM_MD5;
+ else if ((session->avail_auth_type & SMTPAUTH_LOGIN) != 0 &&
+ smtp_auth_login(sock, buf, sizeof(buf)) == SM_OK)
+ authtype = SMTPAUTH_LOGIN;
+ else {
+ log_warning(_("SMTP AUTH not available\n"));
+ return SM_AUTHFAIL;
+ }
+
+ switch (authtype) {
+ case SMTPAUTH_LOGIN:
+ if (!strncmp(buf, "334 ", 4))
+ to64frombits(buf, session->user, strlen(session->user));
+ else
+ /* Server rejects AUTH */
+ g_snprintf(buf, sizeof(buf), "*");
+
+ sock_printf(sock, "%s\r\n", buf);
+ if (verbose) log_print("ESMTP> [USERID]\n");
+
+ smtp_ok(sock, buf, sizeof(buf));
+
+ if (!strncmp(buf, "334 ", 4))
+ to64frombits(buf, session->pass, strlen(session->pass));
+ else
+ /* Server rejects AUTH */
+ g_snprintf(buf, sizeof(buf), "*");
+
+ sock_printf(sock, "%s\r\n", buf);
+ if (verbose) log_print("ESMTP> [PASSWORD]\n");
+ break;
+ case SMTPAUTH_CRAM_MD5:
+ if (!strncmp(buf, "334 ", 4)) {
+ challenge = g_malloc(strlen(buf + 4) + 1);
+ challengelen = from64tobits(challenge, buf + 4);
+ challenge[challengelen] = '\0';
+ if (verbose)
+ log_print("ESMTP< [Decoded: %s]\n", challenge);
+
+ g_snprintf(buf, sizeof(buf), "%s", session->pass);
+ md5_hex_hmac(hexdigest, challenge, challengelen,
+ buf, strlen(session->pass));
+ g_free(challenge);
+
+ response = g_strdup_printf
+ ("%s %s", session->user, hexdigest);
+ if (verbose)
+ log_print("ESMTP> [Encoded: %s]\n", response);
+
+ response64 = g_malloc((strlen(response) + 3) * 2 + 1);
+ to64frombits(response64, response, strlen(response));
+ g_free(response);
+
+ sock_printf(sock, "%s\r\n", response64);
+ if (verbose) log_print("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);
+ }
+ 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);
+ break;
+ }
+
+ return smtp_ok(sock, NULL, 0);
+}
+
+gint smtp_ehlo(SockInfo *sock, const gchar *hostname,
+ SMTPAuthType *avail_auth_type)
+{
+ gchar buf[MSGBUFSIZE];
+
+ *avail_auth_type = 0;
+
+ sock_printf(sock, "EHLO %s\r\n", hostname);
+ if (verbose)
+ log_print("ESMTP> EHLO %s\n", hostname);
+
+ 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 ", 5) == 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 ((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 SM_UNRECOVERABLE;
+}
+
+static gint smtp_starttls(SockInfo *sock)
+{
+ sock_printf(sock, "STARTTLS\r\n");
+ if (verbose)
+ log_print("ESMTP> STARTTLS\n");
+
+ return smtp_ok(sock, NULL, 0);
+}
+
+static gint smtp_auth_cram_md5(SockInfo *sock, gchar *buf, gint len)
+{
+ sock_printf(sock, "AUTH CRAM-MD5\r\n");
+ if (verbose)
+ log_print("ESMTP> AUTH CRAM-MD5\n");
+
+ return smtp_ok(sock, buf, len);
+}
+
+static gint smtp_auth_login(SockInfo *sock, gchar *buf, gint len)
+{
+ sock_printf(sock, "AUTH LOGIN\r\n");
+ if (verbose)
+ log_print("ESMTP> AUTH LOGIN\n");
+
+ return smtp_ok(sock, buf, len);
+}
+
+gint smtp_helo(SockInfo *sock, const gchar *hostname)
+{
+ sock_printf(sock, "HELO %s\r\n", hostname);
+ if (verbose)
+ log_print("SMTP> HELO %s\n", hostname);
+
+ return smtp_ok(sock, NULL, 0);
}
gint smtp_rcpt(SockInfo *sock, const gchar *to)
if (verbose)
log_print("SMTP> %s\n", buf);
- return smtp_ok(sock);
+ return smtp_ok(sock, NULL, 0);
}
gint smtp_data(SockInfo *sock)
if (verbose)
log_print("SMTP> DATA\n");
- return smtp_ok(sock);
+ return smtp_ok(sock, NULL, 0);
}
gint smtp_rset(SockInfo *sock)
if (verbose)
log_print("SMTP> RSET\n");
- return smtp_ok(sock);
+ return smtp_ok(sock, NULL, 0);
}
gint smtp_quit(SockInfo *sock)
if (verbose)
log_print("SMTP> QUIT\n");
- return smtp_ok(sock);
+ return smtp_ok(sock, NULL, 0);
}
gint smtp_eom(SockInfo *sock)
if (verbose)
log_print("SMTP> . (EOM)\n");
- return smtp_ok(sock);
+ return smtp_ok(sock, NULL, 0);
}
-gint smtp_ok(SockInfo *sock)
+static gint smtp_ok(SockInfo *sock, gchar *buf, gint len)
{
- while ((sock_gets(sock, smtp_response, sizeof(smtp_response) - 1))
- != -1) {
- if (strlen(smtp_response) < 4)
+ gchar tmpbuf[MSGBUFSIZE];
+
+ if (!buf) {
+ buf = tmpbuf;
+ len = sizeof(tmpbuf);
+ }
+
+ while ((sock_gets(sock, buf, len - 1)) != -1) {
+ if (strlen(buf) < 4)
return SM_ERROR;
- strretchomp(smtp_response);
+ strretchomp(buf);
if (verbose)
- log_print("SMTP< %s\n", smtp_response);
+ log_print("SMTP< %s\n", buf);
- if ((smtp_response[0] == '1' || smtp_response[0] == '2' ||
- smtp_response[0] == '3') &&
- (smtp_response[3] == ' ' || smtp_response[3] == '\0'))
+ if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
+ (buf[3] == ' ' || buf[3] == '\0'))
return SM_OK;
- else if (smtp_response[3] != '-')
+ else if (buf[3] != '-')
return SM_ERROR;
- else if (smtp_response[0] == '5' &&
- smtp_response[1] == '0' &&
- (smtp_response[2] == '4' ||
- smtp_response[2] == '3' ||
- smtp_response[2] == '1'))
+ else if (buf[0] == '5' && buf[1] == '0' &&
+ (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
return SM_ERROR;
}