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(use_esmtp? _("Error occurred while sending EHLO\n"):
144 _("Error occurred while sending HELO\n"));
149 /* if we have a user to authenticate and no auth methods, but starttls,
151 if (ssl_type == SSL_NONE && avail_auth_type == SMTPAUTH_TLS_AVAILABLE
153 ssl_type = SSL_STARTTLS;
155 if (ssl_type == SSL_STARTTLS) {
156 val = smtp_starttls(session);
158 log_warning(_("Error occurred while sending STARTTLS\n"));
161 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
164 val = smtp_ehlo(session, domain, &avail_auth_type);
166 log_warning(_("Error occurred while sending EHLO\n"));
172 session->avail_auth_type = avail_auth_type;
177 gint smtp_from(SMTPSession *session, const gchar *from)
179 gchar buf[MSGBUFSIZE];
181 g_return_val_if_fail(session != NULL, SM_ERROR);
182 g_return_val_if_fail(from != NULL, SM_ERROR);
184 UI_UPDATE(session, SMTP_FROM);
186 if (strchr(from, '<'))
187 g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", from);
189 g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", from);
191 sock_printf(SESSION(session)->sock, "%s\r\n", buf);
193 log_print("SMTP> %s\n", buf);
195 return smtp_ok(SESSION(session)->sock, NULL, 0);
198 gint smtp_auth(SMTPSession *session, SMTPAuthType forced_auth_type)
200 gchar buf[MSGBUFSIZE];
201 SMTPAuthType authtype = 0;
202 guchar hexdigest[33];
203 gchar *challenge, *response, *response64;
207 g_return_val_if_fail(session != NULL, SM_ERROR);
208 g_return_val_if_fail(session->user != NULL, SM_ERROR);
210 UI_UPDATE(session, SMTP_AUTH);
212 sock = SESSION(session)->sock;
214 if ((forced_auth_type == SMTPAUTH_CRAM_MD5 ||
215 (forced_auth_type == 0 &&
216 (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0)) &&
217 smtp_auth_cram_md5(session, buf, sizeof(buf)) == SM_OK)
218 authtype = SMTPAUTH_CRAM_MD5;
219 else if ((forced_auth_type == SMTPAUTH_LOGIN ||
220 (forced_auth_type == 0 &&
221 (session->avail_auth_type & SMTPAUTH_LOGIN) != 0)) &&
222 smtp_auth_login(session, buf, sizeof(buf)) == SM_OK)
223 authtype = SMTPAUTH_LOGIN;
225 log_warning(_("SMTP AUTH not available\n"));
231 if (!strncmp(buf, "334 ", 4))
232 base64_encode(buf, session->user, strlen(session->user));
234 /* Server rejects AUTH */
235 g_snprintf(buf, sizeof(buf), "*");
237 sock_printf(sock, "%s\r\n", buf);
238 if (verbose) log_print("ESMTP> [USERID]\n");
240 smtp_ok(sock, buf, sizeof(buf));
242 if (!strncmp(buf, "334 ", 4))
243 base64_encode(buf, session->pass, strlen(session->pass));
245 /* Server rejects AUTH */
246 g_snprintf(buf, sizeof(buf), "*");
248 sock_printf(sock, "%s\r\n", buf);
249 if (verbose) log_print("ESMTP> [PASSWORD]\n");
251 case SMTPAUTH_CRAM_MD5:
252 if (!strncmp(buf, "334 ", 4)) {
253 challenge = g_malloc(strlen(buf + 4) + 1);
254 challengelen = base64_decode(challenge, buf + 4, -1);
255 challenge[challengelen] = '\0';
257 log_print("ESMTP< [Decoded: %s]\n", challenge);
259 g_snprintf(buf, sizeof(buf), "%s", session->pass);
260 md5_hex_hmac(hexdigest, challenge, challengelen,
261 buf, strlen(session->pass));
264 response = g_strdup_printf
265 ("%s %s", session->user, hexdigest);
267 log_print("ESMTP> [Encoded: %s]\n", response);
269 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
270 base64_encode(response64, response, strlen(response));
273 sock_printf(sock, "%s\r\n", response64);
274 if (verbose) log_print("ESMTP> %s\n", response64);
277 /* Server rejects AUTH */
278 g_snprintf(buf, sizeof(buf), "*");
279 sock_printf(sock, "%s\r\n", buf);
281 log_print("ESMTP> %s\n", buf);
284 case SMTPAUTH_DIGEST_MD5:
286 /* stop smtp_auth when no correct authtype */
287 g_snprintf(buf, sizeof(buf), "*");
288 sock_printf(sock, "%s\r\n", buf);
289 if (verbose) log_print("ESMTP> %s\n", buf);
293 return smtp_ok(sock, NULL, 0);
296 gint smtp_ehlo(SMTPSession *session, const gchar *hostname,
297 SMTPAuthType *avail_auth_type)
300 gchar buf[MSGBUFSIZE];
302 UI_UPDATE(session, SMTP_EHLO);
304 sock = SESSION(session)->sock;
306 *avail_auth_type = 0;
308 sock_printf(sock, "EHLO %s\r\n", hostname);
310 log_print("ESMTP> EHLO %s\n", hostname);
312 while ((sock_gets(sock, buf, sizeof(buf) - 1)) != -1) {
318 log_print("ESMTP< %s\n", buf);
320 if (strncmp(buf, "250-", 4) == 0) {
323 if (g_strncasecmp(p, "AUTH", 4) == 0) {
325 if (strcasestr(p, "LOGIN"))
326 *avail_auth_type |= SMTPAUTH_LOGIN;
327 if (strcasestr(p, "CRAM-MD5"))
328 *avail_auth_type |= SMTPAUTH_CRAM_MD5;
329 if (strcasestr(p, "DIGEST-MD5"))
330 *avail_auth_type |= SMTPAUTH_DIGEST_MD5;
331 } else if (g_strncasecmp(p, "STARTTLS", 8) == 0) {
333 *avail_auth_type |= SMTPAUTH_TLS_AVAILABLE;
335 } else if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
336 (buf[3] == ' ' || buf[3] == '\0'))
338 else if (buf[3] != '-')
340 else if (buf[0] == '5' && buf[1] == '0' &&
341 (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
345 return SM_UNRECOVERABLE;
348 static gint smtp_starttls(SMTPSession *session)
352 UI_UPDATE(session, SMTP_STARTTLS);
354 sock = SESSION(session)->sock;
356 sock_printf(sock, "STARTTLS\r\n");
358 log_print("ESMTP> STARTTLS\n");
360 return smtp_ok(sock, NULL, 0);
363 static gint smtp_auth_cram_md5(SMTPSession *session, gchar *buf, gint len)
367 UI_UPDATE(session, SMTP_AUTH);
369 sock = SESSION(session)->sock;
371 sock_printf(sock, "AUTH CRAM-MD5\r\n");
373 log_print("ESMTP> AUTH CRAM-MD5\n");
375 return smtp_ok(sock, buf, len);
378 static gint smtp_auth_login(SMTPSession *session, gchar *buf, gint len)
382 UI_UPDATE(session, SMTP_AUTH);
384 sock = SESSION(session)->sock;
386 sock_printf(sock, "AUTH LOGIN\r\n");
388 log_print("ESMTP> AUTH LOGIN\n");
390 return smtp_ok(sock, buf, len);
393 gint smtp_helo(SMTPSession *session, const gchar *hostname)
397 UI_UPDATE(session, SMTP_HELO);
399 sock = SESSION(session)->sock;
401 sock_printf(sock, "HELO %s\r\n", hostname);
403 log_print("SMTP> HELO %s\n", hostname);
405 return smtp_ok(sock, NULL, 0);
408 gint smtp_rcpt(SMTPSession *session, const gchar *to)
411 gchar buf[MSGBUFSIZE];
413 UI_UPDATE(session, SMTP_RCPT);
415 sock = SESSION(session)->sock;
418 g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
420 g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
422 sock_printf(sock, "%s\r\n", buf);
424 log_print("SMTP> %s\n", buf);
426 return smtp_ok(sock, NULL, 0);
429 gint smtp_data(SMTPSession *session)
433 UI_UPDATE(session, SMTP_DATA);
435 sock = SESSION(session)->sock;
437 sock_printf(sock, "DATA\r\n");
439 log_print("SMTP> DATA\n");
441 return smtp_ok(sock, NULL, 0);
444 gint smtp_rset(SMTPSession *session)
448 UI_UPDATE(session, SMTP_RSET);
450 sock = SESSION(session)->sock;
452 sock_printf(sock, "RSET\r\n");
454 log_print("SMTP> RSET\n");
456 return smtp_ok(sock, NULL, 0);
459 gint smtp_quit(SMTPSession *session)
463 UI_UPDATE(session, SMTP_QUIT);
465 sock = SESSION(session)->sock;
467 sock_printf(sock, "QUIT\r\n");
469 log_print("SMTP> QUIT\n");
471 return smtp_ok(sock, NULL, 0);
474 gint smtp_eom(SMTPSession *session)
478 UI_UPDATE(session, SMTP_EOM);
480 sock = SESSION(session)->sock;
482 sock_printf(sock, ".\r\n");
484 log_print("SMTP> . (EOM)\n");
486 return smtp_ok(sock, NULL, 0);
489 static gint smtp_ok(SockInfo *sock, gchar *buf, gint len)
491 gchar tmpbuf[MSGBUFSIZE];
495 len = sizeof(tmpbuf);
498 while ((sock_gets(sock, buf, len - 1)) != -1) {
504 log_print("SMTP< %s\n", buf);
506 if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
507 (buf[3] == ' ' || buf[3] == '\0'))
509 else if (buf[3] != '-')
511 else if (buf[0] == '5' && buf[1] == '0' &&
512 (buf[2] == '4' || buf[2] == '3' || buf[2] == '1'))
516 return SM_UNRECOVERABLE;