104d168fbe8a7e84aaf80ee53f8625590c6ea913
[claws.git] / src / common / smtp.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "intl.h"
29 #include "smtp.h"
30 #include "md5.h"
31 #include "base64.h"
32 #include "utils.h"
33 #include "log.h"
34
35 static void smtp_session_destroy(Session *session);
36
37 static gint smtp_from(SMTPSession *session);
38
39 static gint smtp_auth(SMTPSession *session);
40 static gint smtp_starttls(SMTPSession *session);
41 static gint smtp_auth_cram_md5(SMTPSession *session);
42 static gint smtp_auth_login(SMTPSession *session);
43
44 static gint smtp_ehlo(SMTPSession *session);
45 static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg);
46
47 static gint smtp_helo(SMTPSession *session);
48 static gint smtp_rcpt(SMTPSession *session);
49 static gint smtp_data(SMTPSession *session);
50 static gint smtp_send_data(SMTPSession *session);
51 /* static gint smtp_rset(SMTPSession *session); */
52 static gint smtp_quit(SMTPSession *session);
53 static gint smtp_eom(SMTPSession *session);
54
55 static gint smtp_session_recv_msg(Session *session, const gchar *msg);
56 static gint smtp_session_send_data_finished(Session *session, guint len);
57
58
59 Session *smtp_session_new(void)
60 {
61         SMTPSession *session;
62
63         session = g_new0(SMTPSession, 1);
64
65         session_init(SESSION(session));
66
67         SESSION(session)->type             = SESSION_SMTP;
68
69         SESSION(session)->recv_msg         = smtp_session_recv_msg;
70
71         SESSION(session)->recv_data_finished = NULL;
72         SESSION(session)->send_data_finished = smtp_session_send_data_finished;
73
74         SESSION(session)->destroy          = smtp_session_destroy;
75
76         session->state                     = SMTP_READY;
77
78 #if USE_OPENSSL
79         session->tls_init_done             = FALSE;
80 #endif
81
82         session->hostname                  = NULL;
83         session->user                      = NULL;
84         session->pass                      = NULL;
85
86         session->from                      = NULL;
87         session->to_list                   = NULL;
88         session->cur_to                    = NULL;
89
90         session->send_data                 = NULL;
91         session->send_data_len             = 0;
92
93         session->max_message_size          = -1;
94
95         session->avail_auth_type           = 0;
96         session->forced_auth_type          = 0;
97         session->auth_type                 = 0;
98
99         session->error_val                 = SM_OK;
100         session->error_msg                 = NULL;
101
102         return SESSION(session);
103 }
104
105 static void smtp_session_destroy(Session *session)
106 {
107         SMTPSession *smtp_session = SMTP_SESSION(session);
108
109         g_free(smtp_session->hostname);
110         g_free(smtp_session->user);
111         g_free(smtp_session->pass);
112         g_free(smtp_session->from);
113
114         g_free(smtp_session->send_data);
115
116         g_free(smtp_session->error_msg);
117 }
118
119 static gint smtp_from(SMTPSession *session)
120 {
121         gchar buf[MSGBUFSIZE];
122
123         g_return_val_if_fail(session->from != NULL, SM_ERROR);
124
125         session->state = SMTP_FROM;
126
127         if (strchr(session->from, '<'))
128                 g_snprintf(buf, sizeof(buf), "MAIL FROM:%s", session->from);
129         else
130                 g_snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", session->from);
131
132         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
133         log_print("SMTP> %s\n", buf);
134
135         return SM_OK;
136 }
137
138 static gint smtp_auth(SMTPSession *session)
139 {
140
141         g_return_val_if_fail(session->user != NULL, SM_ERROR);
142
143         session->state = SMTP_AUTH;
144
145         if (session->forced_auth_type == SMTPAUTH_CRAM_MD5 ||
146             (session->forced_auth_type == 0 &&
147              (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0))
148                 smtp_auth_cram_md5(session);
149         else if (session->forced_auth_type == SMTPAUTH_LOGIN ||
150                  (session->forced_auth_type == 0 &&
151                   (session->avail_auth_type & SMTPAUTH_LOGIN) != 0))
152                 smtp_auth_login(session);
153         else {
154                 log_warning(_("SMTP AUTH not available\n"));
155                 return SM_AUTHFAIL;
156         }
157
158         return SM_OK;
159 }
160
161 static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
162 {
163         gchar buf[MSGBUFSIZE];
164
165         switch (session->auth_type) {
166         case SMTPAUTH_LOGIN:
167                 session->state = SMTP_AUTH_LOGIN_USER;
168
169                 if (!strncmp(msg, "334 ", 4)) {
170                         base64_encode(buf, session->user, strlen(session->user));
171
172                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
173                                          buf);
174                         log_print("ESMTP> [USERID]\n");
175                 } else {
176                         /* Server rejects AUTH */
177                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
178                                          "*");
179                         log_print("ESMTP> *\n");
180                 }
181                 break;
182         case SMTPAUTH_CRAM_MD5:
183                 session->state = SMTP_AUTH_CRAM_MD5;
184
185                 if (!strncmp(msg, "334 ", 4)) {
186                         gchar *response;
187                         gchar *response64;
188                         gchar *challenge;
189                         gint challengelen;
190                         guchar hexdigest[33];
191
192                         challenge = g_malloc(strlen(msg + 4) + 1);
193                         challengelen = base64_decode(challenge, msg + 4, -1);
194                         challenge[challengelen] = '\0';
195                         log_print("ESMTP< [Decoded: %s]\n", challenge);
196
197                         g_snprintf(buf, sizeof(buf), "%s", session->pass);
198                         md5_hex_hmac(hexdigest, challenge, challengelen,
199                                      buf, strlen(session->pass));
200                         g_free(challenge);
201
202                         response = g_strdup_printf
203                                 ("%s %s", session->user, hexdigest);
204                         log_print("ESMTP> [Encoded: %s]\n", response);
205
206                         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
207                         base64_encode(response64, response, strlen(response));
208                         g_free(response);
209
210                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
211                                          response64);
212                         log_print("ESMTP> %s\n", response64);
213                         g_free(response64);
214                 } else {
215                         /* Server rejects AUTH */
216                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
217                                          "*");
218                         log_print("ESMTP> *\n");
219                 }
220                 break;
221         case SMTPAUTH_DIGEST_MD5:
222         default:
223                 /* stop smtp_auth when no correct authtype */
224                 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*");
225                 log_print("ESMTP> *\n");
226                 break;
227         }
228
229         return SM_OK;
230 }
231
232 static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
233 {
234         gchar buf[MSGBUFSIZE];
235
236         session->state = SMTP_AUTH_LOGIN_PASS;
237
238         if (!strncmp(msg, "334 ", 4))
239                 base64_encode(buf, session->pass, strlen(session->pass));
240         else
241                 /* Server rejects AUTH */
242                 g_snprintf(buf, sizeof(buf), "*");
243
244         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
245         log_print("ESMTP> [PASSWORD]\n");
246
247         return SM_OK;
248 }
249
250 static gint smtp_ehlo(SMTPSession *session)
251 {
252         gchar buf[MSGBUFSIZE];
253
254         session->state = SMTP_EHLO;
255
256         session->avail_auth_type = 0;
257
258         g_snprintf(buf, sizeof(buf), "EHLO %s",
259                    session->hostname ? session->hostname : get_domain_name());
260         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
261         log_print("ESMTP> %s\n", buf);
262
263         return SM_OK;
264 }
265
266 static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
267 {
268         if (strncmp(msg, "250", 3) == 0) {
269                 const gchar *p = msg;
270                 p += 3;
271                 if (*p == '-' || *p == ' ') p++;
272                 if (g_ascii_strncasecmp(p, "AUTH", 4) == 0) {
273                         p += 5;
274                         if (strcasestr(p, "LOGIN"))
275                                 session->avail_auth_type |= SMTPAUTH_LOGIN;
276                         if (strcasestr(p, "CRAM-MD5"))
277                                 session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
278                         if (strcasestr(p, "DIGEST-MD5"))
279                                 session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
280                 }
281                 if (g_ascii_strncasecmp(p, "SIZE", 4) == 0) {
282                         p += 5;
283                         session->max_message_size = atoi(p);
284                 }
285                 return SM_OK;
286         } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
287             (msg[3] == ' ' || msg[3] == '\0'))
288                 return SM_OK;
289         else if (msg[0] == '5' && msg[1] == '0' &&
290                  (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
291                 return SM_ERROR;
292
293         return SM_ERROR;
294 }
295
296 static gint smtp_starttls(SMTPSession *session)
297 {
298         session->state = SMTP_STARTTLS;
299
300         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
301         log_print("ESMTP> STARTTLS\n");
302
303         return SM_OK;
304 }
305
306 static gint smtp_auth_cram_md5(SMTPSession *session)
307 {
308         session->state = SMTP_AUTH;
309         session->auth_type = SMTPAUTH_CRAM_MD5;
310
311         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
312         log_print("ESMTP> AUTH CRAM-MD5\n");
313
314         return SM_OK;
315 }
316
317 static gint smtp_auth_login(SMTPSession *session)
318 {
319         session->state = SMTP_AUTH;
320         session->auth_type = SMTPAUTH_LOGIN;
321
322         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
323         log_print("ESMTP> AUTH LOGIN\n");
324
325         return SM_OK;
326 }
327
328 static gint smtp_helo(SMTPSession *session)
329 {
330         gchar buf[MSGBUFSIZE];
331
332         session->state = SMTP_HELO;
333
334         g_snprintf(buf, sizeof(buf), "HELO %s",
335                    session->hostname ? session->hostname : get_domain_name());
336         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
337         log_print("SMTP> %s\n", buf);
338
339         return SM_OK;
340 }
341
342 static gint smtp_rcpt(SMTPSession *session)
343 {
344         gchar buf[MSGBUFSIZE];
345         gchar *to;
346
347         g_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
348
349         session->state = SMTP_RCPT;
350
351         to = (gchar *)session->cur_to->data;
352
353         if (strchr(to, '<'))
354                 g_snprintf(buf, sizeof(buf), "RCPT TO:%s", to);
355         else
356                 g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>", to);
357         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
358         log_print("SMTP> %s\n", buf);
359
360         session->cur_to = session->cur_to->next;
361
362         return SM_OK;
363 }
364
365 static gint smtp_data(SMTPSession *session)
366 {
367         session->state = SMTP_DATA;
368
369         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
370         log_print("SMTP> DATA\n");
371
372         return SM_OK;
373 }
374
375 static gint smtp_send_data(SMTPSession *session)
376 {
377         session->state = SMTP_SEND_DATA;
378
379         session_send_data(SESSION(session), session->send_data,
380                           session->send_data_len);
381
382         return SM_OK;
383 }
384
385 #if 0
386 static gint smtp_rset(SMTPSession *session)
387 {
388         session->state = SMTP_RSET;
389
390         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
391         log_print("SMTP> RSET\n");
392
393         return SM_OK;
394 }
395 #endif
396
397 static gint smtp_quit(SMTPSession *session)
398 {
399         session->state = SMTP_QUIT;
400
401         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
402         log_print("SMTP> QUIT\n");
403
404         return SM_OK;
405 }
406
407 static gint smtp_eom(SMTPSession *session)
408 {
409         session->state = SMTP_EOM;
410
411         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
412         log_print("SMTP> . (EOM)\n");
413
414         return SM_OK;
415 }
416
417 static gint smtp_session_recv_msg(Session *session, const gchar *msg)
418 {
419         SMTPSession *smtp_session = SMTP_SESSION(session);
420         gboolean cont = FALSE;
421
422         if (strlen(msg) < 4) {
423                 log_warning(_("bad SMTP response\n"));
424                 return -1;
425         }
426
427         switch (smtp_session->state) {
428         case SMTP_EHLO:
429         case SMTP_STARTTLS:
430         case SMTP_AUTH:
431         case SMTP_AUTH_LOGIN_USER:
432         case SMTP_AUTH_LOGIN_PASS:
433         case SMTP_AUTH_CRAM_MD5:
434                 log_print("ESMTP< %s\n", msg);
435                 break;
436         default:
437                 log_print("SMTP< %s\n", msg);
438                 break;
439         }
440
441         if (msg[0] == '5' && msg[1] == '0' &&
442             (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
443                 log_warning(_("error occurred on SMTP session\n"));
444                 smtp_session->state = SMTP_ERROR;
445                 smtp_session->error_val = SM_ERROR;
446                 g_free(smtp_session->error_msg);
447                 smtp_session->error_msg = g_strdup(msg);
448                 return -1;
449         }
450
451         if (!strncmp(msg, "535", 3)) {
452                 log_warning(_("error occurred on authentication\n"));
453                 smtp_session->state = SMTP_ERROR;
454                 smtp_session->error_val = SM_AUTHFAIL;
455                 g_free(smtp_session->error_msg);
456                 smtp_session->error_msg = g_strdup(msg);
457                 return -1;
458         }
459
460         if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
461                 log_warning(_("error occurred on SMTP session\n"));
462                 smtp_session->state = SMTP_ERROR;
463                 smtp_session->error_val = SM_ERROR;
464                 g_free(smtp_session->error_msg);
465                 smtp_session->error_msg = g_strdup(msg);
466                 return -1;
467         }
468
469         if (msg[3] == '-')
470                 cont = TRUE;
471         else if (msg[3] != ' ' && msg[3] != '\0') {
472                 log_warning(_("bad SMTP response\n"));
473                 smtp_session->state = SMTP_ERROR;
474                 smtp_session->error_val = SM_UNRECOVERABLE;
475                 return -1;
476         }
477
478         /* ignore all multiline responses except for EHLO */
479         if (cont && smtp_session->state != SMTP_EHLO)
480                 return session_recv_msg(session);
481
482         switch (smtp_session->state) {
483         case SMTP_READY:
484         case SMTP_CONNECTED:
485 #if USE_OPENSSL
486                 if (smtp_session->user || session->ssl_type != SSL_NONE)
487 #else
488                 if (smtp_session->user)
489 #endif
490                         smtp_ehlo(smtp_session);
491                 else
492                         smtp_helo(smtp_session);
493                 break;
494         case SMTP_HELO:
495                 smtp_from(smtp_session);
496                 break;
497         case SMTP_EHLO:
498                 smtp_ehlo_recv(smtp_session, msg);
499                 if (cont == TRUE)
500                         break;
501                 if (smtp_session->max_message_size > 0
502                 && smtp_session->max_message_size < 
503                    smtp_session->send_data_len) {
504                         log_warning(_("Message is too big "
505                               "(Maximum size is %s)\n"),
506                               to_human_readable(
507                                (off_t)(smtp_session->max_message_size)));
508                         smtp_session->state = SMTP_ERROR;
509                         smtp_session->error_val = SM_ERROR;
510                         return -1;
511                 }
512 #if USE_OPENSSL
513                 if (session->ssl_type == SSL_STARTTLS &&
514                     smtp_session->tls_init_done == FALSE) {
515                         smtp_starttls(smtp_session);
516                         break;
517                 }
518 #endif
519                 if (smtp_session->user) {
520                         if (smtp_auth(smtp_session) != SM_OK)
521                                 smtp_from(smtp_session);
522                 } else
523                         smtp_from(smtp_session);
524                 break;
525         case SMTP_STARTTLS:
526 #if USE_OPENSSL
527                 if (session_start_tls(session) < 0) {
528                         log_warning(_("can't start TLS session\n"));
529                         smtp_session->state = SMTP_ERROR;
530                         smtp_session->error_val = SM_ERROR;
531                         return -1;
532                 }
533                 smtp_session->tls_init_done = TRUE;
534                 smtp_ehlo(smtp_session);
535 #endif
536                 break;
537         case SMTP_AUTH:
538                 smtp_auth_recv(smtp_session, msg);
539                 break;
540         case SMTP_AUTH_LOGIN_USER:
541                 smtp_auth_login_user_recv(smtp_session, msg);
542                 break;
543         case SMTP_AUTH_LOGIN_PASS:
544         case SMTP_AUTH_CRAM_MD5:
545                 smtp_from(smtp_session);
546                 break;
547         case SMTP_FROM:
548                 if (smtp_session->cur_to)
549                         smtp_rcpt(smtp_session);
550                 break;
551         case SMTP_RCPT:
552                 if (smtp_session->cur_to)
553                         smtp_rcpt(smtp_session);
554                 else
555                         smtp_data(smtp_session);
556                 break;
557         case SMTP_DATA:
558                 smtp_send_data(smtp_session);
559                 break;
560         case SMTP_EOM:
561                 smtp_quit(smtp_session);
562                 break;
563         case SMTP_QUIT:
564                 session_disconnect(session);
565                 break;
566         case SMTP_ERROR:
567         default:
568                 log_warning(_("error occurred on SMTP session\n"));
569                 smtp_session->error_val = SM_ERROR;
570                 return -1;
571         }
572
573         if (cont)
574                 return session_recv_msg(session);
575
576         return 0;
577 }
578
579 static gint smtp_session_send_data_finished(Session *session, guint len)
580 {
581         smtp_eom(SMTP_SESSION(session));
582         return 0;
583 }