2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2002 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 static gint verbose = 1;
38 #define UI_UPDATE(session, phase) \
40 if (SESSION(session)->ui_func) \
41 SESSION(session)->ui_func(SESSION(session), phase); \
44 static gint smtp_starttls(SMTPSession *session);
45 static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len);
46 static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len);
48 static gint smtp_ok(SockInfo *sock, gchar *buf, gint len);
50 Session *smtp_session_new(void)
54 session = g_new0(SMTPSession, 1);
55 SESSION(session)->type = SESSION_SMTP;
56 SESSION(session)->server = NULL;
57 SESSION(session)->sock = NULL;
58 SESSION(session)->connected = FALSE;
59 SESSION(session)->phase = SESSION_READY;
60 SESSION(session)->last_access_time = 0;
61 SESSION(session)->data = NULL;
63 SESSION(session)->destroy = smtp_session_destroy;
64 SESSION(session)->ui_func = NULL;
66 session->avail_auth_type = 0;
70 return SESSION(session);
73 void smtp_session_destroy(Session *session)
75 sock_close(session->sock);
78 g_free(SMTP_SESSION(session)->user);
79 g_free(SMTP_SESSION(session)->pass);
83 gint smtp_connect(SMTPSession *session, const gchar *server, gushort port,
84 const gchar *domain, const gchar *user, const gchar *pass,
87 gint smtp_connect(SMTPSession *session, const gchar *server, gushort port,
88 const gchar *domain, const gchar *user, const gchar *pass)
93 SMTPAuthType avail_auth_type = 0;
96 g_return_val_if_fail(session != NULL, SM_ERROR);
97 g_return_val_if_fail(server != NULL, SM_ERROR);
100 use_esmtp = user != NULL || ssl_type == SSL_STARTTLS;
102 use_esmtp = user != NULL;
105 SESSION(session)->server = g_strdup(server);
106 session->user = user ? g_strdup(user) : NULL;
107 session->pass = pass ? g_strdup(pass) : user ? g_strdup("") : NULL;
109 UI_UPDATE(session, SMTP_CONNECT);
111 if ((sock = sock_connect(server, port)) == NULL) {
112 log_warning(_("Can't connect to SMTP server: %s:%d\n"),
118 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
119 log_warning(_("SSL connection failed"));
125 if (smtp_ok(sock, NULL, 0) != SM_OK) {
126 log_warning(_("Error occurred while connecting to %s:%d\n"),
132 SESSION(session)->sock = sock;
133 SESSION(session)->connected = TRUE;
136 domain = get_domain_name();
139 val = smtp_ehlo(session, domain, &avail_auth_type);
141 val = smtp_helo(session, domain);
143 log_warning(_("Error occurred while sending HELO\n"));
148 if (ssl_type == SSL_STARTTLS) {
149 val = smtp_starttls(session);
151 log_warning(_("Error occurred while sending STARTTLS\n"));
154 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
157 val = smtp_ehlo(session, domain, &avail_auth_type);
159 log_warning(_("Error occurred while sending EHLO\n"));
165 session->avail_auth_type = avail_auth_type;
170 gint smtp_from(SMTPSession *session, const gchar *from)
172 gchar buf[MSGBUFSIZE];
174 g_return_val_if_fail(session != NULL, SM_ERROR);
175 g_return_val_if_fail(from != NULL, SM_ERROR);
177 UI_UPDATE(session, SMTP_FROM);
179 if (strchr(from, '<'))
180 g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", from);
182 g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", from);
184 sock_printf(SESSION(session)->sock, "%s\r\n", buf);
186 log_print("SMTP> %s\n", buf);
188 return smtp_ok(SESSION(session)->sock, NULL, 0);
191 gint smtp_auth(SMTPSession *session, SMTPAuthType forced_auth_type)
193 gchar buf[MSGBUFSIZE];
194 SMTPAuthType authtype = 0;
195 guchar hexdigest[33];
196 gchar *challenge, *response, *response64;
200 g_return_val_if_fail(session != NULL, SM_ERROR);
201 g_return_val_if_fail(session->user != NULL, SM_ERROR);
203 UI_UPDATE(session, SMTP_AUTH);
205 sock = SESSION(session)->sock;
207 if ((forced_auth_type == SMTPAUTH_CRAM_MD5 ||
208 (forced_auth_type == 0 &&
209 (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0)) &&
210 smtp_auth_cram_md5(session, buf, sizeof(buf)) == SM_OK)
211 authtype = SMTPAUTH_CRAM_MD5;
212 else if ((forced_auth_type == SMTPAUTH_LOGIN ||
213 (forced_auth_type == 0 &&
214 (session->avail_auth_type & SMTPAUTH_LOGIN) != 0)) &&
215 smtp_auth_login(session, buf, sizeof(buf)) == SM_OK)
216 authtype = SMTPAUTH_LOGIN;
218 log_warning(_("SMTP AUTH not available\n"));
224 if (!strncmp(buf, "334 ", 4))
225 base64_encode(buf, session->user, strlen(session->user));
227 /* Server rejects AUTH */
228 g_snprintf(buf, sizeof(buf), "*");
230 sock_printf(sock, "%s\r\n", buf);
231 if (verbose) log_print("ESMTP> [USERID]\n");
233 smtp_ok(sock, buf, sizeof(buf));
235 if (!strncmp(buf, "334 ", 4))
236 base64_encode(buf, session->pass, strlen(session->pass));
238 /* Server rejects AUTH */
239 g_snprintf(buf, sizeof(buf), "*");
241 sock_printf(sock, "%s\r\n", buf);
242 if (verbose) log_print("ESMTP> [PASSWORD]\n");
244 case SMTPAUTH_CRAM_MD5:
245 if (!strncmp(buf, "334 ", 4)) {
246 challenge = g_malloc(strlen(buf + 4) + 1);
247 challengelen = base64_decode(challenge, buf + 4, -1);
248 challenge[challengelen] = '\0';
250 log_print("ESMTP< [Decoded: %s]\n", challenge);
252 g_snprintf(buf, sizeof(buf), "%s", session->pass);
253 md5_hex_hmac(hexdigest, challenge, challengelen,
254 buf, strlen(session->pass));
257 response = g_strdup_printf
258 ("%s %s", session->user, hexdigest);
260 log_print("ESMTP> [Encoded: %s]\n", response);
262 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
263 base64_encode(response64, response, strlen(response));
266 sock_printf(sock, "%s\r\n", response64);
267 if (verbose) log_print("ESMTP> %s\n", response64);
270 /* Server rejects AUTH */
271 g_snprintf(buf, sizeof(buf), "*");
272 sock_printf(sock, "%s\r\n", buf);
274 log_print("ESMTP> %s\n", buf);
277 case SMTPAUTH_DIGEST_MD5:
279 /* stop smtp_auth when no correct authtype */
280 g_snprintf(buf, sizeof(buf), "*");
281 sock_printf(sock, "%s\r\n", buf);
282 if (verbose) log_print("ESMTP> %s\n", buf);
286 return smtp_ok(sock, NULL, 0);
289 gint smtp_ehlo(SMTPSession *session, const gchar *hostname,
290 SMTPAuthType *avail_auth_type)
293 gchar buf[MSGBUFSIZE];
295 UI_UPDATE(session, SMTP_EHLO);
297 sock = SESSION(session)->sock;
299 *avail_auth_type = 0;
301 sock_printf(sock, "EHLO %s\r\n", hostname);
303 log_print("ESMTP> EHLO %s\n", hostname);
305 while ((sock_gets(sock, buf, sizeof(buf) - 1)) != -1) {
311 log_print("ESMTP< %s\n", buf);
313 if (strncmp(buf, "250-", 4) == 0) {
316 if (g_strncasecmp(p, "AUTH", 4) == 0) {
318 if (strcasestr(p, "LOGIN"))
319 *avail_auth_type |= SMTPAUTH_LOGIN;
320 if (strcasestr(p, "CRAM-MD5"))
321 *avail_auth_type |= SMTPAUTH_CRAM_MD5;
322 if (strcasestr(p, "DIGEST-MD5"))
323 *avail_auth_type |= SMTPAUTH_DIGEST_MD5;
325 } else if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
326 (buf[3] == ' ' || buf[3] == '\0'))
328 else if (buf[3] != '-')
330 else if (buf[0] == '5' && buf[1] == '0' &&
331 (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
335 return SM_UNRECOVERABLE;
338 static gint smtp_starttls(SMTPSession *session)
342 UI_UPDATE(session, SMTP_STARTTLS);
344 sock = SESSION(session)->sock;
346 sock_printf(sock, "STARTTLS\r\n");
348 log_print("ESMTP> STARTTLS\n");
350 return smtp_ok(sock, NULL, 0);
353 static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len)
357 UI_UPDATE(session, SMTP_AUTH);
359 sock = SESSION(session)->sock;
361 sock_printf(sock, "AUTH CRAM-MD5\r\n");
363 log_print("ESMTP> AUTH CRAM-MD5\n");
365 return smtp_ok(sock, buf, len);
368 static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len)
372 UI_UPDATE(session, SMTP_AUTH);
374 sock = SESSION(session)->sock;
376 sock_printf(sock, "AUTH LOGIN\r\n");
378 log_print("ESMTP> AUTH LOGIN\n");
380 return smtp_ok(sock, buf, len);
383 gint smtp_helo(SMTPSession *session, const gchar *hostname)
387 UI_UPDATE(session, SMTP_HELO);
389 sock = SESSION(session)->sock;
391 sock_printf(sock, "HELO %s\r\n", hostname);
393 log_print("SMTP> HELO %s\n", hostname);
395 return smtp_ok(sock, NULL, 0);
398 gint smtp_rcpt(SMTPSession *session, const gchar *to)
401 gchar buf[MSGBUFSIZE];
403 UI_UPDATE(session, SMTP_RCPT);
405 sock = SESSION(session)->sock;
408 g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
410 g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
412 sock_printf(sock, "%s\r\n", buf);
414 log_print("SMTP> %s\n", buf);
416 return smtp_ok(sock, NULL, 0);
419 gint smtp_data(SMTPSession *session)
423 UI_UPDATE(session, SMTP_DATA);
425 sock = SESSION(session)->sock;
427 sock_printf(sock, "DATA\r\n");
429 log_print("SMTP> DATA\n");
431 return smtp_ok(sock, NULL, 0);
434 gint smtp_rset(SMTPSession *session)
438 UI_UPDATE(session, SMTP_RSET);
440 sock = SESSION(session)->sock;
442 sock_printf(sock, "RSET\r\n");
444 log_print("SMTP> RSET\n");
446 return smtp_ok(sock, NULL, 0);
449 gint smtp_quit(SMTPSession *session)
453 UI_UPDATE(session, SMTP_QUIT);
455 sock = SESSION(session)->sock;
457 sock_printf(sock, "QUIT\r\n");
459 log_print("SMTP> QUIT\n");
461 return smtp_ok(sock, NULL, 0);
464 gint smtp_eom(SMTPSession *session)
468 UI_UPDATE(session, SMTP_EOM);
470 sock = SESSION(session)->sock;
472 sock_printf(sock, ".\r\n");
474 log_print("SMTP> . (EOM)\n");
476 return smtp_ok(sock, NULL, 0);
479 static gint smtp_ok(SockInfo *sock, gchar *buf, gint len)
481 gchar tmpbuf[MSGBUFSIZE];
485 len = sizeof(tmpbuf);
488 while ((sock_gets(sock, buf, len - 1)) != -1) {
494 log_print("SMTP< %s\n", buf);
496 if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
497 (buf[3] == ' ' || buf[3] == '\0'))
499 else if (buf[3] != '-')
501 else if (buf[0] == '5' && buf[1] == '0' &&
502 (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
506 return SM_UNRECOVERABLE;