* src/common/plugin.[ch]
[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
65         session_init(SESSION(session));
66
67         SESSION(session)->type             = SESSION_SMTP;
68
69         SESSION(session)->recv_msg         = smtp_session_recv_msg;
70
71         SESSION(session)->recv_data_finished = NULL;
72         SESSION(session)->send_data_finished = smtp_session_send_data_finished;
73
74         SESSION(session)->destroy          = smtp_session_destroy;
75
76         session->state                     = SMTP_READY;
77
78 #if USE_OPENSSL
79         session->tls_init_done             = FALSE;
80 #endif
81
82         session->hostname                  = NULL;
83         session->user                      = NULL;
84         session->pass                      = NULL;
85
86         session->from                      = NULL;
87         session->to_list                   = NULL;
88         session->cur_to                    = NULL;
89
90         session->send_data                 = NULL;
91         session->send_data_len             = 0;
92
93         session->avail_auth_type           = 0;
94         session->forced_auth_type          = 0;
95         session->auth_type                 = 0;
96
97         session->error_val                 = SM_OK;
98         session->error_msg                 = NULL;
99
100         return SESSION(session);
101 }
102
103 static void smtp_session_destroy(Session *session)
104 {
105         SMTPSession *smtp_session = SMTP_SESSION(session);
106
107         g_free(smtp_session->hostname);
108         g_free(smtp_session->user);
109         g_free(smtp_session->pass);
110         g_free(smtp_session->from);
111
112         g_free(smtp_session->send_data);
113
114         g_free(smtp_session->error_msg);
115 }
116
117 static gint smtp_from(SMTPSession *session)
118 {
119         gchar buf[MSGBUFSIZE];
120
121         g_return_val_if_fail(session->from != NULL, SM_ERROR);
122
123         session->state = SMTP_FROM;
124
125         if (strchr(session->from, '<'))
126                 g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", session->from);
127         else
128                 g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", session->from);
129
130         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
131         log_print("SMTP> %s\n", buf);
132
133         return SM_OK;
134 }
135
136 static gint smtp_auth(SMTPSession *session)
137 {
138
139         g_return_val_if_fail(session->user != NULL, SM_ERROR);
140
141         session->state = SMTP_AUTH;
142
143         if (session->forced_auth_type == SMTPAUTH_CRAM_MD5 ||
144             (session->forced_auth_type == 0 &&
145              (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0))
146                 smtp_auth_cram_md5(session);
147         else if (session->forced_auth_type == SMTPAUTH_LOGIN ||
148                  (session->forced_auth_type == 0 &&
149                   (session->avail_auth_type & SMTPAUTH_LOGIN) != 0))
150                 smtp_auth_login(session);
151         else {
152                 log_warning(_("SMTP AUTH not available\n"));
153                 return SM_AUTHFAIL;
154         }
155
156         return SM_OK;
157 }
158
159 static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
160 {
161         gchar buf[MSGBUFSIZE];
162
163         switch (session->auth_type) {
164         case SMTPAUTH_LOGIN:
165                 session->state = SMTP_AUTH_LOGIN_USER;
166
167                 if (!strncmp(msg, "334 ", 4)) {
168                         base64_encode(buf, session->user, strlen(session->user));
169
170                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
171                                          buf);
172                         log_print("ESMTP> [USERID]\n");
173                 } else {
174                         /* Server rejects AUTH */
175                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
176                                          "*");
177                         log_print("ESMTP> *\n");
178                 }
179                 break;
180         case SMTPAUTH_CRAM_MD5:
181                 session->state = SMTP_AUTH_CRAM_MD5;
182
183                 if (!strncmp(msg, "334 ", 4)) {
184                         gchar *response;
185                         gchar *response64;
186                         gchar *challenge;
187                         gint challengelen;
188                         guchar hexdigest[33];
189
190                         challenge = g_malloc(strlen(msg + 4) + 1);
191                         challengelen = base64_decode(challenge, msg + 4, -1);
192                         challenge[challengelen] = '\0';
193                         log_print("ESMTP< [Decoded: %s]\n", challenge);
194
195                         g_snprintf(buf, sizeof(buf), "%s", session->pass);
196                         md5_hex_hmac(hexdigest, challenge, challengelen,
197                                      buf, strlen(session->pass));
198                         g_free(challenge);
199
200                         response = g_strdup_printf
201                                 ("%s %s", session->user, hexdigest);
202                         log_print("ESMTP> [Encoded: %s]\n", response);
203
204                         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
205                         base64_encode(response64, response, strlen(response));
206                         g_free(response);
207
208                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
209                                          response64);
210                         log_print("ESMTP> %s\n", response64);
211                         g_free(response64);
212                 } else {
213                         /* Server rejects AUTH */
214                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
215                                          "*");
216                         log_print("ESMTP> *\n");
217                 }
218                 break;
219         case SMTPAUTH_DIGEST_MD5:
220         default:
221                 /* stop smtp_auth when no correct authtype */
222                 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*");
223                 log_print("ESMTP> *\n");
224                 break;
225         }
226
227         return SM_OK;
228 }
229
230 static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
231 {
232         gchar buf[MSGBUFSIZE];
233
234         session->state = SMTP_AUTH_LOGIN_PASS;
235
236         if (!strncmp(msg, "334 ", 4))
237                 base64_encode(buf, session->pass, strlen(session->pass));
238         else
239                 /* Server rejects AUTH */
240                 g_snprintf(buf, sizeof(buf), "*");
241
242         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
243         log_print("ESMTP> [PASSWORD]\n");
244
245         return SM_OK;
246 }
247
248 static gint smtp_ehlo(SMTPSession *session)
249 {
250         gchar buf[MSGBUFSIZE];
251
252         session->state = SMTP_EHLO;
253
254         session->avail_auth_type = 0;
255
256         g_snprintf(buf, sizeof(buf), "EHLO %s",
257                    session->hostname ? session->hostname : get_domain_name());
258         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
259         log_print("ESMTP> %s\n", buf);
260
261         return SM_OK;
262 }
263
264 static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
265 {
266         if (strncmp(msg, "250", 3) == 0) {
267                 const gchar *p = msg;
268                 p += 3;
269                 if (*p == '-' || *p == ' ') p++;
270                 if (g_strncasecmp(p, "AUTH", 4) == 0) {
271                         p += 5;
272                         if (strcasestr(p, "LOGIN"))
273                                 session->avail_auth_type |= SMTPAUTH_LOGIN;
274                         if (strcasestr(p, "CRAM-MD5"))
275                                 session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
276                         if (strcasestr(p, "DIGEST-MD5"))
277                                 session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
278                 }
279                 return SM_OK;
280         } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
281             (msg[3] == ' ' || msg[3] == '\0'))
282                 return SM_OK;
283         else if (msg[0] == '5' && msg[1] == '0' &&
284                  (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
285                 return SM_ERROR;
286
287         return SM_ERROR;
288 }
289
290 static gint smtp_starttls(SMTPSession *session)
291 {
292         session->state = SMTP_STARTTLS;
293
294         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
295         log_print("ESMTP> STARTTLS\n");
296
297         return SM_OK;
298 }
299
300 static gint smtp_auth_cram_md5(SMTPSession *session)
301 {
302         session->state = SMTP_AUTH;
303         session->auth_type = SMTPAUTH_CRAM_MD5;
304
305         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
306         log_print("ESMTP> AUTH CRAM-MD5\n");
307
308         return SM_OK;
309 }
310
311 static gint smtp_auth_login(SMTPSession *session)
312 {
313         session->state = SMTP_AUTH;
314         session->auth_type = SMTPAUTH_LOGIN;
315
316         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
317         log_print("ESMTP> AUTH LOGIN\n");
318
319         return SM_OK;
320 }
321
322 static gint smtp_helo(SMTPSession *session)
323 {
324         gchar buf[MSGBUFSIZE];
325
326         session->state = SMTP_HELO;
327
328         g_snprintf(buf, sizeof(buf), "HELO %s",
329                    session->hostname ? session->hostname : get_domain_name());
330         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
331         log_print("SMTP> %s\n", buf);
332
333         return SM_OK;
334 }
335
336 static gint smtp_rcpt(SMTPSession *session)
337 {
338         gchar buf[MSGBUFSIZE];
339         gchar *to;
340
341         g_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
342
343         session->state = SMTP_RCPT;
344
345         to = (gchar *)session->cur_to->data;
346
347         if (strchr(to, '<'))
348                 g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
349         else
350                 g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
351         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
352         log_print("SMTP> %s\n", buf);
353
354         session->cur_to = session->cur_to->next;
355
356         return SM_OK;
357 }
358
359 static gint smtp_data(SMTPSession *session)
360 {
361         session->state = SMTP_DATA;
362
363         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
364         log_print("SMTP> DATA\n");
365
366         return SM_OK;
367 }
368
369 static gint smtp_send_data(SMTPSession *session)
370 {
371         session->state = SMTP_SEND_DATA;
372
373         session_send_data(SESSION(session), session->send_data,
374                           session->send_data_len);
375
376         return SM_OK;
377 }
378
379 #if 0
380 static gint smtp_rset(SMTPSession *session)
381 {
382         session->state = SMTP_RSET;
383
384         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
385         log_print("SMTP> RSET\n");
386
387         return SM_OK;
388 }
389 #endif
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 session_recv_msg(session);
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 session_recv_msg(session);
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 }