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