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