summaryrefslogtreecommitdiffabout
path: root/libetpan/src/low-level/smtp/mailsmtp.c
Unidiff
Diffstat (limited to 'libetpan/src/low-level/smtp/mailsmtp.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libetpan/src/low-level/smtp/mailsmtp.c984
1 files changed, 984 insertions, 0 deletions
diff --git a/libetpan/src/low-level/smtp/mailsmtp.c b/libetpan/src/low-level/smtp/mailsmtp.c
new file mode 100644
index 0000000..85659e9
--- a/dev/null
+++ b/libetpan/src/low-level/smtp/mailsmtp.c
@@ -0,0 +1,984 @@
1/*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2005 - DINH Viet Hoa,
5 * All rights reserved.
6 *
7 * SMTP AUTH support by Juergen Graf
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the libEtPan! project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * $Id$
36 */
37
38#include "mailsmtp.h"
39#include "connect.h"
40#include "md5.h"
41#include "base64.h"
42#include "mail.h"
43
44#include <netinet/in.h>
45#include <string.h>
46#include <stdlib.h>
47#include <unistd.h>
48#include <stdio.h>
49
50
51/*
52 RFC 2821 : SMTP
53 RFC 1891 : SMTP Service Extension for Delivery Status Notifications
54
55 RFC 1428 : Transition of Internet Mail from Just-Send-8 to 8bit-SMTP/MIME
56 RFC 1652 : SMTP Service Extension for 8bit-MIMEtransport
57 RFC 1845 : SMTP Service Extension for Checkpoint/Restart
58 RFC 1846 : SMTP 521 Reply Code
59 RFC 1870 : SMTP Service Extension for Message Size Declaration
60 RFC 1985 : SMTP Service Extension for Remote Message Queue Starting
61 RFC 2034 : SMTP Service Extension for Returning Enhanced Error Codes
62 RFC 2442 : The Batch SMTP Media Type
63 RFC 2487 : SMTP Service Extension for Secure SMTP over TLS
64 RFC 2505 : Anti-Spam Recommendations for SMTP MTAs
65 RFC 2554 : SMTP Service Extension for Authentication
66 RFC 2645 : ON-DEMAND MAIL RELAY (ODMR) SMTP with Dynamic IP Addresses
67 RFC 2852 : Deliver By SMTP Service Extension
68 RFC 2920 : SMTP Service Extension for Command Pipelining
69 RFC 3030 : SMTP Service Extensions for Transmission of Large and Binary MIME
70 Messages
71*/
72
73#define SMTP_STATUS_CONTINUE 0x1000
74
75mailsmtp * mailsmtp_new(size_t progr_rate,
76 progress_function * progr_fun)
77{
78 mailsmtp * session;
79
80 session = malloc(sizeof(* session));
81 if (session == NULL)
82 goto err;
83
84 session->stream = NULL;
85
86 session->progr_rate = progr_rate;
87 session->progr_fun = progr_fun;
88
89 session->response = NULL;
90
91 session->line_buffer = mmap_string_new("");
92 if (session->line_buffer == NULL)
93 goto free_session;
94
95 session->response_buffer = mmap_string_new("");
96 if (session->response_buffer == NULL)
97 goto free_line_buffer;
98
99 session->esmtp = 0;
100 session->auth = MAILSMTP_AUTH_NOT_CHECKED;
101
102 return session;
103
104 free_line_buffer:
105 mmap_string_free(session->line_buffer);
106 free_session:
107 free(session);
108 err:
109 return NULL;
110}
111
112void mailsmtp_free(mailsmtp * session)
113{
114 if (session->stream)
115 mailsmtp_quit(session);
116
117 mmap_string_free(session->line_buffer);
118 mmap_string_free(session->response_buffer);
119 free(session);
120}
121
122static int send_command(mailsmtp * f, char * command);
123
124static int read_response(mailsmtp * session);
125
126/* smtp operations */
127
128int mailsmtp_connect(mailsmtp * session, mailstream * s)
129{
130 int code;
131
132 session->stream = s;
133
134 code = read_response(session);
135
136 switch (code) {
137 case 220:
138 return MAILSMTP_NO_ERROR;
139
140 case 554:
141 session->stream = NULL;
142 mailstream_close(s);
143 return MAILSMTP_ERROR_SERVICE_NOT_AVAILABLE;
144
145 default:
146 session->stream = NULL;
147 mailstream_close(s);
148 return MAILSMTP_ERROR_UNEXPECTED_CODE;
149 }
150}
151
152
153#define SMTP_STRING_SIZE 513
154
155int mailsmtp_quit(mailsmtp * session)
156{
157 char command[SMTP_STRING_SIZE];
158 int r;
159
160 snprintf(command, SMTP_STRING_SIZE, "QUIT\r\n");
161 r = send_command(session, command);
162 if (r == -1)
163 return MAILSMTP_ERROR_STREAM;
164 r = read_response(session);
165 if (r == 0)
166 return MAILSMTP_ERROR_STREAM;
167 mailstream_close(session->stream);
168 session->stream = NULL;
169
170 return MAILSMTP_NO_ERROR;
171}
172
173
174
175#define HOSTNAME_SIZE 256
176
177int mailsmtp_helo(mailsmtp * session)
178{
179 int r;
180 char hostname[HOSTNAME_SIZE];
181 char command[SMTP_STRING_SIZE];
182
183 r = gethostname(hostname, HOSTNAME_SIZE);
184 if (r < 0)
185 return MAILSMTP_ERROR_HOSTNAME;
186
187 snprintf(command, SMTP_STRING_SIZE, "HELO %s\r\n", hostname);
188 r = send_command(session, command);
189 if (r == -1)
190 return MAILSMTP_ERROR_STREAM;
191 r = read_response(session);
192
193 switch (r) {
194 case 250:
195 session->esmtp = 0;
196 session->auth = MAILSMTP_AUTH_NOT_CHECKED;
197 return MAILSMTP_NO_ERROR;
198
199 case 504:
200 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
201
202 case 550:
203 return MAILSMTP_ERROR_ACTION_NOT_TAKEN;
204
205 case 0:
206 return MAILSMTP_ERROR_STREAM;
207
208 default:
209 return MAILSMTP_ERROR_UNEXPECTED_CODE;
210 }
211}
212
213int mailsmtp_mail(mailsmtp * session, const char * from)
214{
215 int r;
216 char command[SMTP_STRING_SIZE];
217
218 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s>\r\n", from);
219 r = send_command(session, command);
220 if (r == -1)
221 return MAILSMTP_ERROR_STREAM;
222 r = read_response(session);
223
224 switch (r) {
225 case 250:
226 return MAILSMTP_NO_ERROR;
227
228 case 552:
229 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
230
231 case 451:
232 return MAILSMTP_ERROR_IN_PROCESSING;
233
234 case 452:
235 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
236
237 case 550:
238 return MAILSMTP_ERROR_MAILBOX_UNAVAILABLE;
239
240 case 553:
241 return MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED;
242
243 case 503:
244 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
245
246 case 0:
247 return MAILSMTP_ERROR_STREAM;
248
249 default:
250 return MAILSMTP_ERROR_UNEXPECTED_CODE;
251 }
252}
253
254int mailsmtp_rcpt(mailsmtp * session, const char * to)
255{
256 return mailesmtp_rcpt(session, to, 0, NULL);
257}
258
259int mailsmtp_data(mailsmtp * session)
260{
261 int r;
262 char command[SMTP_STRING_SIZE];
263
264 snprintf(command, SMTP_STRING_SIZE, "DATA\r\n");
265 r = send_command(session, command);
266 if (r == -1)
267 return MAILSMTP_ERROR_STREAM;
268 r = read_response(session);
269
270 switch (r) {
271 case 354:
272 return MAILSMTP_NO_ERROR;
273
274 case 451:
275 return MAILSMTP_ERROR_IN_PROCESSING;
276
277 case 554:
278 return MAILSMTP_ERROR_TRANSACTION_FAILED;
279
280 case 503:
281 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
282
283 default:
284 return MAILSMTP_ERROR_UNEXPECTED_CODE;
285 }
286}
287
288static int send_data(mailsmtp * session, const char * message, size_t size);
289
290int mailsmtp_data_message(mailsmtp * session,
291 const char * message,
292 size_t size)
293{
294 int r;
295
296 r = send_data(session, message, size);
297 if (r == -1)
298 return MAILSMTP_ERROR_STREAM;
299
300 r = read_response(session);
301
302 switch(r) {
303 case 250:
304 return MAILSMTP_NO_ERROR;
305
306 case 552:
307 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
308
309 case 554:
310 return MAILSMTP_ERROR_TRANSACTION_FAILED;
311
312 case 451:
313 return MAILSMTP_ERROR_IN_PROCESSING;
314
315 case 452:
316 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
317
318 case 0:
319 return MAILSMTP_ERROR_STREAM;
320
321 default:
322 return MAILSMTP_ERROR_UNEXPECTED_CODE;
323 }
324}
325
326/* esmtp operations */
327
328
329/**
330 * called during mailesmtp_ehlo
331 * checks EHLO answer for server extensions and sets flags
332 * in session->esmtp
333 * checks AUTH methods in session->response and sets flags
334 * in session->auth
335 */
336#define isdelim(x) ((x) == ' ' || (x) == '\r' || (x) == '\n' || (x) == '\0')
337
338int mailesmtp_parse_ehlo(mailsmtp * session)
339{
340 char * response;
341
342 /* restore data */
343 session->esmtp = MAILSMTP_ESMTP;
344 session->auth = MAILSMTP_AUTH_CHECKED;
345
346 response = session->response;
347
348 /* ESMTP supported extensions :
349 DSN
350 EXPN
351 8BITMIME
352 SIZE [<n>]
353 ETRN
354 STARTTLS
355 AUTH <mechanisms...>
356 */
357 while (response != NULL) {
358 if (!strncasecmp(response, "EXPN", 4) && isdelim(response[4]))
359 session->esmtp |= MAILSMTP_ESMTP_EXPN;
360 else if (!strncasecmp(response, "ETRN", 4) && isdelim(response[4]))
361 session->esmtp |= MAILSMTP_ESMTP_ETRN;
362 else if (!strncasecmp(response, "DSN", 3) && isdelim(response[3]))
363 session->esmtp |= MAILSMTP_ESMTP_DSN;
364 else if (!strncasecmp(response, "8BITMIME", 8) && isdelim(response[8]))
365 session->esmtp |= MAILSMTP_ESMTP_8BITMIME;
366 else if (!strncasecmp(response, "STARTTLS", 8) && isdelim(response[8]))
367 session->esmtp |= MAILSMTP_ESMTP_STARTTLS;
368 else if (!strncasecmp(response, "SIZE", 4) && isdelim(response[4])) {
369 session->esmtp |= MAILSMTP_ESMTP_SIZE;
370 /* TODO: grab optionnal max size */
371 } else if (!strncasecmp(response, "AUTH ", 5)) {
372 response += 5; /* remove "AUTH " */
373 while (response[0] != '\n' && response[0] != '\0') {
374 while (response[0] == ' ') response++;
375 if (strncasecmp(response, "LOGIN", 5) == 0) {
376 session->auth |= MAILSMTP_AUTH_LOGIN;
377 response += 5;
378 } else if (strncasecmp(response, "CRAM-MD5", 8) == 0) {
379 session->auth |= MAILSMTP_AUTH_CRAM_MD5;
380 response += 8;
381 } else if (strncasecmp(response, "PLAIN", 5) == 0) {
382 session->auth |= MAILSMTP_AUTH_PLAIN;
383 response += 5;
384 } else {
385 /* unknown auth method - jump to next word or eol */
386 while (!isdelim(response[0]) || response[0] == '\r')
387 response++;
388 }
389 }
390 }
391 response = strpbrk(response, "\n");
392 if (response != NULL)
393 response++;
394 }
395
396 return MAILSMTP_NO_ERROR;
397}
398
399
400int mailesmtp_ehlo(mailsmtp * session)
401{
402 int r;
403 char hostname[HOSTNAME_SIZE];
404 char command[SMTP_STRING_SIZE];
405
406 r = gethostname(hostname, HOSTNAME_SIZE);
407 if (r != 0)
408 return MAILSMTP_ERROR_HOSTNAME;
409
410 snprintf(command, SMTP_STRING_SIZE, "EHLO %s\r\n", hostname);
411 r = send_command(session, command);
412 if (r == -1)
413 return MAILSMTP_ERROR_STREAM;
414 r = read_response(session);
415
416 switch (r) {
417 case 250:
418 return mailesmtp_parse_ehlo(session);
419
420 case 504:
421 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
422
423 case 550:
424 return MAILSMTP_ERROR_ACTION_NOT_TAKEN;
425
426 case 0:
427 return MAILSMTP_ERROR_STREAM;
428
429 default:
430 return MAILSMTP_ERROR_UNEXPECTED_CODE;
431 }
432}
433
434/*
435 if return_full is TRUE, the entire message is returned on error
436 envid can be NULL
437*/
438
439
440int mailesmtp_mail(mailsmtp * session,
441 const char * from,
442 int return_full,
443 const char * envid)
444{
445 int r;
446 char command[SMTP_STRING_SIZE];
447 char *body = "";
448
449#if notyet
450 /* TODO: figure out a way for the user to explicity enable this or not */
451 if (session->esmtp & MAILSMTP_ESMTP_8BITMIME)
452 body = " BODY=8BITMIME";
453#endif
454
455 if (session->esmtp & MAILSMTP_ESMTP_DSN) {
456 if (envid)
457 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s> RET=%s ENVID=%s%s\r\n",
458 from, return_full ? "FULL" : "HDRS", envid, body);
459 else
460 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s> RET=%s%s\r\n",
461 from, return_full ? "FULL" : "HDRS", body);
462 } else
463 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s>%s\r\n",
464 from, body);
465
466 r = send_command(session, command);
467 if (r == -1)
468 return MAILSMTP_ERROR_STREAM;
469 r = read_response(session);
470
471 switch (r) {
472 case 250:
473 return MAILSMTP_NO_ERROR;
474
475 case 552:
476 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
477
478 case 451:
479 return MAILSMTP_ERROR_IN_PROCESSING;
480
481 case 452:
482 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
483
484 case 550:
485 return MAILSMTP_ERROR_MAILBOX_UNAVAILABLE;
486
487 case 553:
488 return MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED;
489
490 case 503:
491 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
492
493 case 0:
494 return MAILSMTP_ERROR_STREAM;
495
496 default:
497 return MAILSMTP_ERROR_UNEXPECTED_CODE;
498 }
499}
500
501int mailesmtp_rcpt(mailsmtp * session,
502 const char * to,
503 int notify,
504 const char * orcpt)
505{
506 int r;
507 char command[SMTP_STRING_SIZE];
508 char notify_str[30] = "";
509 char notify_info_str[30] = "";
510
511 if (notify != 0 && session->esmtp & MAILSMTP_ESMTP_DSN) {
512 if (notify & MAILSMTP_DSN_NOTIFY_SUCCESS)
513 strcat(notify_info_str, ",SUCCESS");
514 if (notify & MAILSMTP_DSN_NOTIFY_FAILURE)
515 strcat(notify_info_str, ",FAILURE");
516 if (notify & MAILSMTP_DSN_NOTIFY_DELAY)
517 strcat(notify_info_str, ",DELAY");
518
519 if (notify & MAILSMTP_DSN_NOTIFY_NEVER)
520 strcpy(notify_info_str, ",NEVER");
521
522 notify_info_str[0] = '=';
523
524 strcpy(notify_str, " NOTIFY");
525 strcat(notify_str, notify_info_str);
526 }
527
528 if (orcpt && session->esmtp & MAILSMTP_ESMTP_DSN)
529 snprintf(command, SMTP_STRING_SIZE, "RCPT TO:<%s>%s ORCPT=%s\r\n",
530 to, notify_str, orcpt);
531 else
532 snprintf(command, SMTP_STRING_SIZE, "RCPT TO:<%s>%s\r\n", to, notify_str);
533
534 r = send_command(session, command);
535 if (r == -1)
536 return MAILSMTP_ERROR_STREAM;
537 r = read_response(session);
538
539 switch (r) {
540 case 250:
541 return MAILSMTP_NO_ERROR;
542
543 case 251: /* not local user, will be forwarded */
544 return MAILSMTP_NO_ERROR;
545
546 case 550:
547 case 450:
548 return MAILSMTP_ERROR_MAILBOX_UNAVAILABLE;
549
550 case 551:
551 return MAILSMTP_ERROR_USER_NOT_LOCAL;
552
553 case 552:
554 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
555
556 case 553:
557 return MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED;
558
559 case 451:
560 return MAILSMTP_ERROR_IN_PROCESSING;
561
562 case 452:
563 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
564
565 case 503:
566 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
567
568 case 0:
569 return MAILSMTP_ERROR_STREAM;
570
571 default:
572 return MAILSMTP_ERROR_UNEXPECTED_CODE;
573 }
574}
575
576int auth_map_errors(int err)
577{
578 switch (err) {
579 case 235:
580 return MAILSMTP_NO_ERROR; /* AUTH successfull */
581 case 334:
582 return MAILSMTP_NO_ERROR; /* AUTH in progress */
583 case 432:
584 return MAILSMTP_ERROR_AUTH_TRANSITION_NEEDED;
585 case 454:
586 return MAILSMTP_ERROR_AUTH_TEMPORARY_FAILTURE;
587 case 504:
588 return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED;
589 case 530:
590 return MAILSMTP_ERROR_AUTH_REQUIRED;
591 case 534:
592 return MAILSMTP_ERROR_AUTH_TOO_WEAK;
593 case 538:
594 return MAILSMTP_ERROR_AUTH_ENCRYPTION_REQUIRED;
595 default:
596 /* opportunistic approach ;) */
597 return MAILSMTP_NO_ERROR;
598 }
599}
600
601static int mailsmtp_auth_login(mailsmtp * session,
602 const char * user, const char * pass)
603{
604 int err;
605 char command[SMTP_STRING_SIZE];
606 char * user64, * pass64;
607
608 user64 = NULL;
609 pass64 = NULL;
610
611 user64 = encode_base64(user, strlen(user));
612 if (user64 == NULL) {
613 err = MAILSMTP_ERROR_MEMORY;
614 goto err_free;
615 }
616
617 pass64 = encode_base64(pass, strlen(pass));
618 if (pass64 == NULL) {
619 err = MAILSMTP_ERROR_MEMORY;
620 goto err_free;
621 }
622
623 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", user64);
624 err = send_command(session, command);
625 if (err == -1) {
626 err = MAILSMTP_ERROR_STREAM;
627 goto err_free;
628 }
629 err = read_response(session);
630 err = auth_map_errors(err);
631 if (err != MAILSMTP_NO_ERROR)
632 goto err_free;
633
634 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", pass64);
635 err = send_command(session, command);
636 if (err == -1) {
637 err = MAILSMTP_ERROR_STREAM;
638 goto err_free;
639 }
640 err = read_response(session);
641 err = auth_map_errors(err);
642
643 err_free:
644 free(user64);
645 free(pass64);
646
647 return err;
648}
649
650static int mailsmtp_auth_plain(mailsmtp * session,
651 const char * user, const char * pass)
652{
653 int err, len;
654 char command[SMTP_STRING_SIZE];
655 char * plain, * plain64;
656
657 len = strlen(user) + strlen(pass) + 3;
658 plain = (char *) malloc(len);
659 if (plain == NULL) {
660 err = MAILSMTP_ERROR_MEMORY;
661 goto err;
662 }
663
664 snprintf(plain, len, "%c%s%c%s", '\0', user, '\0', pass);
665 plain64 = encode_base64(plain, len - 1);
666
667 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", plain64);
668 err = send_command(session, command);
669 if (err == -1) {
670 err = MAILSMTP_ERROR_STREAM;
671 goto err_free;
672 }
673
674 err = read_response(session);
675 err = auth_map_errors(err);
676
677err_free:
678 free(plain64);
679 free(plain);
680
681 err:
682 return err;
683}
684
685static char * convert_hex(unsigned char *in, int len)
686{
687 static char hex[] = "0123456789abcdef";
688 char * out;
689 int i;
690
691 out = (char *) malloc(len * 2 + 1);
692 if (out == NULL)
693 return NULL;
694
695 for (i = 0; i < len; i++) {
696 out[i * 2] = hex[in[i] >> 4];
697 out[i * 2 + 1] = hex[in[i] & 15];
698 }
699
700 out[i*2] = 0;
701
702 return out;
703}
704
705static char * hash_md5(const char * sec_key, const char * data, int len)
706{
707 char key[65], digest[24];
708 char * hash_hex;
709
710 int sec_len, i;
711
712 sec_len = strlen(sec_key);
713
714 if (sec_len < 64) {
715 memcpy(key, sec_key, sec_len);
716 for (i = sec_len; i < 64; i++) {
717 key[i] = 0;
718 }
719 } else {
720 memcpy(key, sec_key, 64);
721 }
722
723 hmac_md5(data, len, key, 64, digest);
724 hash_hex = convert_hex(digest, 16);
725
726 return hash_hex;
727}
728
729static int mailsmtp_auth_cram_md5(mailsmtp * session,
730 const char * user, const char * pass)
731{
732 int err;
733 char command[SMTP_STRING_SIZE];
734 char *response, *auth_hex, *auth;
735
736 response = decode_base64(session->response, strlen(session->response));
737 if (response == NULL) return MAILSMTP_ERROR_MEMORY;
738
739 auth_hex = hash_md5(pass, response, strlen(response));
740 if (auth_hex == NULL) {
741 err = MAILSMTP_ERROR_MEMORY;
742 goto err_free_response;
743 }
744
745 snprintf(command, SMTP_STRING_SIZE, "%s %s", user, auth_hex);
746
747 auth = encode_base64(command, strlen(command));
748 if (auth == NULL) {
749 err = MAILSMTP_ERROR_MEMORY;
750 goto err_free_auth_hex;
751 }
752
753 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", auth);
754 err = send_command(session, command);
755 if (err == -1) {
756 err = MAILSMTP_ERROR_STREAM;
757 goto err_free;
758 }
759
760 err = read_response(session);
761 err = auth_map_errors(err);
762
763err_free:
764 free(auth);
765err_free_auth_hex:
766 free(auth_hex);
767err_free_response:
768 free(response);
769 return err;
770}
771
772int mailsmtp_auth_type(mailsmtp * session,
773 const char * user, const char * pass, int type)
774{
775 int err;
776 char command[SMTP_STRING_SIZE];
777
778 if (session->auth == MAILSMTP_AUTH_NOT_CHECKED)
779 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
780
781 if ( !(session->auth & type) ) return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED;
782
783 switch (type) {
784 case MAILSMTP_AUTH_LOGIN:
785 snprintf(command, SMTP_STRING_SIZE, "AUTH LOGIN\r\n");
786 break;
787 case MAILSMTP_AUTH_PLAIN:
788 snprintf(command, SMTP_STRING_SIZE, "AUTH PLAIN\r\n");
789 break;
790 case MAILSMTP_AUTH_CRAM_MD5:
791 snprintf(command, SMTP_STRING_SIZE, "AUTH CRAM-MD5\r\n");
792 break;
793 default:
794 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
795 }
796
797 err = send_command(session, command);
798 if (err == -1) return MAILSMTP_ERROR_STREAM;
799
800 err = read_response(session);
801 err = auth_map_errors(err);
802 if (err != MAILSMTP_NO_ERROR) return err;
803
804 switch (type) {
805 case MAILSMTP_AUTH_LOGIN:
806 return mailsmtp_auth_login(session, user, pass);
807 case MAILSMTP_AUTH_PLAIN:
808 return mailsmtp_auth_plain(session, user, pass);
809 case MAILSMTP_AUTH_CRAM_MD5:
810 return mailsmtp_auth_cram_md5(session, user, pass);
811 default:
812 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
813 }
814}
815
816
817int mailsmtp_auth(mailsmtp * session, const char * user, const char * pass)
818{
819 if (session->auth == MAILSMTP_AUTH_NOT_CHECKED)
820 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
821
822 if (session->auth & MAILSMTP_AUTH_CRAM_MD5) {
823 return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_CRAM_MD5);
824 } else if (session->auth & MAILSMTP_AUTH_PLAIN) {
825 return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_PLAIN);
826 } else if (session->auth & MAILSMTP_AUTH_LOGIN) {
827 return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_LOGIN);
828 } else {
829 return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED;
830 }
831}
832
833/* TODO: add mailesmtp_etrn, mailssmtp_expn */
834
835int mailesmtp_starttls(mailsmtp * session) {
836 int r;
837
838 if (!(session->esmtp & MAILSMTP_ESMTP_STARTTLS))
839 return MAILSMTP_ERROR_STARTTLS_NOT_SUPPORTED;
840
841 r = send_command(session, "STARTTLS\r\n");
842 if (r == -1)
843 return MAILSMTP_ERROR_STREAM;
844 r = read_response(session);
845
846 switch (r) {
847 case 220:
848 return MAILSMTP_NO_ERROR;
849
850 case 454:
851 return MAILSMTP_ERROR_STARTTLS_TEMPORARY_FAILURE;
852
853 default:
854 return MAILSMTP_ERROR_UNEXPECTED_CODE;
855 }
856}
857
858static int parse_response(mailsmtp * session,
859 char * response)
860{
861 char * message;
862 int code;
863 int cont = 0;
864
865 code = strtol(response, &message, 10);
866 if (* message == ' ')
867 mmap_string_append(session->response_buffer, message + 1);
868 else if (* message == '-') {
869 cont = SMTP_STATUS_CONTINUE;
870 mmap_string_append(session->response_buffer, message + 1);
871 }
872 else
873 mmap_string_append(session->response_buffer, message);
874
875 return code | cont;
876}
877
878static char * read_line(mailsmtp * session)
879{
880 return mailstream_read_line_remove_eol(session->stream,
881 session->line_buffer);
882}
883
884static int read_response(mailsmtp * session)
885{
886 char * line;
887 int code;
888
889 mmap_string_assign(session->response_buffer, "");
890
891 do {
892 line = read_line(session);
893
894 if (line != NULL) {
895 code = parse_response(session, line);
896 mmap_string_append_c(session->response_buffer, '\n');
897 }
898 else
899 code = 0;
900 }
901 while ((code & SMTP_STATUS_CONTINUE) != 0);
902
903 session->response = session->response_buffer->str;
904
905 return code;
906}
907
908
909
910
911
912static int send_command(mailsmtp * f, char * command)
913{
914 ssize_t r;
915
916 r = mailstream_write(f->stream, command, strlen(command));
917 if (r == -1)
918 return -1;
919
920 r = mailstream_flush(f->stream);
921 if (r == -1)
922 return -1;
923
924 return 0;
925}
926
927static int send_data(mailsmtp * session, const char * message, size_t size)
928{
929 if (mailstream_send_data(session->stream, message, size,
930 session->progr_rate, session->progr_fun) == -1)
931 return -1;
932
933 if (mailstream_flush(session->stream) == -1)
934 return -1;
935
936 return 0;
937}
938
939
940const char * mailsmtp_strerror(int errnum)
941{
942 switch (errnum) {
943 case MAILSMTP_NO_ERROR:
944 return "No error";
945 case MAILSMTP_ERROR_UNEXPECTED_CODE:
946 return "Unexpected error code";
947 case MAILSMTP_ERROR_SERVICE_NOT_AVAILABLE:
948 return "Service not available";
949 case MAILSMTP_ERROR_STREAM:
950 return "Stream error";
951 case MAILSMTP_ERROR_HOSTNAME:
952 return "gethostname() failed";
953 case MAILSMTP_ERROR_NOT_IMPLEMENTED:
954 return "Not implemented";
955 case MAILSMTP_ERROR_ACTION_NOT_TAKEN:
956 return "Error, action not taken";
957 case MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION:
958 return "Data exceeds storage allocation";
959 case MAILSMTP_ERROR_IN_PROCESSING:
960 return "Error in processing";
961 case MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE:
962 return "Insufficient system storage";
963 case MAILSMTP_ERROR_MAILBOX_UNAVAILABLE:
964 return "Mailbox unavailable";
965 case MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED:
966 return "Mailbox name not allowed";
967 case MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND:
968 return "Bad command sequence";
969 case MAILSMTP_ERROR_USER_NOT_LOCAL:
970 return "User not local";
971 case MAILSMTP_ERROR_TRANSACTION_FAILED:
972 return "Transaction failed";
973 case MAILSMTP_ERROR_MEMORY:
974 return "Memory error";
975 case MAILSMTP_ERROR_CONNECTION_REFUSED:
976 return "Connection refused";
977 case MAILSMTP_ERROR_STARTTLS_TEMPORARY_FAILURE:
978 return "TLS not available on server for temporary reason";
979 case MAILSMTP_ERROR_STARTTLS_NOT_SUPPORTED:
980 return "TLS not supported by server";
981 default:
982 return "Unknown error code";
983 }
984}