summaryrefslogtreecommitdiffabout
path: root/kmicromail/libetpan/smtp/mailsmtp.c
Unidiff
Diffstat (limited to 'kmicromail/libetpan/smtp/mailsmtp.c') (more/less context) (ignore whitespace changes)
-rw-r--r--kmicromail/libetpan/smtp/mailsmtp.c982
1 files changed, 982 insertions, 0 deletions
diff --git a/kmicromail/libetpan/smtp/mailsmtp.c b/kmicromail/libetpan/smtp/mailsmtp.c
new file mode 100644
index 0000000..b3be432
--- a/dev/null
+++ b/kmicromail/libetpan/smtp/mailsmtp.c
@@ -0,0 +1,982 @@
1/*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2002 - 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 REGENTS 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 REGENTS 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 return MAILSMTP_NO_ERROR;
196
197 case 504:
198 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
199
200 case 550:
201 return MAILSMTP_ERROR_ACTION_NOT_TAKEN;
202
203 case 0:
204 return MAILSMTP_ERROR_STREAM;
205
206 default:
207 return MAILSMTP_ERROR_UNEXPECTED_CODE;
208 }
209}
210
211int mailsmtp_mail(mailsmtp * session, const char * from)
212{
213 int r;
214 char command[SMTP_STRING_SIZE];
215
216 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s>\r\n", from);
217 r = send_command(session, command);
218 if (r == -1)
219 return MAILSMTP_ERROR_STREAM;
220 r = read_response(session);
221
222 switch (r) {
223 case 250:
224 return MAILSMTP_NO_ERROR;
225
226 case 552:
227 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
228
229 case 451:
230 return MAILSMTP_ERROR_IN_PROCESSING;
231
232 case 452:
233 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
234
235 case 550:
236 return MAILSMTP_ERROR_MAILBOX_UNAVAILABLE;
237
238 case 553:
239 return MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED;
240
241 case 503:
242 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
243
244 case 0:
245 return MAILSMTP_ERROR_STREAM;
246
247 default:
248 return MAILSMTP_ERROR_UNEXPECTED_CODE;
249 }
250}
251
252int mailsmtp_rcpt(mailsmtp * session, const char * to)
253{
254 return mailesmtp_rcpt(session, to, 0, NULL);
255}
256
257int mailsmtp_data(mailsmtp * session)
258{
259 int r;
260 char command[SMTP_STRING_SIZE];
261
262 snprintf(command, SMTP_STRING_SIZE, "DATA\r\n");
263 r = send_command(session, command);
264 if (r == -1)
265 return MAILSMTP_ERROR_STREAM;
266 r = read_response(session);
267
268 switch (r) {
269 case 354:
270 return MAILSMTP_NO_ERROR;
271
272 case 451:
273 return MAILSMTP_ERROR_IN_PROCESSING;
274
275 case 554:
276 return MAILSMTP_ERROR_TRANSACTION_FAILED;
277
278 case 503:
279 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
280
281 default:
282 return MAILSMTP_ERROR_UNEXPECTED_CODE;
283 }
284}
285
286static int send_data(mailsmtp * session, const char * message, size_t size);
287
288int mailsmtp_data_message(mailsmtp * session,
289 const char * message,
290 size_t size)
291{
292 int r;
293
294 r = send_data(session, message, size);
295 if (r == -1)
296 return MAILSMTP_ERROR_STREAM;
297
298 r = read_response(session);
299
300 switch(r) {
301 case 250:
302 return MAILSMTP_NO_ERROR;
303
304 case 552:
305 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
306
307 case 554:
308 return MAILSMTP_ERROR_TRANSACTION_FAILED;
309
310 case 451:
311 return MAILSMTP_ERROR_IN_PROCESSING;
312
313 case 452:
314 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
315
316 case 0:
317 return MAILSMTP_ERROR_STREAM;
318
319 default:
320 return MAILSMTP_ERROR_UNEXPECTED_CODE;
321 }
322}
323
324/* esmtp operations */
325
326
327/**
328 * called during mailesmtp_ehlo
329 * checks EHLO answer for server extensions and sets flags
330 * in session->esmtp
331 * checks AUTH methods in session->response and sets flags
332 * in session->auth
333 */
334#define isdelim(x) ((x) == ' ' || (x) == '\r' || (x) == '\n' || (x) == '\0')
335
336int mailesmtp_parse_ehlo(mailsmtp * session)
337{
338 char * response;
339
340 /* restore data */
341 session->esmtp = MAILSMTP_ESMTP;
342 session->auth = MAILSMTP_AUTH_CHECKED;
343
344 response = session->response;
345
346 /* ESMTP supported extensions :
347 DSN
348 EXPN
349 8BITMIME
350 SIZE [<n>]
351 ETRN
352 STARTTLS
353 AUTH <mechanisms...>
354 */
355 while (response != NULL) {
356 if (!strncasecmp(response, "EXPN", 4) && isdelim(response[4]))
357 session->esmtp |= MAILSMTP_ESMTP_EXPN;
358 else if (!strncasecmp(response, "ETRN", 4) && isdelim(response[4]))
359 session->esmtp |= MAILSMTP_ESMTP_ETRN;
360 else if (!strncasecmp(response, "DSN", 3) && isdelim(response[3]))
361 session->esmtp |= MAILSMTP_ESMTP_DSN;
362 else if (!strncasecmp(response, "8BITMIME", 8) && isdelim(response[8]))
363 session->esmtp |= MAILSMTP_ESMTP_8BITMIME;
364 else if (!strncasecmp(response, "STARTTLS", 8) && isdelim(response[8]))
365 session->esmtp |= MAILSMTP_ESMTP_STARTTLS;
366 else if (!strncasecmp(response, "SIZE", 4) && isdelim(response[4])) {
367 session->esmtp |= MAILSMTP_ESMTP_SIZE;
368 /* TODO: grab optionnal max size */
369 } else if (!strncasecmp(response, "AUTH ", 5)) {
370 response += 5; /* remove "AUTH " */
371 while (response[0] != '\n' && response[0] != '\0') {
372 while (response[0] == ' ') response++;
373 if (strncasecmp(response, "LOGIN", 5) == 0) {
374 session->auth |= MAILSMTP_AUTH_LOGIN;
375 response += 5;
376 } else if (strncasecmp(response, "CRAM-MD5", 8) == 0) {
377 session->auth |= MAILSMTP_AUTH_CRAM_MD5;
378 response += 8;
379 } else if (strncasecmp(response, "PLAIN", 5) == 0) {
380 session->auth |= MAILSMTP_AUTH_PLAIN;
381 response += 5;
382 } else {
383 /* unknown auth method - jump to next word or eol */
384 while (!isdelim(response[0]) || response[0] == '\r')
385 response++;
386 }
387 }
388 }
389 response = strpbrk(response, "\n");
390 if (response != NULL)
391 response++;
392 }
393
394 return MAILSMTP_NO_ERROR;
395}
396
397
398int mailesmtp_ehlo(mailsmtp * session)
399{
400 int r;
401 char hostname[HOSTNAME_SIZE];
402 char command[SMTP_STRING_SIZE];
403
404 r = gethostname(hostname, HOSTNAME_SIZE);
405 if (r != 0)
406 return MAILSMTP_ERROR_HOSTNAME;
407
408 snprintf(command, SMTP_STRING_SIZE, "EHLO %s\r\n", hostname);
409 r = send_command(session, command);
410 if (r == -1)
411 return MAILSMTP_ERROR_STREAM;
412 r = read_response(session);
413
414 switch (r) {
415 case 250:
416 return mailesmtp_parse_ehlo(session);
417
418 case 504:
419 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
420
421 case 550:
422 return MAILSMTP_ERROR_ACTION_NOT_TAKEN;
423
424 case 0:
425 return MAILSMTP_ERROR_STREAM;
426
427 default:
428 return MAILSMTP_ERROR_UNEXPECTED_CODE;
429 }
430}
431
432/*
433 if return_full is TRUE, the entire message is returned on error
434 envid can be NULL
435*/
436
437
438int mailesmtp_mail(mailsmtp * session,
439 const char * from,
440 int return_full,
441 const char * envid)
442{
443 int r;
444 char command[SMTP_STRING_SIZE];
445 char *body = "";
446
447#if notyet
448 /* TODO: figure out a way for the user to explicity enable this or not */
449 if (session->esmtp & MAILSMTP_ESMTP_8BITMIME)
450 body = " BODY=8BITMIME";
451#endif
452
453 if (session->esmtp & MAILSMTP_ESMTP_DSN) {
454 if (envid)
455 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s> RET=%s ENVID=%s%s\r\n",
456 from, return_full ? "FULL" : "HDRS", envid, body);
457 else
458 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s> RET=%s%s\r\n",
459 from, return_full ? "FULL" : "HDRS", body);
460 } else
461 snprintf(command, SMTP_STRING_SIZE, "MAIL FROM:<%s>%s\r\n",
462 from, body);
463
464 r = send_command(session, command);
465 if (r == -1)
466 return MAILSMTP_ERROR_STREAM;
467 r = read_response(session);
468
469 switch (r) {
470 case 250:
471 return MAILSMTP_NO_ERROR;
472
473 case 552:
474 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
475
476 case 451:
477 return MAILSMTP_ERROR_IN_PROCESSING;
478
479 case 452:
480 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
481
482 case 550:
483 return MAILSMTP_ERROR_MAILBOX_UNAVAILABLE;
484
485 case 553:
486 return MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED;
487
488 case 503:
489 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
490
491 case 0:
492 return MAILSMTP_ERROR_STREAM;
493
494 default:
495 return MAILSMTP_ERROR_UNEXPECTED_CODE;
496 }
497}
498
499int mailesmtp_rcpt(mailsmtp * session,
500 const char * to,
501 int notify,
502 const char * orcpt)
503{
504 int r;
505 char command[SMTP_STRING_SIZE];
506 char notify_str[30] = "";
507 char notify_info_str[30] = "";
508
509 if (notify != 0 && session->esmtp & MAILSMTP_ESMTP_DSN) {
510 if (notify & MAILSMTP_DSN_NOTIFY_SUCCESS)
511 strcat(notify_info_str, ",SUCCESS");
512 if (notify & MAILSMTP_DSN_NOTIFY_FAILURE)
513 strcat(notify_info_str, ",FAILURE");
514 if (notify & MAILSMTP_DSN_NOTIFY_DELAY)
515 strcat(notify_info_str, ",DELAY");
516
517 if (notify & MAILSMTP_DSN_NOTIFY_NEVER)
518 strcpy(notify_info_str, ",NEVER");
519
520 notify_info_str[0] = '=';
521
522 strcpy(notify_str, " NOTIFY");
523 strcat(notify_str, notify_info_str);
524 }
525
526 if (orcpt && session->esmtp & MAILSMTP_ESMTP_DSN)
527 snprintf(command, SMTP_STRING_SIZE, "RCPT TO:<%s>%s ORCPT=%s\r\n",
528 to, notify_str, orcpt);
529 else
530 snprintf(command, SMTP_STRING_SIZE, "RCPT TO:<%s>%s\r\n", to, notify_str);
531
532 r = send_command(session, command);
533 if (r == -1)
534 return MAILSMTP_ERROR_STREAM;
535 r = read_response(session);
536
537 switch (r) {
538 case 250:
539 return MAILSMTP_NO_ERROR;
540
541 case 251: /* not local user, will be forwarded */
542 return MAILSMTP_NO_ERROR;
543
544 case 550:
545 case 450:
546 return MAILSMTP_ERROR_MAILBOX_UNAVAILABLE;
547
548 case 551:
549 return MAILSMTP_ERROR_USER_NOT_LOCAL;
550
551 case 552:
552 return MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION;
553
554 case 553:
555 return MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED;
556
557 case 451:
558 return MAILSMTP_ERROR_IN_PROCESSING;
559
560 case 452:
561 return MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE;
562
563 case 503:
564 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
565
566 case 0:
567 return MAILSMTP_ERROR_STREAM;
568
569 default:
570 return MAILSMTP_ERROR_UNEXPECTED_CODE;
571 }
572}
573
574int auth_map_errors(int err)
575{
576 switch (err) {
577 case 235:
578 return MAILSMTP_NO_ERROR; /* AUTH successfull */
579 case 334:
580 return MAILSMTP_NO_ERROR; /* AUTH in progress */
581 case 432:
582 return MAILSMTP_ERROR_AUTH_TRANSITION_NEEDED;
583 case 454:
584 return MAILSMTP_ERROR_AUTH_TEMPORARY_FAILTURE;
585 case 504:
586 return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED;
587 case 530:
588 return MAILSMTP_ERROR_AUTH_REQUIRED;
589 case 534:
590 return MAILSMTP_ERROR_AUTH_TOO_WEAK;
591 case 538:
592 return MAILSMTP_ERROR_AUTH_ENCRYPTION_REQUIRED;
593 default:
594 /* opportunistic approach ;) */
595 return MAILSMTP_NO_ERROR;
596 }
597}
598
599static int mailsmtp_auth_login(mailsmtp * session,
600 const char * user, const char * pass)
601{
602 int err;
603 char command[SMTP_STRING_SIZE];
604 char * user64, * pass64;
605
606 user64 = NULL;
607 pass64 = NULL;
608
609 user64 = encode_base64(user, strlen(user));
610 if (user64 == NULL) {
611 err = MAILSMTP_ERROR_MEMORY;
612 goto err_free;
613 }
614
615 pass64 = encode_base64(pass, strlen(pass));
616 if (pass64 == NULL) {
617 err = MAILSMTP_ERROR_MEMORY;
618 goto err_free;
619 }
620
621 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", user64);
622 err = send_command(session, command);
623 if (err == -1) {
624 err = MAILSMTP_ERROR_STREAM;
625 goto err_free;
626 }
627 err = read_response(session);
628 err = auth_map_errors(err);
629 if (err != MAILSMTP_NO_ERROR)
630 goto err_free;
631
632 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", pass64);
633 err = send_command(session, command);
634 if (err == -1) {
635 err = MAILSMTP_ERROR_STREAM;
636 goto err_free;
637 }
638 err = read_response(session);
639 err = auth_map_errors(err);
640
641 err_free:
642 free(user64);
643 free(pass64);
644
645 return err;
646}
647
648static int mailsmtp_auth_plain(mailsmtp * session,
649 const char * user, const char * pass)
650{
651 int err, len;
652 char command[SMTP_STRING_SIZE];
653 char * plain, * plain64;
654
655 len = strlen(user) + strlen(pass) + 3;
656 plain = (char *) malloc(len);
657 if (plain == NULL) {
658 err = MAILSMTP_ERROR_MEMORY;
659 goto err;
660 }
661
662 snprintf(plain, len, "%c%s%c%s", '\0', user, '\0', pass);
663 plain64 = encode_base64(plain, len - 1);
664
665 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", plain64);
666 err = send_command(session, command);
667 if (err == -1) {
668 err = MAILSMTP_ERROR_STREAM;
669 goto err_free;
670 }
671
672 err = read_response(session);
673 err = auth_map_errors(err);
674
675err_free:
676 free(plain64);
677 free(plain);
678
679 err:
680 return err;
681}
682
683static char * convert_hex(unsigned char *in, int len)
684{
685 static char hex[] = "0123456789abcdef";
686 char * out;
687 int i;
688
689 out = (char *) malloc(len * 2 + 1);
690 if (out == NULL)
691 return NULL;
692
693 for (i = 0; i < len; i++) {
694 out[i * 2] = hex[in[i] >> 4];
695 out[i * 2 + 1] = hex[in[i] & 15];
696 }
697
698 out[i*2] = 0;
699
700 return out;
701}
702
703static char * hash_md5(const char * sec_key, const char * data, int len)
704{
705 char key[65], digest[24];
706 char * hash_hex;
707
708 int sec_len, i;
709
710 sec_len = strlen(sec_key);
711
712 if (sec_len < 64) {
713 memcpy(key, sec_key, sec_len);
714 for (i = sec_len; i < 64; i++) {
715 key[i] = 0;
716 }
717 } else {
718 memcpy(key, sec_key, 64);
719 }
720
721 hmac_md5(data, len, key, 64, digest);
722 hash_hex = convert_hex(digest, 16);
723
724 return hash_hex;
725}
726
727static int mailsmtp_auth_cram_md5(mailsmtp * session,
728 const char * user, const char * pass)
729{
730 int err;
731 char command[SMTP_STRING_SIZE];
732 char *response, *auth_hex, *auth;
733
734 response = decode_base64(session->response, strlen(session->response));
735 if (response == NULL) return MAILSMTP_ERROR_MEMORY;
736
737 auth_hex = hash_md5(pass, response, strlen(response));
738 if (auth_hex == NULL) {
739 err = MAILSMTP_ERROR_MEMORY;
740 goto err_free_response;
741 }
742
743 snprintf(command, SMTP_STRING_SIZE, "%s %s", user, auth_hex);
744
745 auth = encode_base64(command, strlen(command));
746 if (auth == NULL) {
747 err = MAILSMTP_ERROR_MEMORY;
748 goto err_free_auth_hex;
749 }
750
751 snprintf(command, SMTP_STRING_SIZE, "%s\r\n", auth);
752 err = send_command(session, command);
753 if (err == -1) {
754 err = MAILSMTP_ERROR_STREAM;
755 goto err_free;
756 }
757
758 err = read_response(session);
759 err = auth_map_errors(err);
760
761err_free:
762 free(auth);
763err_free_auth_hex:
764 free(auth_hex);
765err_free_response:
766 free(response);
767 return err;
768}
769
770int mailsmtp_auth_type(mailsmtp * session,
771 const char * user, const char * pass, int type)
772{
773 int err;
774 char command[SMTP_STRING_SIZE];
775
776 if (session->auth == MAILSMTP_AUTH_NOT_CHECKED)
777 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
778
779 if ( !(session->auth & type) ) return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED;
780
781 switch (type) {
782 case MAILSMTP_AUTH_LOGIN:
783 snprintf(command, SMTP_STRING_SIZE, "AUTH LOGIN\r\n");
784 break;
785 case MAILSMTP_AUTH_PLAIN:
786 snprintf(command, SMTP_STRING_SIZE, "AUTH PLAIN\r\n");
787 break;
788 case MAILSMTP_AUTH_CRAM_MD5:
789 snprintf(command, SMTP_STRING_SIZE, "AUTH CRAM-MD5\r\n");
790 break;
791 default:
792 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
793 }
794
795 err = send_command(session, command);
796 if (err == -1) return MAILSMTP_ERROR_STREAM;
797
798 err = read_response(session);
799 err = auth_map_errors(err);
800 if (err != MAILSMTP_NO_ERROR) return err;
801
802 switch (type) {
803 case MAILSMTP_AUTH_LOGIN:
804 return mailsmtp_auth_login(session, user, pass);
805 case MAILSMTP_AUTH_PLAIN:
806 return mailsmtp_auth_plain(session, user, pass);
807 case MAILSMTP_AUTH_CRAM_MD5:
808 return mailsmtp_auth_cram_md5(session, user, pass);
809 default:
810 return MAILSMTP_ERROR_NOT_IMPLEMENTED;
811 }
812}
813
814
815int mailsmtp_auth(mailsmtp * session, const char * user, const char * pass)
816{
817 if (session->auth == MAILSMTP_AUTH_NOT_CHECKED)
818 return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND;
819
820 if (session->auth & MAILSMTP_AUTH_CRAM_MD5) {
821 return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_CRAM_MD5);
822 } else if (session->auth & MAILSMTP_AUTH_PLAIN) {
823 return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_PLAIN);
824 } else if (session->auth & MAILSMTP_AUTH_LOGIN) {
825 return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_LOGIN);
826 } else {
827 return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED;
828 }
829}
830
831/* TODO: add mailesmtp_etrn, mailssmtp_expn */
832
833int mailesmtp_starttls(mailsmtp * session) {
834 int r;
835
836 if (!(session->esmtp & MAILSMTP_ESMTP_STARTTLS))
837 return MAILSMTP_ERROR_STARTTLS_NOT_SUPPORTED;
838
839 r = send_command(session, "STARTTLS\r\n");
840 if (r == -1)
841 return MAILSMTP_ERROR_STREAM;
842 r = read_response(session);
843
844 switch (r) {
845 case 220:
846 return MAILSMTP_NO_ERROR;
847
848 case 454:
849 return MAILSMTP_ERROR_STARTTLS_TEMPORARY_FAILURE;
850
851 default:
852 return MAILSMTP_ERROR_UNEXPECTED_CODE;
853 }
854}
855
856static int parse_response(mailsmtp * session,
857 char * response)
858{
859 char * message;
860 int code;
861 int cont = 0;
862
863 code = strtol(response, &message, 10);
864 if (* message == ' ')
865 mmap_string_append(session->response_buffer, message + 1);
866 else if (* message == '-') {
867 cont = SMTP_STATUS_CONTINUE;
868 mmap_string_append(session->response_buffer, message + 1);
869 }
870 else
871 mmap_string_append(session->response_buffer, message);
872
873 return code | cont;
874}
875
876static char * read_line(mailsmtp * session)
877{
878 return mailstream_read_line_remove_eol(session->stream,
879 session->line_buffer);
880}
881
882static int read_response(mailsmtp * session)
883{
884 char * line;
885 int code;
886
887 mmap_string_assign(session->response_buffer, "");
888
889 do {
890 line = read_line(session);
891
892 if (line != NULL) {
893 code = parse_response(session, line);
894 mmap_string_append_c(session->response_buffer, '\n');
895 }
896 else
897 code = 0;
898 }
899 while ((code & SMTP_STATUS_CONTINUE) != 0);
900
901 session->response = session->response_buffer->str;
902
903 return code;
904}
905
906
907
908
909
910static int send_command(mailsmtp * f, char * command)
911{
912 ssize_t r;
913
914 r = mailstream_write(f->stream, command, strlen(command));
915 if (r == -1)
916 return -1;
917
918 r = mailstream_flush(f->stream);
919 if (r == -1)
920 return -1;
921
922 return 0;
923}
924
925static int send_data(mailsmtp * session, const char * message, size_t size)
926{
927 if (mailstream_send_data(session->stream, message, size,
928 session->progr_rate, session->progr_fun) == -1)
929 return -1;
930
931 if (mailstream_flush(session->stream) == -1)
932 return -1;
933
934 return 0;
935}
936
937
938const char * mailsmtp_strerror(int errnum)
939{
940 switch (errnum) {
941 case MAILSMTP_NO_ERROR:
942 return "No error";
943 case MAILSMTP_ERROR_UNEXPECTED_CODE:
944 return "Unexpected error code";
945 case MAILSMTP_ERROR_SERVICE_NOT_AVAILABLE:
946 return "Service not available";
947 case MAILSMTP_ERROR_STREAM:
948 return "Stream error";
949 case MAILSMTP_ERROR_HOSTNAME:
950 return "gethostname() failed";
951 case MAILSMTP_ERROR_NOT_IMPLEMENTED:
952 return "Not implemented";
953 case MAILSMTP_ERROR_ACTION_NOT_TAKEN:
954 return "Error, action not taken";
955 case MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION:
956 return "Data exceeds storage allocation";
957 case MAILSMTP_ERROR_IN_PROCESSING:
958 return "Error in processing";
959 case MAILSMTP_ERROR_INSUFFICIENT_SYSTEM_STORAGE:
960 return "Insufficient system storage";
961 case MAILSMTP_ERROR_MAILBOX_UNAVAILABLE:
962 return "Mailbox unavailable";
963 case MAILSMTP_ERROR_MAILBOX_NAME_NOT_ALLOWED:
964 return "Mailbox name not allowed";
965 case MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND:
966 return "Bad command sequence";
967 case MAILSMTP_ERROR_USER_NOT_LOCAL:
968 return "User not local";
969 case MAILSMTP_ERROR_TRANSACTION_FAILED:
970 return "Transaction failed";
971 case MAILSMTP_ERROR_MEMORY:
972 return "Memory error";
973 case MAILSMTP_ERROR_CONNECTION_REFUSED:
974 return "Connection refused";
975 case MAILSMTP_ERROR_STARTTLS_TEMPORARY_FAILURE:
976 return "TLS not available on server for temporary reason";
977 case MAILSMTP_ERROR_STARTTLS_NOT_SUPPORTED:
978 return "TLS not supported by server";
979 default:
980 return "Unknown error code";
981 }
982}