Statistics
| Revision:

root / src / smtp.c @ 133

History | View | Annotate | Download (13.6 kB)

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