sync with 0.9.0cvs3
[claws.git] / src / common / smtp.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 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         SESSION(session)->type             = SESSION_SMTP;
65         SESSION(session)->server           = NULL;
66         SESSION(session)->port             = 0;
67         SESSION(session)->sock             = NULL;
68         SESSION(session)->state            = SESSION_READY;
69         SESSION(session)->data             = NULL;
70
71         SESSION(session)->recv_msg         = smtp_session_recv_msg;
72
73         SESSION(session)->recv_data_finished = NULL;
74         SESSION(session)->send_data_finished = smtp_session_send_data_finished;
75
76         SESSION(session)->destroy          = smtp_session_destroy;
77
78         session->state                     = SMTP_READY;
79
80 #if USE_OPENSSL
81         session->tls_init_done             = FALSE;
82 #endif
83
84         session->hostname                  = NULL;
85         session->user                      = NULL;
86         session->pass                      = NULL;
87
88         session->from                      = NULL;
89         session->to_list                   = NULL;
90         session->cur_to                    = NULL;
91
92         session->send_data                 = NULL;
93         session->send_data_len             = 0;
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_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                 return SM_OK;
282         } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
283             (msg[3] == ' ' || msg[3] == '\0'))
284                 return SM_OK;
285         else if (msg[0] == '5' && msg[1] == '0' &&
286                  (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
287                 return SM_ERROR;
288
289         return SM_ERROR;
290 }
291
292 static gint smtp_starttls(SMTPSession *session)
293 {
294         session->state = SMTP_STARTTLS;
295
296         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
297         log_print("ESMTP> STARTTLS\n");
298
299         return SM_OK;
300 }
301
302 static gint smtp_auth_cram_md5(SMTPSession *session)
303 {
304         session->state = SMTP_AUTH;
305         session->auth_type = SMTPAUTH_CRAM_MD5;
306
307         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
308         log_print("ESMTP> AUTH CRAM-MD5\n");
309
310         return SM_OK;
311 }
312
313 static gint smtp_auth_login(SMTPSession *session)
314 {
315         session->state = SMTP_AUTH;
316         session->auth_type = SMTPAUTH_LOGIN;
317
318         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
319         log_print("ESMTP> AUTH LOGIN\n");
320
321         return SM_OK;
322 }
323
324 static gint smtp_helo(SMTPSession *session)
325 {
326         gchar buf[MSGBUFSIZE];
327
328         session->state = SMTP_HELO;
329
330         g_snprintf(buf, sizeof(buf), "HELO %s",
331                    session->hostname ? session->hostname : get_domain_name());
332         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
333         log_print("SMTP> %s\n", buf);
334
335         return SM_OK;
336 }
337
338 static gint smtp_rcpt(SMTPSession *session)
339 {
340         gchar buf[MSGBUFSIZE];
341         gchar *to;
342
343         g_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
344
345         session->state = SMTP_RCPT;
346
347         to = (gchar *)session->cur_to->data;
348
349         if (strchr(to, '<'))
350                 g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
351         else
352                 g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
353         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
354         log_print("SMTP> %s\n", buf);
355
356         session->cur_to = session->cur_to->next;
357
358         return SM_OK;
359 }
360
361 static gint smtp_data(SMTPSession *session)
362 {
363         session->state = SMTP_DATA;
364
365         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
366         log_print("SMTP> DATA\n");
367
368         return SM_OK;
369 }
370
371 static gint smtp_send_data(SMTPSession *session)
372 {
373         session->state = SMTP_SEND_DATA;
374
375         session_send_data(SESSION(session), session->send_data,
376                           session->send_data_len);
377
378         return SM_OK;
379 }
380
381 #if 0
382 static gint smtp_rset(SMTPSession *session)
383 {
384         session->state = SMTP_RSET;
385
386         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
387         log_print("SMTP> RSET\n");
388
389         return SM_OK;
390 }
391 #endif
392
393 static gint smtp_quit(SMTPSession *session)
394 {
395         session->state = SMTP_QUIT;
396
397         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
398         log_print("SMTP> QUIT\n");
399
400         return SM_OK;
401 }
402
403 static gint smtp_eom(SMTPSession *session)
404 {
405         session->state = SMTP_EOM;
406
407         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
408         log_print("SMTP> . (EOM)\n");
409
410         return SM_OK;
411 }
412
413 static gint smtp_session_recv_msg(Session *session, const gchar *msg)
414 {
415         SMTPSession *smtp_session = SMTP_SESSION(session);
416         gboolean cont = FALSE;
417
418         if (strlen(msg) < 4) {
419                 log_warning(_("bad SMTP response\n"));
420                 return -1;
421         }
422
423         switch (smtp_session->state) {
424         case SMTP_EHLO:
425         case SMTP_STARTTLS:
426         case SMTP_AUTH:
427         case SMTP_AUTH_LOGIN_USER:
428         case SMTP_AUTH_LOGIN_PASS:
429         case SMTP_AUTH_CRAM_MD5:
430                 log_print("ESMTP< %s\n", msg);
431                 break;
432         default:
433                 log_print("SMTP< %s\n", msg);
434                 break;
435         }
436
437         if (msg[0] == '5' && msg[1] == '0' &&
438             (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
439                 log_warning(_("error occurred on SMTP session\n"));
440                 smtp_session->state = SMTP_ERROR;
441                 smtp_session->error_val = SM_ERROR;
442                 g_free(smtp_session->error_msg);
443                 smtp_session->error_msg = g_strdup(msg);
444                 return -1;
445         }
446
447         if (!strncmp(msg, "535", 3)) {
448                 log_warning(_("error occurred on authentication\n"));
449                 smtp_session->state = SMTP_ERROR;
450                 smtp_session->error_val = SM_AUTHFAIL;
451                 g_free(smtp_session->error_msg);
452                 smtp_session->error_msg = g_strdup(msg);
453                 return -1;
454         }
455
456         if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
457                 log_warning(_("error occurred on SMTP session\n"));
458                 smtp_session->state = SMTP_ERROR;
459                 smtp_session->error_val = SM_ERROR;
460                 g_free(smtp_session->error_msg);
461                 smtp_session->error_msg = g_strdup(msg);
462                 return -1;
463         }
464
465         if (msg[3] == '-')
466                 cont = TRUE;
467         else if (msg[3] != ' ' && msg[3] != '\0') {
468                 log_warning(_("bad SMTP response\n"));
469                 smtp_session->state = SMTP_ERROR;
470                 smtp_session->error_val = SM_UNRECOVERABLE;
471                 return -1;
472         }
473
474         /* ignore all multiline responses except for EHLO */
475         if (cont && smtp_session->state != SMTP_EHLO)
476                 return 1;
477
478         switch (smtp_session->state) {
479         case SMTP_READY:
480         case SMTP_CONNECTED:
481 #if USE_OPENSSL
482                 if (smtp_session->user || session->ssl_type != SSL_NONE)
483 #else
484                 if (smtp_session->user)
485 #endif
486                         smtp_ehlo(smtp_session);
487                 else
488                         smtp_helo(smtp_session);
489                 break;
490         case SMTP_HELO:
491                 smtp_from(smtp_session);
492                 break;
493         case SMTP_EHLO:
494                 smtp_ehlo_recv(smtp_session, msg);
495                 if (cont == TRUE)
496                         break;
497 #if USE_OPENSSL
498                 if (session->ssl_type == SSL_STARTTLS &&
499                     smtp_session->tls_init_done == FALSE) {
500                         smtp_starttls(smtp_session);
501                         break;
502                 }
503 #endif
504                 if (smtp_session->user) {
505                         if (smtp_auth(smtp_session) != SM_OK)
506                                 smtp_from(smtp_session);
507                 } else
508                         smtp_from(smtp_session);
509                 break;
510         case SMTP_STARTTLS:
511 #if USE_OPENSSL
512                 if (session_start_tls(session) < 0) {
513                         log_warning(_("can't start TLS session\n"));
514                         smtp_session->state = SMTP_ERROR;
515                         smtp_session->error_val = SM_ERROR;
516                         return -1;
517                 }
518                 smtp_session->tls_init_done = TRUE;
519                 smtp_ehlo(smtp_session);
520 #endif
521                 break;
522         case SMTP_AUTH:
523                 smtp_auth_recv(smtp_session, msg);
524                 break;
525         case SMTP_AUTH_LOGIN_USER:
526                 smtp_auth_login_user_recv(smtp_session, msg);
527                 break;
528         case SMTP_AUTH_LOGIN_PASS:
529         case SMTP_AUTH_CRAM_MD5:
530                 smtp_from(smtp_session);
531                 break;
532         case SMTP_FROM:
533                 if (smtp_session->cur_to)
534                         smtp_rcpt(smtp_session);
535                 break;
536         case SMTP_RCPT:
537                 if (smtp_session->cur_to)
538                         smtp_rcpt(smtp_session);
539                 else
540                         smtp_data(smtp_session);
541                 break;
542         case SMTP_DATA:
543                 smtp_send_data(smtp_session);
544                 break;
545         case SMTP_EOM:
546                 smtp_quit(smtp_session);
547                 break;
548         case SMTP_QUIT:
549                 session_disconnect(session);
550                 break;
551         case SMTP_ERROR:
552         default:
553                 log_warning(_("error occurred on SMTP session\n"));
554                 smtp_session->error_val = SM_ERROR;
555                 return -1;
556         }
557
558         if (cont)
559                 return 1;
560
561         return 0;
562 }
563
564 static gint smtp_session_send_data_finished(Session *session, guint len)
565 {
566         smtp_eom(SMTP_SESSION(session));
567         return 0;
568 }