summaryrefslogtreecommitdiff
path: root/libsylph/smtp.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph/smtp.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph/smtp.c')
-rw-r--r--libsylph/smtp.c617
1 files changed, 617 insertions, 0 deletions
diff --git a/libsylph/smtp.c b/libsylph/smtp.c
new file mode 100644
index 0000000..da41a97
--- /dev/null
+++ b/libsylph/smtp.c
@@ -0,0 +1,617 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2008 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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_hmac.h"
31#include "base64.h"
32#include "utils.h"
33
34static void smtp_session_destroy(Session *session);
35
36static gint smtp_from(SMTPSession *session);
37
38static gint smtp_auth(SMTPSession *session);
39static gint smtp_starttls(SMTPSession *session);
40static gint smtp_auth_cram_md5(SMTPSession *session);
41static gint smtp_auth_plain(SMTPSession *session);
42static gint smtp_auth_login(SMTPSession *session);
43
44static gint smtp_ehlo(SMTPSession *session);
45static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg);
46
47static gint smtp_helo(SMTPSession *session);
48static gint smtp_rcpt(SMTPSession *session);
49static gint smtp_data(SMTPSession *session);
50static gint smtp_send_data(SMTPSession *session);
51/* static gint smtp_rset(SMTPSession *session); */
52static gint smtp_quit(SMTPSession *session);
53static gint smtp_eom(SMTPSession *session);
54
55static gint smtp_session_recv_msg(Session *session, const gchar *msg);
56static gint smtp_session_send_data_finished(Session *session, guint len);
57
58
59Session *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_SSL
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_fp = 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
103static 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 if (smtp_session->send_data_fp)
113 fclose(smtp_session->send_data_fp);
114
115 g_free(smtp_session->error_msg);
116}
117
118static gint smtp_from(SMTPSession *session)
119{
120 gchar buf[SMTPBUFSIZE];
121
122 g_return_val_if_fail(session->from != NULL, SM_ERROR);
123
124 session->state = SMTP_FROM;
125
126 if (strchr(session->from, '<'))
127 g_snprintf(buf, sizeof(buf), "MAIL FROM:%s", session->from);
128 else
129 g_snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", session->from);
130
131 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
132 log_print("SMTP> %s\n", buf);
133
134 return SM_OK;
135}
136
137static gint smtp_auth(SMTPSession *session)
138{
139
140 g_return_val_if_fail(session->user != NULL, SM_ERROR);
141
142 session->state = SMTP_AUTH;
143
144 if (session->forced_auth_type == SMTPAUTH_CRAM_MD5 ||
145 (session->forced_auth_type == 0 &&
146 (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0))
147 smtp_auth_cram_md5(session);
148 else if (session->forced_auth_type == SMTPAUTH_PLAIN ||
149 (session->forced_auth_type == 0 &&
150 (session->avail_auth_type & SMTPAUTH_PLAIN) != 0))
151 smtp_auth_plain(session);
152 else if (session->forced_auth_type == SMTPAUTH_LOGIN ||
153 (session->forced_auth_type == 0 &&
154 (session->avail_auth_type & SMTPAUTH_LOGIN) != 0))
155 smtp_auth_login(session);
156 else {
157 log_warning(_("SMTP AUTH not available\n"));
158 return SM_AUTHFAIL;
159 }
160
161 return SM_OK;
162}
163
164static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
165{
166 gchar buf[SMTPBUFSIZE];
167
168 switch (session->auth_type) {
169 case SMTPAUTH_LOGIN:
170 session->state = SMTP_AUTH_LOGIN_USER;
171
172 if (!strncmp(msg, "334 ", 4)) {
173 base64_encode(buf, (guchar *)session->user,
174 strlen(session->user));
175
176 session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
177 buf);
178 log_print("ESMTP> [USERID]\n");
179 } else {
180 /* Server rejects AUTH */
181 session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
182 "*");
183 log_print("ESMTP> *\n");
184 }
185 break;
186 case SMTPAUTH_CRAM_MD5:
187 session->state = SMTP_AUTH_CRAM_MD5;
188
189 if (!strncmp(msg, "334 ", 4)) {
190 gchar *response;
191 gchar *response64;
192 guchar *challenge;
193 gint challengelen;
194 gchar hexdigest[33];
195
196 challenge = g_malloc(strlen(msg + 4) + 1);
197 challengelen = base64_decode(challenge, msg + 4, -1);
198 challenge[challengelen] = '\0';
199 log_print("ESMTP< [Decoded: %s]\n", challenge);
200
201 g_snprintf(buf, sizeof(buf), "%s", session->pass);
202 md5_hex_hmac(hexdigest, challenge, challengelen,
203 (guchar *)buf, strlen(buf));
204 g_free(challenge);
205
206 response = g_strdup_printf
207 ("%s %s", session->user, hexdigest);
208 log_print("ESMTP> [Encoded: %s]\n", response);
209
210 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
211 base64_encode(response64, (guchar *)response,
212 strlen(response));
213 g_free(response);
214
215 session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
216 response64);
217 log_print("ESMTP> %s\n", response64);
218 g_free(response64);
219 } else {
220 /* Server rejects AUTH */
221 session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
222 "*");
223 log_print("ESMTP> *\n");
224 }
225 break;
226 case SMTPAUTH_DIGEST_MD5:
227 default:
228 /* stop smtp_auth when no correct authtype */
229 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*");
230 log_print("ESMTP> *\n");
231 break;
232 }
233
234 return SM_OK;
235}
236
237static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
238{
239 gchar buf[SMTPBUFSIZE];
240
241 session->state = SMTP_AUTH_LOGIN_PASS;
242
243 if (!strncmp(msg, "334 ", 4))
244 base64_encode(buf, (guchar *)session->pass,
245 strlen(session->pass));
246 else
247 /* Server rejects AUTH */
248 g_snprintf(buf, sizeof(buf), "*");
249
250 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
251 log_print("ESMTP> [PASSWORD]\n");
252
253 return SM_OK;
254}
255
256static gint smtp_ehlo(SMTPSession *session)
257{
258 gchar buf[SMTPBUFSIZE];
259
260 session->state = SMTP_EHLO;
261
262 session->avail_auth_type = 0;
263
264 g_snprintf(buf, sizeof(buf), "EHLO %s",
265 session->hostname ? session->hostname : get_domain_name());
266 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
267 log_print("ESMTP> %s\n", buf);
268
269 return SM_OK;
270}
271
272static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
273{
274 if (strncmp(msg, "250", 3) == 0) {
275 const gchar *p = msg;
276 p += 3;
277 if (*p == '-' || *p == ' ') p++;
278 if (g_ascii_strncasecmp(p, "AUTH", 4) == 0 && p[4] != '\0') {
279 p += 5;
280 if (strcasestr(p, "PLAIN"))
281 session->avail_auth_type |= SMTPAUTH_PLAIN;
282 if (strcasestr(p, "LOGIN"))
283 session->avail_auth_type |= SMTPAUTH_LOGIN;
284 if (strcasestr(p, "CRAM-MD5"))
285 session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
286 if (strcasestr(p, "DIGEST-MD5"))
287 session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
288 }
289 return SM_OK;
290 } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
291 (msg[3] == ' ' || msg[3] == '\0'))
292 return SM_OK;
293 else if (msg[0] == '5' && msg[1] == '0' &&
294 (msg[2] == '4' || msg[2] == '3' || msg[2] == '1'))
295 return SM_ERROR;
296
297 return SM_ERROR;
298}
299
300static gint smtp_starttls(SMTPSession *session)
301{
302 session->state = SMTP_STARTTLS;
303
304 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
305 log_print("ESMTP> STARTTLS\n");
306
307 return SM_OK;
308}
309
310static gint smtp_auth_cram_md5(SMTPSession *session)
311{
312 session->state = SMTP_AUTH;
313 session->auth_type = SMTPAUTH_CRAM_MD5;
314
315 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
316 log_print("ESMTP> AUTH CRAM-MD5\n");
317
318 return SM_OK;
319}
320
321static gint smtp_auth_plain(SMTPSession *session)
322{
323 gchar *authstr;
324 gint authlen;
325 gchar *outbuf;
326 gchar *p;
327
328 session->state = SMTP_AUTH_PLAIN;
329 session->auth_type = SMTPAUTH_PLAIN;
330
331 /*
332 * construct the string: \0<user>\0<pass>
333 */
334
335 authlen = 1 + strlen(session->user) + 1 + strlen(session->pass);
336 authstr = g_malloc(authlen + 1);
337
338 p = authstr;
339
340 *p++ = '\0';
341 strcpy(p, session->user);
342 p += strlen(p) + 1;
343 strcpy(p, session->pass);
344
345 outbuf = g_malloc(sizeof("AUTH PLAIN ") + authlen * 2 + 1);
346
347 strcpy(outbuf, "AUTH PLAIN ");
348 p = outbuf + strlen(outbuf);
349 base64_encode(p, (guchar *)authstr, authlen);
350
351 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, outbuf);
352 log_print("ESMTP> AUTH PLAIN ********\n");
353
354 g_free(outbuf);
355 g_free(authstr);
356
357 return SM_OK;
358}
359
360static gint smtp_auth_login(SMTPSession *session)
361{
362 session->state = SMTP_AUTH;
363 session->auth_type = SMTPAUTH_LOGIN;
364
365 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
366 log_print("ESMTP> AUTH LOGIN\n");
367
368 return SM_OK;
369}
370
371static gint smtp_helo(SMTPSession *session)
372{
373 gchar buf[SMTPBUFSIZE];
374
375 session->state = SMTP_HELO;
376
377 g_snprintf(buf, sizeof(buf), "HELO %s",
378 session->hostname ? session->hostname : get_domain_name());
379 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
380 log_print("SMTP> %s\n", buf);
381
382 return SM_OK;
383}
384
385static gint smtp_rcpt(SMTPSession *session)
386{
387 gchar buf[SMTPBUFSIZE];
388 gchar *to;
389
390 g_return_val_if_fail(session->cur_to != NULL, SM_ERROR);
391
392 session->state = SMTP_RCPT;
393
394 to = (gchar *)session->cur_to->data;
395
396 if (strchr(to, '<'))
397 g_snprintf(buf, sizeof(buf), "RCPT TO:%s", to);
398 else
399 g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>", to);
400 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
401 log_print("SMTP> %s\n", buf);
402
403 session->cur_to = session->cur_to->next;
404
405 return SM_OK;
406}
407
408static gint smtp_data(SMTPSession *session)
409{
410 session->state = SMTP_DATA;
411
412 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
413 log_print("SMTP> DATA\n");
414
415 return SM_OK;
416}
417
418static gint smtp_send_data(SMTPSession *session)
419{
420 session->state = SMTP_SEND_DATA;
421
422 session_send_data(SESSION(session), session->send_data_fp,
423 session->send_data_len);
424
425 return SM_OK;
426}
427
428#if 0
429static gint smtp_rset(SMTPSession *session)
430{
431 session->state = SMTP_RSET;
432
433 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
434 log_print("SMTP> RSET\n");
435
436 return SM_OK;
437}
438#endif
439
440static gint smtp_quit(SMTPSession *session)
441{
442 session->state = SMTP_QUIT;
443
444 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT");
445 log_print("SMTP> QUIT\n");
446
447 return SM_OK;
448}
449
450static gint smtp_eom(SMTPSession *session)
451{
452 session->state = SMTP_EOM;
453
454 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
455 log_print("SMTP> . (EOM)\n");
456
457 return SM_OK;
458}
459
460static gint smtp_session_recv_msg(Session *session, const gchar *msg)
461{
462 SMTPSession *smtp_session = SMTP_SESSION(session);
463 gboolean cont = FALSE;
464
465 if (strlen(msg) < 4) {
466 log_warning(_("bad SMTP response\n"));
467 return -1;
468 }
469
470 switch (smtp_session->state) {
471 case SMTP_EHLO:
472 case SMTP_STARTTLS:
473 case SMTP_AUTH:
474 case SMTP_AUTH_PLAIN:
475 case SMTP_AUTH_LOGIN_USER:
476 case SMTP_AUTH_LOGIN_PASS:
477 case SMTP_AUTH_CRAM_MD5:
478 log_print("ESMTP< %s\n", msg);
479 break;
480 default:
481 log_print("SMTP< %s\n", msg);
482 break;
483 }
484
485 if (msg[0] == '5' && msg[1] == '0' &&
486 (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) {
487 log_warning(_("error occurred on SMTP session\n"));
488 smtp_session->state = SMTP_ERROR;
489 smtp_session->error_val = SM_ERROR;
490 g_free(smtp_session->error_msg);
491 smtp_session->error_msg = g_strdup(msg);
492 return -1;
493 }
494
495 if (!strncmp(msg, "535", 3)) {
496 log_warning(_("error occurred on authentication\n"));
497 smtp_session->state = SMTP_ERROR;
498 smtp_session->error_val = SM_AUTHFAIL;
499 g_free(smtp_session->error_msg);
500 smtp_session->error_msg = g_strdup(msg);
501 return -1;
502 }
503
504 if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') {
505 log_warning(_("error occurred on SMTP session\n"));
506 smtp_session->state = SMTP_ERROR;
507 smtp_session->error_val = SM_ERROR;
508 g_free(smtp_session->error_msg);
509 smtp_session->error_msg = g_strdup(msg);
510 return -1;
511 }
512
513 if (msg[3] == '-')
514 cont = TRUE;
515 else if (msg[3] != ' ' && msg[3] != '\0') {
516 log_warning(_("bad SMTP response\n"));
517 smtp_session->state = SMTP_ERROR;
518 smtp_session->error_val = SM_UNRECOVERABLE;
519 return -1;
520 }
521
522 /* ignore all multiline responses except for EHLO */
523 if (cont && smtp_session->state != SMTP_EHLO)
524 return session_recv_msg(session);
525
526 switch (smtp_session->state) {
527 case SMTP_READY:
528 case SMTP_CONNECTED:
529#if USE_SSL
530 if (smtp_session->user || session->ssl_type != SSL_NONE)
531#else
532 if (smtp_session->user)
533#endif
534 smtp_ehlo(smtp_session);
535 else
536 smtp_helo(smtp_session);
537 break;
538 case SMTP_HELO:
539 smtp_from(smtp_session);
540 break;
541 case SMTP_EHLO:
542 smtp_ehlo_recv(smtp_session, msg);
543 if (cont == TRUE)
544 break;
545#if USE_SSL
546 if (session->ssl_type == SSL_STARTTLS &&
547 smtp_session->tls_init_done == FALSE) {
548 smtp_starttls(smtp_session);
549 break;
550 }
551#endif
552 if (smtp_session->user) {
553 if (smtp_auth(smtp_session) != SM_OK)
554 smtp_from(smtp_session);
555 } else
556 smtp_from(smtp_session);
557 break;
558 case SMTP_STARTTLS:
559#if USE_SSL
560 if (session_start_tls(session) < 0) {
561 log_warning(_("can't start TLS session\n"));
562 smtp_session->state = SMTP_ERROR;
563 smtp_session->error_val = SM_ERROR;
564 return -1;
565 }
566 smtp_session->tls_init_done = TRUE;
567 smtp_ehlo(smtp_session);
568#endif
569 break;
570 case SMTP_AUTH:
571 smtp_auth_recv(smtp_session, msg);
572 break;
573 case SMTP_AUTH_LOGIN_USER:
574 smtp_auth_login_user_recv(smtp_session, msg);
575 break;
576 case SMTP_AUTH_PLAIN:
577 case SMTP_AUTH_LOGIN_PASS:
578 case SMTP_AUTH_CRAM_MD5:
579 smtp_from(smtp_session);
580 break;
581 case SMTP_FROM:
582 if (smtp_session->cur_to)
583 smtp_rcpt(smtp_session);
584 break;
585 case SMTP_RCPT:
586 if (smtp_session->cur_to)
587 smtp_rcpt(smtp_session);
588 else
589 smtp_data(smtp_session);
590 break;
591 case SMTP_DATA:
592 smtp_send_data(smtp_session);
593 break;
594 case SMTP_EOM:
595 smtp_quit(smtp_session);
596 break;
597 case SMTP_QUIT:
598 session_disconnect(session);
599 break;
600 case SMTP_ERROR:
601 default:
602 log_warning(_("error occurred on SMTP session\n"));
603 smtp_session->error_val = SM_ERROR;
604 return -1;
605 }
606
607 if (cont)
608 return session_recv_msg(session);
609
610 return 0;
611}
612
613static gint smtp_session_send_data_finished(Session *session, guint len)
614{
615 smtp_eom(SMTP_SESSION(session));
616 return 0;
617}