more sync with 0.8.11cvs35
[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 static gint smtp_rset(SMTPSession *session)
382 {
383         session->state = SMTP_RSET;
384
385         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
386         log_print("SMTP> RSET\n");
387
388         return SM_OK;
389 }
390
391 static gint smtp_quit(SMTPSession *session)
392 {
393         session->state = SMTP_QUIT;
394
395         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
396         log_print("SMTP> QUIT\n");
397
398         return SM_OK;
399 }
400
401 static gint smtp_eom(SMTPSession *session)
402 {
403         session->state = SMTP_EOM;
404
405         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
406         log_print("SMTP> . (EOM)\n");
407
408         return SM_OK;
409 }
410
411 static gint smtp_session_recv_msg(Session *session, const gchar *msg)
412 {
413         SMTPSession *smtp_session = SMTP_SESSION(session);
414         gboolean cont = FALSE;
415
416         if (strlen(msg) < 4) {
417                 log_warning(_("bad SMTP response\n"));
418                 return -1;
419         }
420
421         switch (smtp_session->state) {
422         case SMTP_EHLO:
423         case SMTP_STARTTLS:
424         case SMTP_AUTH:
425         case SMTP_AUTH_LOGIN_USER:
426         case SMTP_AUTH_LOGIN_PASS:
427         case SMTP_AUTH_CRAM_MD5:
428                 log_print("ESMTP< %s\n", msg);
429                 break;
430         default:
431                 log_print("SMTP< %s\n", msg);
432                 break;
433         }
434
435         if (msg[0] == '5' && msg[1] == '0' &&
436             (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
437                 log_warning(_("error occurred on SMTP session\n"));
438                 smtp_session->state = SMTP_ERROR;
439                 smtp_session->error_val = SM_ERROR;
440                 g_free(smtp_session->error_msg);
441                 smtp_session->error_msg = g_strdup(msg);
442                 return -1;
443         }
444
445         if (!strncmp(msg, "535", 3)) {
446                 log_warning(_("error occurred on authentication\n"));
447                 smtp_session->state = SMTP_ERROR;
448                 smtp_session->error_val = SM_AUTHFAIL;
449                 g_free(smtp_session->error_msg);
450                 smtp_session->error_msg = g_strdup(msg);
451                 return -1;
452         }
453
454         if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
455                 log_warning(_("error occurred on SMTP session\n"));
456                 smtp_session->state = SMTP_ERROR;
457                 smtp_session->error_val = SM_ERROR;
458                 g_free(smtp_session->error_msg);
459                 smtp_session->error_msg = g_strdup(msg);
460                 return -1;
461         }
462
463         if (msg[3] == '-')
464                 cont = TRUE;
465         else if (msg[3] != ' ' && msg[3] != '\0') {
466                 log_warning(_("bad SMTP response\n"));
467                 smtp_session->state = SMTP_ERROR;
468                 smtp_session->error_val = SM_UNRECOVERABLE;
469                 return -1;
470         }
471
472         /* ignore all multiline responses except for EHLO */
473         if (cont && smtp_session->state != SMTP_EHLO)
474                 return 1;
475
476         switch (smtp_session->state) {
477         case SMTP_READY:
478         case SMTP_CONNECTED:
479 #if USE_OPENSSL
480                 if (smtp_session->user || session->ssl_type != SSL_NONE)
481 #else
482                 if (smtp_session->user)
483 #endif
484                         smtp_ehlo(smtp_session);
485                 else
486                         smtp_helo(smtp_session);
487                 break;
488         case SMTP_HELO:
489                 smtp_from(smtp_session);
490                 break;
491         case SMTP_EHLO:
492                 smtp_ehlo_recv(smtp_session, msg);
493                 if (cont == TRUE)
494                         break;
495 #if USE_OPENSSL
496                 if (session->ssl_type == SSL_STARTTLS &&
497                     smtp_session->tls_init_done == FALSE) {
498                         smtp_starttls(smtp_session);
499                         break;
500                 }
501 #endif
502                 if (smtp_session->user) {
503                         if (smtp_auth(smtp_session) != SM_OK)
504                                 smtp_from(smtp_session);
505                 } else
506                         smtp_from(smtp_session);
507                 break;
508         case SMTP_STARTTLS:
509 #if USE_OPENSSL
510                 if (session_start_tls(session) < 0) {
511                         log_warning(_("can't start TLS session\n"));
512                         smtp_session->state = SMTP_ERROR;
513                         smtp_session->error_val = SM_ERROR;
514                         return -1;
515                 }
516                 smtp_session->tls_init_done = TRUE;
517                 smtp_ehlo(smtp_session);
518 #endif
519                 break;
520         case SMTP_AUTH:
521                 smtp_auth_recv(smtp_session, msg);
522                 break;
523         case SMTP_AUTH_LOGIN_USER:
524                 smtp_auth_login_user_recv(smtp_session, msg);
525                 break;
526         case SMTP_AUTH_LOGIN_PASS:
527         case SMTP_AUTH_CRAM_MD5:
528                 smtp_from(smtp_session);
529                 break;
530         case SMTP_FROM:
531                 if (smtp_session->cur_to)
532                         smtp_rcpt(smtp_session);
533                 break;
534         case SMTP_RCPT:
535                 if (smtp_session->cur_to)
536                         smtp_rcpt(smtp_session);
537                 else
538                         smtp_data(smtp_session);
539                 break;
540         case SMTP_DATA:
541                 smtp_send_data(smtp_session);
542                 break;
543         case SMTP_EOM:
544                 smtp_quit(smtp_session);
545                 break;
546         case SMTP_QUIT:
547                 session_disconnect(session);
548                 break;
549         case SMTP_ERROR:
550         default:
551                 log_warning(_("error occurred on SMTP session\n"));
552                 smtp_session->error_val = SM_ERROR;
553                 return -1;
554         }
555
556         if (cont)
557                 return 1;
558
559         return 0;
560 }
561
562 static gint smtp_session_send_data_finished(Session *session, guint len)
563 {
564         smtp_eom(SMTP_SESSION(session));
565         return 0;
566 }