author | zautrix <zautrix> | 2005-03-18 20:17:03 (UTC) |
---|---|---|
committer | zautrix <zautrix> | 2005-03-18 20:17:03 (UTC) |
commit | 9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf (patch) (unidiff) | |
tree | 2528e6cc740225ca0f47d5ac8ff70f7d3bb10621 /libetpan/src/low-level/smtp/mailsmtp.c | |
parent | 9319998f20f03dcc217fbb39656755dc65226276 (diff) | |
download | kdepimpi-9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf.zip kdepimpi-9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf.tar.gz kdepimpi-9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf.tar.bz2 |
Initial revision
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.c | 984 |
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 | |||
75 | mailsmtp * 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 | |||
112 | void 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 | |||
122 | static int send_command(mailsmtp * f, char * command); | ||
123 | |||
124 | static int read_response(mailsmtp * session); | ||
125 | |||
126 | /* smtp operations */ | ||
127 | |||
128 | int 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 | |||
155 | int 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 | |||
177 | int 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 | |||
213 | int 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 | |||
254 | int mailsmtp_rcpt(mailsmtp * session, const char * to) | ||
255 | { | ||
256 | return mailesmtp_rcpt(session, to, 0, NULL); | ||
257 | } | ||
258 | |||
259 | int 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 | |||
288 | static int send_data(mailsmtp * session, const char * message, size_t size); | ||
289 | |||
290 | int 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 | |||
338 | int 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 | |||
400 | int 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 | |||
440 | int 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 | |||
501 | int 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 | |||
576 | int 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 | |||
601 | static 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 | |||
650 | static 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 | |||
677 | err_free: | ||
678 | free(plain64); | ||
679 | free(plain); | ||
680 | |||
681 | err: | ||
682 | return err; | ||
683 | } | ||
684 | |||
685 | static 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 | |||
705 | static 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 | |||
729 | static 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 | |||
763 | err_free: | ||
764 | free(auth); | ||
765 | err_free_auth_hex: | ||
766 | free(auth_hex); | ||
767 | err_free_response: | ||
768 | free(response); | ||
769 | return err; | ||
770 | } | ||
771 | |||
772 | int 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 | |||
817 | int 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 | |||
835 | int 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 | |||
858 | static 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 | |||
878 | static char * read_line(mailsmtp * session) | ||
879 | { | ||
880 | return mailstream_read_line_remove_eol(session->stream, | ||
881 | session->line_buffer); | ||
882 | } | ||
883 | |||
884 | static 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 | |||
912 | static 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 | |||
927 | static 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 | |||
940 | const 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 | } | ||