2 This file is part of libquickmail.
4 libquickmail is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 libquickmail is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with libquickmail. If not, see <http://www.gnu.org/licenses/>.
18 #if defined(_WIN32) && defined(DLL_EXPORT) && !defined(BUILD_QUICKMAIL_DLL)
19 #define BUILD_QUICKMAIL_DLL
21 #include "quickmail.h"
30 #define snprintf _snprintf
31 #define strdup _strdup
34 #if (defined(STATIC) || defined(BUILD_QUICKMAIL_STATIC)) && !defined(CURL_STATICLIB)
35 #define CURL_STATICLIB
37 #include <curl/curl.h>
39 #include "smtpsocket.h"
42 #define LIBQUICKMAIL_VERSION_MAJOR 0
43 #define LIBQUICKMAIL_VERSION_MINOR 1
44 #define LIBQUICKMAIL_VERSION_MICRO 18
46 #define VERSION_STRINGIZE_(major, minor, micro) #major"."#minor"."#micro
47 #define VERSION_STRINGIZE(major, minor, micro) VERSION_STRINGIZE_(major, minor, micro)
49 #define LIBQUICKMAIL_VERSION VERSION_STRINGIZE(LIBQUICKMAIL_VERSION_MAJOR,LIBQUICKMAIL_VERSION_MINOR,LIBQUICKMAIL_VERSION_MICRO)
51 #define NEWLINE "\r\n"
52 #define NEWLINELENGTH 2
53 //#define NEWLINE "\n"
54 //#define NEWLINELENGTH 1
56 #define MIME_LINE_WIDTH 72
57 #define BODY_BUFFER_SIZE 256
59 //definitions of the differen stages of generating the message data
60 #define MAILPART_INITIALIZE 0
61 #define MAILPART_HEADER 1
62 #define MAILPART_BODY 2
63 #define MAILPART_BODY_DONE 3
64 #define MAILPART_ATTACHMENT 4
65 #define MAILPART_END 5
66 #define MAILPART_DONE 6
68 static const char* default_mime_type = "text/plain";
70 ////////////////////////////////////////////////////////////////////////
72 #define DEBUG_ERROR(errmsg)
73 static const char* ERRMSG_MEMORY_ALLOCATION_ERROR = "Memory allocation error";
75 ////////////////////////////////////////////////////////////////////////
77 char* randomize_zeros (char* data)
79 //replace all 0s with random digits
83 *p = '0' + rand() % 10;
89 char* str_append (char** data, const char* newdata)
91 //append a string to the end of an existing string
93 int len = (*data ? strlen(*data) : 0);
94 if ((p = (char*)realloc(*data, len + strlen(newdata) + 1)) == NULL) {
96 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
100 strcpy(*data + len, newdata);
104 ////////////////////////////////////////////////////////////////////////
106 struct email_info_struct {
107 int current; //must be zet to 0
110 struct email_info_email_list_struct* to;
111 struct email_info_email_list_struct* cc;
112 struct email_info_email_list_struct* bcc;
115 struct email_info_attachment_list_struct* bodylist;
116 struct email_info_attachment_list_struct* attachmentlist;
119 char* mime_boundary_body;
120 char* mime_boundary_part;
121 struct email_info_attachment_list_struct* current_attachment;
126 ////////////////////////////////////////////////////////////////////////
128 struct email_info_email_list_struct {
130 struct email_info_email_list_struct* next;
133 void email_info_string_list_add (struct email_info_email_list_struct** list, const char* data)
135 struct email_info_email_list_struct** p = list;
138 if ((*p = (struct email_info_email_list_struct*)malloc(sizeof(struct email_info_email_list_struct))) == NULL) {
139 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
142 (*p)->data = (data ? strdup(data) : NULL);
146 void email_info_string_list_free (struct email_info_email_list_struct** list)
148 struct email_info_email_list_struct* p = *list;
149 struct email_info_email_list_struct* current;
159 char* email_info_string_list_concatenate (struct email_info_email_list_struct* list)
162 struct email_info_email_list_struct* listentry = list;
164 if (listentry->data && *listentry->data) {
166 str_append(&result, "," NEWLINE "\t");
167 str_append(&result, "<");
168 str_append(&result, listentry->data);
169 str_append(&result, ">");
171 listentry = listentry->next;
176 ////////////////////////////////////////////////////////////////////////
178 struct email_info_attachment_list_struct {
183 quickmail_attachment_open_fn email_info_attachment_open;
184 quickmail_attachment_read_fn email_info_attachment_read;
185 quickmail_attachment_close_fn email_info_attachment_close;
186 quickmail_attachment_free_filedata_fn email_info_attachment_filedata_free;
187 struct email_info_attachment_list_struct* next;
190 struct email_info_attachment_list_struct* email_info_attachment_list_add (struct email_info_attachment_list_struct** list, const char* filename, const char* mimetype, void* filedata, quickmail_attachment_open_fn email_info_attachment_open, quickmail_attachment_read_fn email_info_attachment_read, quickmail_attachment_close_fn email_info_attachment_close, quickmail_attachment_free_filedata_fn email_info_attachment_filedata_free)
192 struct email_info_attachment_list_struct** p = list;
195 if ((*p = (struct email_info_attachment_list_struct*)malloc(sizeof(struct email_info_attachment_list_struct))) == NULL) {
196 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
199 (*p)->filename = strdup(filename ? filename : "UNNAMED");
200 (*p)->mimetype = (mimetype ? strdup(mimetype) : NULL);
201 (*p)->filedata = filedata;
203 (*p)->email_info_attachment_open = email_info_attachment_open;
204 (*p)->email_info_attachment_read = email_info_attachment_read;
205 (*p)->email_info_attachment_close = email_info_attachment_close;
206 (*p)->email_info_attachment_filedata_free = email_info_attachment_filedata_free;
211 void email_info_attachment_list_free_entry (struct email_info_attachment_list_struct* current)
213 if (current->handle) {
214 if (current->email_info_attachment_close)
215 current->email_info_attachment_close(current->handle);
217 // free(current->handle);
218 current->handle = NULL;
220 if (current->filedata) {
221 if (current->email_info_attachment_filedata_free)
222 current->email_info_attachment_filedata_free(current->filedata);
224 free(current->filedata);
226 if (current->mimetype)
227 free(current->mimetype);
228 free(current->filename);
232 void email_info_attachment_list_free (struct email_info_attachment_list_struct** list)
234 struct email_info_attachment_list_struct* p = *list;
235 struct email_info_attachment_list_struct* current;
239 email_info_attachment_list_free_entry(current);
244 int email_info_attachment_list_delete (struct email_info_attachment_list_struct** list, const char* filename)
246 struct email_info_attachment_list_struct** p = list;
248 if (strcmp((*p)->filename, filename) == 0) {
249 struct email_info_attachment_list_struct* current = *p;
251 email_info_attachment_list_free_entry(current);
259 void email_info_attachment_list_close_handles (struct email_info_attachment_list_struct* list)
261 struct email_info_attachment_list_struct* p = list;
264 if (p->email_info_attachment_close)
265 p->email_info_attachment_close(p->handle);
274 //dummy attachment functions
276 void* email_info_attachment_open_dummy (void* filedata)
278 return &email_info_attachment_open_dummy;
281 size_t email_info_attachment_read_dummy (void* handle, void* buf, size_t len)
286 struct email_info_attachment_list_struct* email_info_attachment_list_add_dummy (struct email_info_attachment_list_struct** list, const char* filename, const char* mimetype)
288 return email_info_attachment_list_add(list, filename, mimetype, NULL, email_info_attachment_open_dummy, email_info_attachment_read_dummy, NULL, NULL);
291 //file attachment functions
293 void* email_info_attachment_open_file (void* filedata)
295 return (void*)fopen((char*)filedata, "rb");
298 size_t email_info_attachment_read_file (void* handle, void* buf, size_t len)
300 return fread(buf, 1, len, (FILE*)handle);
303 void email_info_attachment_close_file (void* handle)
306 fclose((FILE*)handle);
309 struct email_info_attachment_list_struct* email_info_attachment_list_add_file (struct email_info_attachment_list_struct** list, const char* path, const char* mimetype)
311 //determine base filename
312 const char* basename = path + strlen(path);
313 while (basename != path) {
317 || *basename == '\\' || *basename == ':'
324 return email_info_attachment_list_add(list, basename, mimetype, (void*)strdup(path), email_info_attachment_open_file, email_info_attachment_read_file, email_info_attachment_close_file, NULL);
327 //memory attachment functions
329 struct email_info_attachment_memory_filedata_struct {
335 struct email_info_attachment_memory_handle_struct {
341 void* email_info_attachment_open_memory (void* filedata)
343 struct email_info_attachment_memory_filedata_struct* data;
344 struct email_info_attachment_memory_handle_struct* result;
345 data = ((struct email_info_attachment_memory_filedata_struct*)filedata);
348 if ((result = (struct email_info_attachment_memory_handle_struct*)malloc(sizeof(struct email_info_attachment_memory_handle_struct))) == NULL) {
349 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
352 result->data = data->data;
353 result->datalen = data->datalen;
358 size_t email_info_attachment_read_memory (void* handle, void* buf, size_t len)
360 struct email_info_attachment_memory_handle_struct* h = (struct email_info_attachment_memory_handle_struct*)handle;
361 size_t n = (h->pos + len <= h->datalen ? len : h->datalen - h->pos);
362 memcpy(buf, h->data + h->pos, n);
367 void email_info_attachment_close_memory (void* handle)
373 void email_info_attachment_filedata_free_memory (void* filedata)
375 struct email_info_attachment_memory_filedata_struct* data = ((struct email_info_attachment_memory_filedata_struct*)filedata);
383 struct email_info_attachment_list_struct* email_info_attachment_list_add_memory (struct email_info_attachment_list_struct** list, const char* filename, const char* mimetype, char* data, size_t datalen, int mustfree)
385 struct email_info_attachment_memory_filedata_struct* filedata;
386 if ((filedata = (struct email_info_attachment_memory_filedata_struct*)malloc(sizeof(struct email_info_attachment_memory_filedata_struct))) == NULL) {
387 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
390 filedata->data = data;
391 filedata->datalen = datalen;
392 filedata->mustfree = mustfree;
393 return email_info_attachment_list_add(list, filename, mimetype, filedata, email_info_attachment_open_memory, email_info_attachment_read_memory, email_info_attachment_close_memory, email_info_attachment_filedata_free_memory);
396 ////////////////////////////////////////////////////////////////////////
398 DLL_EXPORT_LIBQUICKMAIL const char* quickmail_get_version ()
400 return VERSION_STRINGIZE(LIBQUICKMAIL_VERSION_MAJOR, LIBQUICKMAIL_VERSION_MINOR, LIBQUICKMAIL_VERSION_MICRO)
407 DLL_EXPORT_LIBQUICKMAIL int quickmail_initialize ()
409 #if defined(NOCURL) && defined(_WIN32)
410 static WSADATA wsaData;
411 int wsaerr = WSAStartup(MAKEWORD(1, 0), &wsaData);
414 atexit((void(*)())WSACleanup);
419 DLL_EXPORT_LIBQUICKMAIL quickmail quickmail_create (const char* from, const char* subject)
422 struct email_info_struct* mailobj;
423 if ((mailobj = (struct email_info_struct*)malloc(sizeof(struct email_info_struct))) == NULL) {
424 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
427 mailobj->current = 0;
428 mailobj->timestamp = time(NULL);
429 mailobj->from = (from ? strdup(from) : NULL);
433 mailobj->subject = (subject ? strdup(subject) : NULL);
434 mailobj->header = NULL;
435 mailobj->bodylist = NULL;
436 mailobj->attachmentlist = NULL;
439 mailobj->mime_boundary_body = NULL;
440 mailobj->mime_boundary_part = NULL;
441 mailobj->current_attachment = NULL;
442 mailobj->debuglog = NULL;
443 for (i = 0; i < 26; i++) {
444 mailobj->dtable[i] = (char)('A' + i);
445 mailobj->dtable[26 + i] = (char)('a' + i);
447 for (i = 0; i < 10; i++) {
448 mailobj->dtable[52 + i] = (char)('0' + i);
450 mailobj->dtable[62] = '+';
451 mailobj->dtable[63] = '/';
456 DLL_EXPORT_LIBQUICKMAIL void quickmail_destroy (quickmail mailobj)
459 email_info_string_list_free(&mailobj->to);
460 email_info_string_list_free(&mailobj->cc);
461 email_info_string_list_free(&mailobj->bcc);
462 free(mailobj->subject);
463 free(mailobj->header);
464 email_info_attachment_list_free(&mailobj->bodylist);
465 email_info_attachment_list_free(&mailobj->attachmentlist);
467 free(mailobj->mime_boundary_body);
468 free(mailobj->mime_boundary_part);
472 DLL_EXPORT_LIBQUICKMAIL void quickmail_set_from (quickmail mailobj, const char* from)
475 mailobj->from = strdup(from);
478 DLL_EXPORT_LIBQUICKMAIL const char* quickmail_get_from (quickmail mailobj)
480 return mailobj->from;
483 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_to (quickmail mailobj, const char* email)
485 email_info_string_list_add(&mailobj->to, email);
488 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_cc (quickmail mailobj, const char* email)
490 email_info_string_list_add(&mailobj->cc, email);
493 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_bcc (quickmail mailobj, const char* email)
495 email_info_string_list_add(&mailobj->bcc, email);
498 DLL_EXPORT_LIBQUICKMAIL void quickmail_set_subject (quickmail mailobj, const char* subject)
500 free(mailobj->subject);
501 mailobj->subject = (subject ? strdup(subject) : NULL);
504 DLL_EXPORT_LIBQUICKMAIL const char* quickmail_get_subject (quickmail mailobj)
506 return mailobj->subject;
509 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_header (quickmail mailobj, const char* headerline)
511 str_append(&mailobj->header, headerline);
512 str_append(&mailobj->header, NEWLINE);
515 DLL_EXPORT_LIBQUICKMAIL void quickmail_set_body (quickmail mailobj, const char* body)
517 email_info_attachment_list_free(&mailobj->bodylist);
519 email_info_attachment_list_add_memory(&mailobj->bodylist, default_mime_type, default_mime_type, strdup(body), strlen(body), 1);
522 DLL_EXPORT_LIBQUICKMAIL char* quickmail_get_body (quickmail mailobj)
527 size_t resultlen = 0;
528 if (mailobj->bodylist && (mailobj->bodylist->handle = mailobj->bodylist->email_info_attachment_open(mailobj->bodylist->filedata)) != NULL) {
530 if ((p = (char*)realloc(result, resultlen + BODY_BUFFER_SIZE)) == NULL) {
532 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
536 if ((n = mailobj->bodylist->email_info_attachment_read(mailobj->bodylist->handle, result + resultlen, BODY_BUFFER_SIZE)) > 0)
539 if (mailobj->bodylist->email_info_attachment_close)
540 mailobj->bodylist->email_info_attachment_close(mailobj->bodylist->handle);
542 // free(mailobj->bodylist->handle);
543 mailobj->bodylist->handle = NULL;
548 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_body_file (quickmail mailobj, const char* mimetype, const char* path)
550 email_info_attachment_list_add(&mailobj->bodylist, (mimetype ? mimetype : default_mime_type), (mimetype ? mimetype : default_mime_type), (void*)strdup(path), email_info_attachment_open_file, email_info_attachment_read_file, email_info_attachment_close_file, NULL);
553 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_body_memory (quickmail mailobj, const char* mimetype, char* data, size_t datalen, int mustfree)
555 email_info_attachment_list_add_memory(&mailobj->bodylist, (mimetype ? mimetype : default_mime_type), (mimetype ? mimetype : default_mime_type), data, datalen, mustfree);
558 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_body_custom (quickmail mailobj, const char* mimetype, char* data, quickmail_attachment_open_fn attachment_data_open, quickmail_attachment_read_fn attachment_data_read, quickmail_attachment_close_fn attachment_data_close, quickmail_attachment_free_filedata_fn attachment_data_filedata_free)
560 email_info_attachment_list_add(&mailobj->bodylist, (mimetype ? mimetype : default_mime_type), (mimetype ? mimetype : default_mime_type), data, (attachment_data_open ? attachment_data_open : email_info_attachment_open_dummy), (attachment_data_read ? attachment_data_read : email_info_attachment_read_dummy), attachment_data_close, attachment_data_filedata_free);
563 DLL_EXPORT_LIBQUICKMAIL int quickmail_remove_body (quickmail mailobj, const char* mimetype)
565 return email_info_attachment_list_delete(&mailobj->bodylist, mimetype);
568 DLL_EXPORT_LIBQUICKMAIL void quickmail_list_bodies (quickmail mailobj, quickmail_list_attachment_callback_fn callback, void* callbackdata)
570 struct email_info_attachment_list_struct* p = mailobj->bodylist;
572 callback(mailobj, p->filename, p->mimetype, p->email_info_attachment_open, p->email_info_attachment_read, p->email_info_attachment_close, callbackdata);
577 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_attachment_file (quickmail mailobj, const char* path, const char* mimetype)
579 email_info_attachment_list_add_file(&mailobj->attachmentlist, path, mimetype);
582 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_attachment_memory (quickmail mailobj, const char* filename, const char* mimetype, char* data, size_t datalen, int mustfree)
584 email_info_attachment_list_add_memory(&mailobj->attachmentlist, filename, mimetype, data, datalen, mustfree);
587 DLL_EXPORT_LIBQUICKMAIL void quickmail_add_attachment_custom (quickmail mailobj, const char* filename, const char* mimetype, char* data, quickmail_attachment_open_fn attachment_data_open, quickmail_attachment_read_fn attachment_data_read, quickmail_attachment_close_fn attachment_data_close, quickmail_attachment_free_filedata_fn attachment_data_filedata_free)
589 email_info_attachment_list_add(&mailobj->attachmentlist, filename, mimetype, data, (attachment_data_open ? attachment_data_open : email_info_attachment_open_dummy), (attachment_data_read ? attachment_data_read : email_info_attachment_read_dummy), attachment_data_close, attachment_data_filedata_free);
592 DLL_EXPORT_LIBQUICKMAIL int quickmail_remove_attachment (quickmail mailobj, const char* filename)
594 return email_info_attachment_list_delete(&mailobj->attachmentlist, filename);
597 DLL_EXPORT_LIBQUICKMAIL void quickmail_list_attachments (quickmail mailobj, quickmail_list_attachment_callback_fn callback, void* callbackdata)
599 struct email_info_attachment_list_struct* p = mailobj->attachmentlist;
601 callback(mailobj, p->filename, p->mimetype, p->email_info_attachment_open, p->email_info_attachment_read, p->email_info_attachment_close, callbackdata);
606 DLL_EXPORT_LIBQUICKMAIL void quickmail_set_debug_log (quickmail mailobj, FILE* filehandle)
608 mailobj->debuglog = filehandle;
611 DLL_EXPORT_LIBQUICKMAIL void quickmail_fsave (quickmail mailobj, FILE* filehandle)
616 while ((n = quickmail_get_data(buf, sizeof(buf), 1, mailobj)) > 0) {
617 for (i = 0; i < n; i++)
618 fprintf(filehandle, "%c", buf[i]);
622 DLL_EXPORT_LIBQUICKMAIL size_t quickmail_get_data (void* ptr, size_t size, size_t nmemb, void* userp)
624 struct email_info_struct* mailobj = (struct email_info_struct*)userp;
626 //abort if no data is requested
627 if (size * nmemb == 0)
630 //initialize on first run
631 if (mailobj->current == MAILPART_INITIALIZE) {
635 free(mailobj->mime_boundary_body);
636 mailobj->mime_boundary_body = NULL;
637 free(mailobj->mime_boundary_part);
638 mailobj->mime_boundary_part = NULL;
639 mailobj->current_attachment = mailobj->bodylist;
643 //process current part of mail if no partial data is pending
644 while (mailobj->buflen == 0) {
645 if (mailobj->buflen == 0 && mailobj->current == MAILPART_HEADER) {
647 //generate header part
648 char** p = &mailobj->buf;
650 str_append(p, "User-Agent: libquickmail v" LIBQUICKMAIL_VERSION NEWLINE);
651 if (mailobj->timestamp != 0) {
652 char timestamptext[32];
653 if (strftime(timestamptext, sizeof(timestamptext), "%a, %d %b %Y %H:%M:%S %z", localtime(&mailobj->timestamp))) {
\r
654 str_append(p, "Date: ");
655 str_append(p, timestamptext);
656 str_append(p, NEWLINE);
659 //fallback method for Windows when %z (time zone offset) fails
660 else if (strftime(timestamptext, sizeof(timestamptext), "%a, %d %b %Y %H:%M:%S", localtime(&mailobj->timestamp))) {
\r
661 TIME_ZONE_INFORMATION tzinfo;
\r
662 if (GetTimeZoneInformation(&tzinfo) != TIME_ZONE_ID_INVALID)
\r
663 sprintf(timestamptext + strlen(timestamptext), " %c%02i%02i", (tzinfo.Bias > 0 ? '-' : '+'), (int)-tzinfo.Bias / 60, (int)-tzinfo.Bias % 60);
\r
664 str_append(p, "Date: ");
665 str_append(p, timestamptext);
666 str_append(p, NEWLINE);
670 if (mailobj->from && *mailobj->from) {
671 str_append(p, "From: <");
672 str_append(p, mailobj->from);
673 str_append(p, ">" NEWLINE);
675 if ((s = email_info_string_list_concatenate(mailobj->to)) != NULL) {
676 str_append(p, "To: ");
678 str_append(p, NEWLINE);
681 if ((s = email_info_string_list_concatenate(mailobj->cc)) != NULL) {
682 str_append(p, "Cc: ");
684 str_append(p, NEWLINE);
687 if (mailobj->subject) {
688 str_append(p, "Subject: ");
689 str_append(p, mailobj->subject);
690 str_append(p, NEWLINE);
692 if (mailobj->header) {
693 str_append(p, mailobj->header);
695 if (mailobj->attachmentlist) {
696 str_append(p, "MIME-Version: 1.0" NEWLINE);
698 if (mailobj->attachmentlist) {
699 mailobj->mime_boundary_part = randomize_zeros(strdup("=PART=SEPARATOR=_0000_0000_0000_0000_0000_0000_="));
700 str_append(p, "Content-Type: multipart/mixed; boundary=\"");
701 str_append(p, mailobj->mime_boundary_part);
702 str_append(p, "\"" NEWLINE NEWLINE "This is a multipart message in MIME format." NEWLINE NEWLINE "--");
703 str_append(p, mailobj->mime_boundary_part);
704 str_append(p, NEWLINE);
706 if (mailobj->bodylist && mailobj->bodylist->next) {
707 mailobj->mime_boundary_body = randomize_zeros(strdup("=BODY=SEPARATOR=_0000_0000_0000_0000_0000_0000_="));
708 str_append(p, "Content-Type: multipart/alternative; boundary=\"");
709 str_append(p, mailobj->mime_boundary_body);
710 str_append(p, NEWLINE);
712 mailobj->buflen = strlen(mailobj->buf);
715 if (mailobj->buflen == 0 && mailobj->current == MAILPART_BODY) {
716 if (mailobj->current_attachment) {
717 if (!mailobj->current_attachment->handle) {
718 //open file with body data
719 while (mailobj->current_attachment) {
720 if ((mailobj->current_attachment->handle = mailobj->current_attachment->email_info_attachment_open(mailobj->current_attachment->filedata)) != NULL) {
723 mailobj->current_attachment = mailobj->current_attachment->next;
725 if (!mailobj->current_attachment) {
726 mailobj->current_attachment = mailobj->attachmentlist;
729 //generate attachment header
730 if (mailobj->current_attachment && mailobj->current_attachment->handle) {
732 if (mailobj->mime_boundary_body) {
733 mailobj->buf = str_append(&mailobj->buf, NEWLINE "--");
734 mailobj->buf = str_append(&mailobj->buf, mailobj->mime_boundary_body);
735 mailobj->buf = str_append(&mailobj->buf, NEWLINE);
737 mailobj->buf = str_append(&mailobj->buf, "Content-Type: ");
738 mailobj->buf = str_append(&mailobj->buf, (mailobj->bodylist && mailobj->current_attachment->filename ? mailobj->current_attachment->filename : default_mime_type));
739 mailobj->buf = str_append(&mailobj->buf, NEWLINE "Content-Transfer-Encoding: 8bit" NEWLINE "Content-Disposition: inline" NEWLINE NEWLINE);
740 mailobj->buflen = strlen(mailobj->buf);
743 if (mailobj->buflen == 0 && mailobj->current_attachment && mailobj->current_attachment->handle) {
745 if ((mailobj->buf = malloc(BODY_BUFFER_SIZE)) == NULL) {
746 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
748 if (mailobj->buf == NULL || (mailobj->buflen = mailobj->current_attachment->email_info_attachment_read(mailobj->current_attachment->handle, mailobj->buf, BODY_BUFFER_SIZE)) <= 0) {
752 if (mailobj->current_attachment->email_info_attachment_close)
753 mailobj->current_attachment->email_info_attachment_close(mailobj->current_attachment->handle);
755 // free(mailobj->current_attachment->handle);
756 mailobj->current_attachment->handle = NULL;
757 mailobj->current_attachment = mailobj->current_attachment->next;
761 mailobj->current_attachment = mailobj->attachmentlist;
765 if (mailobj->buflen == 0 && mailobj->current == MAILPART_BODY_DONE) {
767 if (mailobj->mime_boundary_body) {
768 mailobj->buf = str_append(&mailobj->buf, NEWLINE "--");
769 mailobj->buf = str_append(&mailobj->buf, mailobj->mime_boundary_body);
770 mailobj->buf = str_append(&mailobj->buf, "--" NEWLINE);
771 mailobj->buflen = strlen(mailobj->buf);
772 free(mailobj->mime_boundary_body);
773 mailobj->mime_boundary_body = NULL;
777 if (mailobj->buflen == 0 && mailobj->current == MAILPART_ATTACHMENT) {
778 if (mailobj->current_attachment) {
779 if (!mailobj->current_attachment->handle) {
780 //open file to attach
781 while (mailobj->current_attachment) {
782 if ((mailobj->current_attachment->handle = mailobj->current_attachment->email_info_attachment_open(mailobj->current_attachment->filedata)) != NULL) {
785 mailobj->current_attachment = mailobj->current_attachment->next;
787 //generate attachment header
788 if (mailobj->current_attachment && mailobj->current_attachment->handle) {
790 if (mailobj->mime_boundary_part) {
791 mailobj->buf = str_append(&mailobj->buf, NEWLINE "--");
792 mailobj->buf = str_append(&mailobj->buf, mailobj->mime_boundary_part);
793 mailobj->buf = str_append(&mailobj->buf, NEWLINE);
795 mailobj->buf = str_append(&mailobj->buf, "Content-Type: ");
796 mailobj->buf = str_append(&mailobj->buf, (mailobj->current_attachment->mimetype ? mailobj->current_attachment->mimetype : "application/octet-stream"));
797 mailobj->buf = str_append(&mailobj->buf, "; Name=\"");
798 mailobj->buf = str_append(&mailobj->buf, mailobj->current_attachment->filename);
799 mailobj->buf = str_append(&mailobj->buf, "\"" NEWLINE "Content-Transfer-Encoding: base64" NEWLINE NEWLINE);
800 mailobj->buflen = strlen(mailobj->buf);
803 //generate next line of attachment data
806 unsigned char igroup[3] = {0, 0, 0};
807 unsigned char ogroup[4];
809 if ((mailobj->buf = (char*)malloc(MIME_LINE_WIDTH + NEWLINELENGTH + 1)) == NULL) {
810 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
813 while (mimelinepos < MIME_LINE_WIDTH && (n = mailobj->current_attachment->email_info_attachment_read(mailobj->current_attachment->handle, igroup, 3)) > 0) {
815 ogroup[0] = mailobj->dtable[igroup[0] >> 2];
816 ogroup[1] = mailobj->dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
817 ogroup[2] = mailobj->dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
818 ogroup[3] = mailobj->dtable[igroup[2] & 0x3F];
819 //padd with "=" characters if less than 3 characters were read
825 memcpy(mailobj->buf + mimelinepos, ogroup, 4);
826 mailobj->buflen += 4;
829 if (mimelinepos > 0) {
830 memcpy(mailobj->buf + mimelinepos, NEWLINE, NEWLINELENGTH);
831 mailobj->buflen += NEWLINELENGTH;
836 if (mailobj->current_attachment->email_info_attachment_close)
837 mailobj->current_attachment->email_info_attachment_close(mailobj->current_attachment->handle);
839 free(mailobj->current_attachment->handle);
840 mailobj->current_attachment->handle = NULL;
841 mailobj->current_attachment = mailobj->current_attachment->next;
848 if (mailobj->buflen == 0 && mailobj->current == MAILPART_END) {
851 if (mailobj->mime_boundary_part) {
852 mailobj->buf = str_append(&mailobj->buf, NEWLINE "--");
853 mailobj->buf = str_append(&mailobj->buf, mailobj->mime_boundary_part);
854 mailobj->buf = str_append(&mailobj->buf, "--" NEWLINE);
855 mailobj->buflen = strlen(mailobj->buf);
856 free(mailobj->mime_boundary_part);
857 mailobj->mime_boundary_part = NULL;
859 //mailobj->buf = str_append(&mailobj->buf, NEWLINE "." NEWLINE);
860 //mailobj->buflen = strlen(mailobj->buf);
863 if (mailobj->buflen == 0 && mailobj->current == MAILPART_DONE) {
868 //flush pending data if any
869 if (mailobj->buflen > 0) {
870 int len = (mailobj->buflen > size * nmemb ? size * nmemb : mailobj->buflen);
871 memcpy(ptr, mailobj->buf, len);
872 if (len < mailobj->buflen) {
873 mailobj->buf = memmove(mailobj->buf, mailobj->buf + len, mailobj->buflen - len);
874 mailobj->buflen -= len;
883 //if (mailobj->current != MAILPART_DONE)
884 // ;//this should never be reached
885 mailobj->current = 0;
890 char* add_angle_brackets (const char* data)
892 size_t datalen = strlen(data);
894 if ((result = (char*)malloc(datalen + 3)) == NULL) {
895 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
899 memcpy(result + 1, data, datalen);
900 result[datalen + 1] = '>';
901 result[datalen + 2] = 0;
906 DLL_EXPORT_LIBQUICKMAIL const char* quickmail_send (quickmail mailobj, const char* smtpserver, unsigned int smtpport, const char* username, const char* password)
909 //libcurl based sending
911 CURLcode result = CURLE_FAILED_INIT;
912 //curl_global_init(CURL_GLOBAL_ALL);
913 if ((curl = curl_easy_init()) != NULL) {
914 struct curl_slist *recipients = NULL;
915 struct email_info_email_list_struct* listentry;
916 //set destination URL
918 size_t len = strlen(smtpserver) + 14;
919 if ((addr = (char*)malloc(len)) == NULL) {
920 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
921 return ERRMSG_MEMORY_ALLOCATION_ERROR;
923 snprintf(addr, len, "smtp://%s:%u", smtpserver, smtpport);
924 curl_easy_setopt(curl, CURLOPT_URL, addr);
926 //try Transport Layer Security (TLS), but continue anyway if it fails
927 curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
928 //don't fail if the TLS/SSL a certificate could not be verified
929 //alternative: add the issuer certificate (or the host certificate if
930 //the certificate is self-signed) to the set of certificates that are
931 //known to libcurl using CURLOPT_CAINFO and/or CURLOPT_CAPATH
932 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
933 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
934 //set authentication credentials if provided
935 if (username && *username)
936 curl_easy_setopt(curl, CURLOPT_USERNAME, username);
938 curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
939 //set from value for envelope reverse-path
940 if (mailobj->from && *mailobj->from) {
941 addr = add_angle_brackets(mailobj->from);
942 curl_easy_setopt(curl, CURLOPT_MAIL_FROM, addr);
946 listentry = mailobj->to;
948 if (listentry->data && *listentry->data) {
949 addr = add_angle_brackets(listentry->data);
950 recipients = curl_slist_append(recipients, addr);
953 listentry = listentry->next;
955 listentry = mailobj->cc;
957 if (listentry->data && *listentry->data) {
958 addr = add_angle_brackets(listentry->data);
959 recipients = curl_slist_append(recipients, addr);
962 listentry = listentry->next;
964 listentry = mailobj->bcc;
966 if (listentry->data && *listentry->data) {
967 addr = add_angle_brackets(listentry->data);
968 recipients = curl_slist_append(recipients, addr);
971 listentry = listentry->next;
973 curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
974 //set callback function for getting message body
975 curl_easy_setopt(curl, CURLOPT_READFUNCTION, quickmail_get_data);
976 curl_easy_setopt(curl, CURLOPT_READDATA, mailobj);
977 //enable debugging if requested
978 if (mailobj->debuglog) {
979 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
980 curl_easy_setopt(curl, CURLOPT_STDERR, mailobj->debuglog);
983 result = curl_easy_perform(curl);
984 //free the list of recipients and clean up
985 curl_slist_free_all(recipients);
986 curl_easy_cleanup(curl);
988 return (result == CURLE_OK ? NULL : curl_easy_strerror(result));
990 //minimal implementation without libcurl
993 struct email_info_email_list_struct* listentry;
994 char local_hostname[64];
996 //determine local host name
997 if (gethostname(local_hostname, sizeof(local_hostname)) != 0)
998 strcpy(local_hostname, "localhost");
1000 if ((sock = socket_open(smtpserver, smtpport, &errmsg)) != INVALID_SOCKET) {
1001 //talk with SMTP server
1002 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, NULL)) >= 400) {
1003 errmsg = "SMTP server returned an error on connection";
1006 char buf[WRITE_BUFFER_CHUNK_SIZE];
1008 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "EHLO %s", local_hostname)) >= 400) {
1009 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "HELO %s", local_hostname)) >= 400) {
1010 errmsg = "SMTP EHLO/HELO returned error";
1014 //authenticate if needed
1015 if (username || password) {
1019 size_t usernamelen = (username ? strlen(username) : 0);
1020 size_t passwordlen = (password ? strlen(password) : 0);
1023 if ((auth = (char*)malloc(usernamelen + passwordlen + 4)) == NULL) {
1024 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
1025 return ERRMSG_MEMORY_ALLOCATION_ERROR;
1027 if ((base64auth = (char*)malloc(((usernamelen + passwordlen + 2) + 2) / 3 * 4 + 1)) == NULL) {
1028 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
1029 return ERRMSG_MEMORY_ALLOCATION_ERROR;
1031 //leave the authorization identity empty to indicate it's the same the as authentication identity
1034 //set the authentication identity
1035 memcpy(auth + len, (username ? username : ""), usernamelen + 1);
1036 len += usernamelen + 1;
1038 memcpy(auth + len, (password ? password : ""), passwordlen + 1);
1040 //padd with extra zero so groups of 3 bytes can be read
1041 auth[usernamelen + len + 1] = 0;
1043 while (inpos < len) {
1045 base64auth[outpos + 0] = mailobj->dtable[auth[inpos + 0] >> 2];
1046 base64auth[outpos + 1] = mailobj->dtable[((auth[inpos + 0] & 3) << 4) | (auth[inpos + 1] >> 4)];
1047 base64auth[outpos + 2] = mailobj->dtable[((auth[inpos + 1] & 0xF) << 2) | (auth[inpos + 2] >> 6)];
1048 base64auth[outpos + 3] = mailobj->dtable[auth[inpos + 2] & 0x3F];
1049 //padd with "=" characters if less than 3 characters were read
1050 if (inpos + 1 >= len) {
1051 base64auth[outpos + 3] = '=';
1052 if (inpos + 2 >= len)
1053 base64auth[outpos + 2] = '=';
1055 //advance to next position
1059 base64auth[outpos] = 0;
1060 //send originator e-mail address
1061 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "AUTH PLAIN %s", base64auth)) >= 400) {
1062 errmsg = "SMTP authentication failed";
1066 //send originator e-mail address
1067 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "MAIL FROM:<%s>", mailobj->from)) >= 400) {
1068 errmsg = "SMTP server did not accept sender";
1071 //send recipient e-mail addresses
1072 listentry = mailobj->to;
1073 while (!errmsg && listentry) {
1074 if (listentry->data && *listentry->data) {
1075 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "RCPT TO:<%s>", listentry->data)) >= 400)
1076 errmsg = "SMTP server did not accept e-mail address (To)";
1078 listentry = listentry->next;
1080 listentry = mailobj->cc;
1081 while (!errmsg && listentry) {
1082 if (listentry->data && *listentry->data) {
1083 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "RCPT TO:<%s>", listentry->data)) >= 400)
1084 errmsg = "SMTP server did not accept e-mail address (CC)";
1086 listentry = listentry->next;
1088 listentry = mailobj->bcc;
1089 while (!errmsg && listentry) {
1090 if (listentry->data && *listentry->data) {
1091 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "RCPT TO:<%s>", listentry->data)) >= 400)
1092 errmsg = "SMTP server did not accept e-mail address (BCC)";
1094 listentry = listentry->next;
1098 //prepare to send mail body
1099 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "DATA")) >= 400) {
1100 errmsg = "SMTP DATA returned error";
1103 //send mail body data
1104 while ((n = quickmail_get_data(buf, sizeof(buf), 1, mailobj)) > 0) {
1105 socket_send(sock, buf, n);
1108 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "\r\n.")) >= 400) {
1109 errmsg = "SMTP error after sending message data";
1114 socket_smtp_command(sock, mailobj->debuglog, "QUIT");