sync with 0.8.11cvs11
[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         return SESSION(session);
100 }
101
102 static void smtp_session_destroy(Session *session)
103 {
104         SMTPSession *smtp_session = SMTP_SESSION(session);
105
106         g_free(smtp_session->hostname);
107         g_free(smtp_session->user);
108         g_free(smtp_session->pass);
109         g_free(smtp_session->from);
110
111         g_free(smtp_session->send_data);
112 }
113
114 static gint smtp_from(SMTPSession *session)
115 {
116         gchar buf[MSGBUFSIZE];
117
118         g_return_val_if_fail(session->from != NULL, SM_ERROR);
119
120         session->state = SMTP_FROM;
121
122         if (strchr(session->from, '<'))
123                 g_snprintf(buf, sizeof(buf), "MAIL FROM: %s", session->from);
124         else
125                 g_snprintf(buf, sizeof(buf), "MAIL FROM: <%s>", session->from);
126
127         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
128         log_print("SMTP> %s\n", buf);
129
130         return SM_OK;
131 }
132
133 static gint smtp_auth(SMTPSession *session)
134 {
135
136         g_return_val_if_fail(session->user != NULL, SM_ERROR);
137
138         session->state = SMTP_AUTH;
139
140         if (session->forced_auth_type == SMTPAUTH_CRAM_MD5 ||
141             (session->forced_auth_type == 0 &&
142              (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0))
143                 smtp_auth_cram_md5(session);
144         else if (session->forced_auth_type == SMTPAUTH_LOGIN ||
145                  (session->forced_auth_type == 0 &&
146                   (session->avail_auth_type & SMTPAUTH_LOGIN) != 0))
147                 smtp_auth_login(session);
148         else {
149                 log_warning(_("SMTP AUTH not available\n"));
150                 return SM_AUTHFAIL;
151         }
152
153         return SM_OK;
154 }
155
156 static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
157 {
158         gchar buf[MSGBUFSIZE];
159
160         switch (session->auth_type) {
161         case SMTPAUTH_LOGIN:
162                 session->state = SMTP_AUTH_LOGIN_USER;
163
164                 if (!strncmp(msg, "334 ", 4)) {
165                         base64_encode(buf, session->user, strlen(session->user));
166
167                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
168                                          buf);
169                         log_print("ESMTP> [USERID]\n");
170                 } else {
171                         /* Server rejects AUTH */
172                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
173                                          "*");
174                         log_print("ESMTP> *\n");
175                 }
176                 break;
177         case SMTPAUTH_CRAM_MD5:
178                 session->state = SMTP_AUTH_CRAM_MD5;
179
180                 if (!strncmp(msg, "334 ", 4)) {
181                         gchar *response;
182                         gchar *response64;
183                         gchar *challenge;
184                         gint challengelen;
185                         guchar hexdigest[33];
186
187                         challenge = g_malloc(strlen(msg + 4) + 1);
188                         challengelen = base64_decode(challenge, msg + 4, -1);
189                         challenge[challengelen] = '\0';
190                         log_print("ESMTP< [Decoded: %s]\n", challenge);
191
192                         g_snprintf(buf, sizeof(buf), "%s", session->pass);
193                         md5_hex_hmac(hexdigest, challenge, challengelen,
194                                      buf, strlen(session->pass));
195                         g_free(challenge);
196
197                         response = g_strdup_printf
198                                 ("%s %s", session->user, hexdigest);
199                         log_print("ESMTP> [Encoded: %s]\n", response);
200
201                         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
202                         base64_encode(response64, response, strlen(response));
203                         g_free(response);
204
205                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
206                                          response64);
207                         log_print("ESMTP> %s\n", response64);
208                         g_free(response64);
209                 } else {
210                         /* Server rejects AUTH */
211                         session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
212                                          "*");
213                         log_print("ESMTP> *\n");
214                 }
215                 break;
216         case SMTPAUTH_DIGEST_MD5:
217         default:
218                 /* stop smtp_auth when no correct authtype */
219                 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*");
220                 log_print("ESMTP> *\n");
221                 break;
222         }
223
224         return SM_OK;
225 }
226
227 static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
228 {
229         gchar buf[MSGBUFSIZE];
230
231         session->state = SMTP_AUTH_LOGIN_PASS;
232
233         if (!strncmp(msg, "334 ", 4))
234                 base64_encode(buf, session->pass, strlen(session->pass));
235         else
236                 /* Server rejects AUTH */
237                 g_snprintf(buf, sizeof(buf), "*");
238
239         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
240         log_print("ESMTP> [PASSWORD]\n");
241
242         return SM_OK;
243 }
244
245 static gint smtp_ehlo(SMTPSession *session)
246 {
247         gchar buf[MSGBUFSIZE];
248
249         session->state = SMTP_EHLO;
250
251         session->avail_auth_type = 0;
252
253         g_snprintf(buf, sizeof(buf), "EHLO %s",
254                    session->hostname ? session->hostname : get_domain_name());
255         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
256         log_print("ESMTP> %s\n", buf);
257
258         return SM_OK;
259 }
260
261 static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
262 {
263         if (strncmp(msg, "250", 3) == 0) {
264                 const gchar *p = msg;
265                 p += 3;
266                 if (*p == '-' || *p == ' ') p++;
267                 if (g_strncasecmp(p, "AUTH", 4) == 0) {
268                         p += 5;
269                         if (strcasestr(p, "LOGIN"))
270                                 session->avail_auth_type |= SMTPAUTH_LOGIN;
271                         if (strcasestr(p, "CRAM-MD5"))
272                                 session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
273                         if (strcasestr(p, "DIGEST-MD5"))
274                                 session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
275                 }
276                 return SM_OK;
277         } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
278             (msg[3] == ' ' || msg[3] == '\0'))
279                 return SM_OK;
280         else if (msg[0] == '5' && msg[1] == '0' &&
281                  (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
282                 return SM_ERROR;
283
284         return SM_ERROR;
285 }
286
287 static gint smtp_starttls(SMTPSession *session)
288 {
289         session->state = SMTP_STARTTLS;
290
291         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
292         log_print("ESMTP> STARTTLS\n");
293
294         return SM_OK;
295 }
296
297 static gint smtp_auth_cram_md5(SMTPSession *session)
298 {
299         session->state = SMTP_AUTH;
300         session->auth_type = SMTPAUTH_CRAM_MD5;
301
302         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
303         log_print("ESMTP> AUTH CRAM-MD5\n");
304
305         return SM_OK;
306 }
307
308 static gint smtp_auth_login(SMTPSession *session)
309 {
310         session->state = SMTP_AUTH;
311         session->auth_type = SMTPAUTH_LOGIN;
312
313         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
314         log_print("ESMTP> AUTH LOGIN\n");
315
316         return SM_OK;
317 }
318
319 static gint smtp_helo(SMTPSession *session)
320 {
321         gchar buf[MSGBUFSIZE];
322
323         session->state = SMTP_HELO;
324
325         g_snprintf(buf, sizeof(buf), "HELO %s",
326                    session->hostname ? session->hostname : get_domain_name());
327         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
328         log_print("SMTP> %s\n", buf);
329
330         return SM_OK;
331 }
332
333 static gint smtp_rcpt(SMTPSession *session)
334 {
335         gchar buf[MSGBUFSIZE];
336         gchar *to;
337
338         g_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
339
340         session->state = SMTP_RCPT;
341
342         to = (gchar *)session->cur_to->data;
343
344         if (strchr(to, '<'))
345                 g_snprintf(buf, sizeof(buf), "RCPT TO: %s", to);
346         else
347                 g_snprintf(buf, sizeof(buf), "RCPT TO: <%s>", to);
348         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
349         log_print("SMTP> %s\n", buf);
350
351         session->cur_to = session->cur_to->next;
352
353         return SM_OK;
354 }
355
356 static gint smtp_data(SMTPSession *session)
357 {
358         session->state = SMTP_DATA;
359
360         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
361         log_print("SMTP> DATA\n");
362
363         return SM_OK;
364 }
365
366 static gint smtp_send_data(SMTPSession *session)
367 {
368         session->state = SMTP_SEND_DATA;
369
370         session_send_data(SESSION(session), session->send_data,
371                           session->send_data_len);
372
373         return SM_OK;
374 }
375
376 static gint smtp_rset(SMTPSession *session)
377 {
378         session->state = SMTP_RSET;
379
380         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
381         log_print("SMTP> RSET\n");
382
383         return SM_OK;
384 }
385
386 static gint smtp_quit(SMTPSession *session)
387 {
388         session->state = SMTP_QUIT;
389
390         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
391         log_print("SMTP> QUIT\n");
392
393         return SM_OK;
394 }
395
396 static gint smtp_eom(SMTPSession *session)
397 {
398         session->state = SMTP_EOM;
399
400         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
401         log_print("SMTP> . (EOM)\n");
402
403         return SM_OK;
404 }
405
406 static gint smtp_session_recv_msg(Session *session, const gchar *msg)
407 {
408         SMTPSession *smtp_session = SMTP_SESSION(session);
409         gboolean cont = FALSE;
410
411         if (strlen(msg) < 4)
412                 return -1;
413
414         log_print("SMTP< %s\n", msg);
415
416         if (msg[0] == '5' && msg[1] == '0' &&
417             (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
418                 smtp_session->state = SMTP_ERROR;
419                 return -1;
420         }
421
422         if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
423                 smtp_session->state = SMTP_ERROR;
424                 return -1;
425         }
426
427         if (msg[3] == '-')
428                 cont = TRUE;
429         else if (msg[3] != ' ' && msg[3] != '\0') {
430                 smtp_session->state = SMTP_ERROR;
431                 return -1;
432         }
433
434         /* ignore all multiline responses except for EHLO */
435         if (cont && smtp_session->state != SMTP_EHLO)
436                 return 1;
437
438         switch (smtp_session->state) {
439         case SMTP_READY:
440         case SMTP_CONNECTED:
441 #if USE_OPENSSL
442                 if (smtp_session->user || session->ssl_type != SSL_NONE)
443 #else
444                 if (smtp_session->user)
445 #endif
446                         smtp_ehlo(smtp_session);
447                 else
448                         smtp_helo(smtp_session);
449                 break;
450         case SMTP_HELO:
451                 smtp_from(smtp_session);
452                 break;
453         case SMTP_EHLO:
454                 smtp_ehlo_recv(smtp_session, msg);
455                 if (cont == TRUE)
456                         break;
457 #if USE_OPENSSL
458                 if (session->ssl_type == SSL_STARTTLS &&
459                     smtp_session->tls_init_done == FALSE) {
460                         smtp_starttls(smtp_session);
461                         break;
462                 }
463 #endif
464                 if (smtp_session->user) {
465                         if (smtp_auth(smtp_session) != SM_OK)
466                                 smtp_from(smtp_session);
467                 } else
468                         smtp_from(smtp_session);
469                 break;
470         case SMTP_STARTTLS:
471 #if USE_OPENSSL
472                 if (session_start_tls(session) < 0)
473                         return -1;
474                 smtp_session->tls_init_done = TRUE;
475                 smtp_ehlo(smtp_session);
476 #endif
477                 break;
478         case SMTP_AUTH:
479                 smtp_auth_recv(smtp_session, msg);
480                 break;
481         case SMTP_AUTH_LOGIN_USER:
482                 smtp_auth_login_user_recv(smtp_session, msg);
483                 break;
484         case SMTP_AUTH_LOGIN_PASS:
485         case SMTP_AUTH_CRAM_MD5:
486                 smtp_from(smtp_session);
487                 break;
488         case SMTP_FROM:
489                 if (smtp_session->cur_to)
490                         smtp_rcpt(smtp_session);
491                 break;
492         case SMTP_RCPT:
493                 if (smtp_session->cur_to)
494                         smtp_rcpt(smtp_session);
495                 else
496                         smtp_data(smtp_session);
497                 break;
498         case SMTP_DATA:
499                 smtp_send_data(smtp_session);
500                 break;
501         case SMTP_EOM:
502                 smtp_quit(smtp_session);
503                 break;
504         case SMTP_QUIT:
505                 session_disconnect(session);
506                 break;
507         case SMTP_ERROR:
508         default:
509                 return -1;
510         }
511
512         if (cont)
513                 return 1;
514
515         return 0;
516 }
517
518 static gint smtp_session_send_data_finished(Session *session, guint len)
519 {
520         smtp_eom(SMTP_SESSION(session));
521         return 0;
522 }