Statistics
| Revision:

root / src / smtp.c @ 1

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 <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

    
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
}