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