source: AE/RequestHelp/src/quickmail/quickmail.c@ 1192

Last change on this file since 1192 was 1005, checked in by alloc, 10 years ago

RequestHelp initial commit

File size: 40.9 KB
RevLine 
[1005]1/*
2 This file is part of libquickmail.
3
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.
8
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.
13
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/>.
16*/
17
18#if defined(_WIN32) && defined(DLL_EXPORT) && !defined(BUILD_QUICKMAIL_DLL)
19#define BUILD_QUICKMAIL_DLL
20#endif
21#include "quickmail.h"
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <time.h>
26#ifndef _WIN32
27#include <unistd.h>
28#endif
29#if _MSC_VER
30#define snprintf _snprintf
31#define strdup _strdup
32#endif
33#ifndef NOCURL
34#if (defined(STATIC) || defined(BUILD_QUICKMAIL_STATIC)) && !defined(CURL_STATICLIB)
35#define CURL_STATICLIB
36#endif
37#include <curl/curl.h>
38#else
39#include "smtpsocket.h"
40#endif
41
42#define LIBQUICKMAIL_VERSION_MAJOR 0
43#define LIBQUICKMAIL_VERSION_MINOR 1
44#define LIBQUICKMAIL_VERSION_MICRO 18
45
46#define VERSION_STRINGIZE_(major, minor, micro) #major"."#minor"."#micro
47#define VERSION_STRINGIZE(major, minor, micro) VERSION_STRINGIZE_(major, minor, micro)
48
49#define LIBQUICKMAIL_VERSION VERSION_STRINGIZE(LIBQUICKMAIL_VERSION_MAJOR,LIBQUICKMAIL_VERSION_MINOR,LIBQUICKMAIL_VERSION_MICRO)
50
51#define NEWLINE "\r\n"
52#define NEWLINELENGTH 2
53//#define NEWLINE "\n"
54//#define NEWLINELENGTH 1
55
56#define MIME_LINE_WIDTH 72
57#define BODY_BUFFER_SIZE 256
58
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
67
68static const char* default_mime_type = "text/plain";
69
70////////////////////////////////////////////////////////////////////////
71
72#define DEBUG_ERROR(errmsg)
73static const char* ERRMSG_MEMORY_ALLOCATION_ERROR = "Memory allocation error";
74
75////////////////////////////////////////////////////////////////////////
76
77char* randomize_zeros (char* data)
78{
79 //replace all 0s with random digits
80 char* p = data;
81 while (*p) {
82 if (*p == '0')
83 *p = '0' + rand() % 10;
84 p++;
85 }
86 return data;
87}
88
89char* str_append (char** data, const char* newdata)
90{
91 //append a string to the end of an existing string
92 char* p;
93 int len = (*data ? strlen(*data) : 0);
94 if ((p = (char*)realloc(*data, len + strlen(newdata) + 1)) == NULL) {
95 free(p);
96 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
97 return NULL;
98 }
99 *data = p;
100 strcpy(*data + len, newdata);
101 return *data;
102}
103
104////////////////////////////////////////////////////////////////////////
105
106struct email_info_struct {
107 int current; //must be zet to 0
108 time_t timestamp;
109 char* from;
110 struct email_info_email_list_struct* to;
111 struct email_info_email_list_struct* cc;
112 struct email_info_email_list_struct* bcc;
113 char* subject;
114 char* header;
115 struct email_info_attachment_list_struct* bodylist;
116 struct email_info_attachment_list_struct* attachmentlist;
117 char* buf;
118 int buflen;
119 char* mime_boundary_body;
120 char* mime_boundary_part;
121 struct email_info_attachment_list_struct* current_attachment;
122 FILE* debuglog;
123 char dtable[64];
124};
125
126////////////////////////////////////////////////////////////////////////
127
128struct email_info_email_list_struct {
129 char* data;
130 struct email_info_email_list_struct* next;
131};
132
133void email_info_string_list_add (struct email_info_email_list_struct** list, const char* data)
134{
135 struct email_info_email_list_struct** p = list;
136 while (*p)
137 p = &(*p)->next;
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)
140 return;
141 }
142 (*p)->data = (data ? strdup(data) : NULL);
143 (*p)->next = NULL;
144}
145
146void email_info_string_list_free (struct email_info_email_list_struct** list)
147{
148 struct email_info_email_list_struct* p = *list;
149 struct email_info_email_list_struct* current;
150 while (p) {
151 current = p;
152 p = current->next;
153 free(current->data);
154 free(current);
155 }
156 *list = NULL;
157}
158
159char* email_info_string_list_concatenate (struct email_info_email_list_struct* list)
160{
161 char* result = NULL;
162 struct email_info_email_list_struct* listentry = list;
163 while (listentry) {
164 if (listentry->data && *listentry->data) {
165 if (result)
166 str_append(&result, "," NEWLINE "\t");
167 str_append(&result, "<");
168 str_append(&result, listentry->data);
169 str_append(&result, ">");
170 }
171 listentry = listentry->next;
172 }
173 return result;
174}
175
176////////////////////////////////////////////////////////////////////////
177
178struct email_info_attachment_list_struct {
179 char* filename;
180 char* mimetype;
181 void* filedata;
182 void* handle;
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;
188};
189
190struct 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)
191{
192 struct email_info_attachment_list_struct** p = list;
193 while (*p)
194 p = &(*p)->next;
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)
197 return NULL;
198 }
199 (*p)->filename = strdup(filename ? filename : "UNNAMED");
200 (*p)->mimetype = (mimetype ? strdup(mimetype) : NULL);
201 (*p)->filedata = filedata;
202 (*p)->handle = NULL;
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;
207 (*p)->next = NULL;
208 return *p;
209}
210
211void email_info_attachment_list_free_entry (struct email_info_attachment_list_struct* current)
212{
213 if (current->handle) {
214 if (current->email_info_attachment_close)
215 current->email_info_attachment_close(current->handle);
216 //else
217 // free(current->handle);
218 current->handle = NULL;
219 }
220 if (current->filedata) {
221 if (current->email_info_attachment_filedata_free)
222 current->email_info_attachment_filedata_free(current->filedata);
223 else
224 free(current->filedata);
225 }
226 if (current->mimetype)
227 free(current->mimetype);
228 free(current->filename);
229 free(current);
230}
231
232void email_info_attachment_list_free (struct email_info_attachment_list_struct** list)
233{
234 struct email_info_attachment_list_struct* p = *list;
235 struct email_info_attachment_list_struct* current;
236 while (p) {
237 current = p;
238 p = current->next;
239 email_info_attachment_list_free_entry(current);
240 }
241 *list = NULL;
242}
243
244int email_info_attachment_list_delete (struct email_info_attachment_list_struct** list, const char* filename)
245{
246 struct email_info_attachment_list_struct** p = list;
247 while (*p) {
248 if (strcmp((*p)->filename, filename) == 0) {
249 struct email_info_attachment_list_struct* current = *p;
250 *p = current->next;
251 email_info_attachment_list_free_entry(current);
252 return 0;
253 }
254 p = &(*p)->next;
255 }
256 return -1;
257}
258
259void email_info_attachment_list_close_handles (struct email_info_attachment_list_struct* list)
260{
261 struct email_info_attachment_list_struct* p = list;
262 while (p) {
263 if (p->handle) {
264 if (p->email_info_attachment_close)
265 p->email_info_attachment_close(p->handle);
266 //else
267 // free(p->handle);
268 p->handle = NULL;
269 }
270 p = p->next;
271 }
272}
273
274//dummy attachment functions
275
276void* email_info_attachment_open_dummy (void* filedata)
277{
278 return &email_info_attachment_open_dummy;
279}
280
281size_t email_info_attachment_read_dummy (void* handle, void* buf, size_t len)
282{
283 return 0;
284}
285
286struct email_info_attachment_list_struct* email_info_attachment_list_add_dummy (struct email_info_attachment_list_struct** list, const char* filename, const char* mimetype)
287{
288 return email_info_attachment_list_add(list, filename, mimetype, NULL, email_info_attachment_open_dummy, email_info_attachment_read_dummy, NULL, NULL);
289}
290
291//file attachment functions
292
293void* email_info_attachment_open_file (void* filedata)
294{
295 return (void*)fopen((char*)filedata, "rb");
296}
297
298size_t email_info_attachment_read_file (void* handle, void* buf, size_t len)
299{
300 return fread(buf, 1, len, (FILE*)handle);
301}
302
303void email_info_attachment_close_file (void* handle)
304{
305 if (handle)
306 fclose((FILE*)handle);
307}
308
309struct email_info_attachment_list_struct* email_info_attachment_list_add_file (struct email_info_attachment_list_struct** list, const char* path, const char* mimetype)
310{
311 //determine base filename
312 const char* basename = path + strlen(path);
313 while (basename != path) {
314 basename--;
315 if (*basename == '/'
316#ifdef _WIN32
317 || *basename == '\\' || *basename == ':'
318#endif
319 ) {
320 basename++;
321 break;
322 }
323 }
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);
325}
326
327//memory attachment functions
328
329struct email_info_attachment_memory_filedata_struct {
330 char* data;
331 size_t datalen;
332 int mustfree;
333};
334
335struct email_info_attachment_memory_handle_struct {
336 const char* data;
337 size_t datalen;
338 size_t pos;
339};
340
341void* email_info_attachment_open_memory (void* filedata)
342{
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);
346 if (!data->data)
347 return NULL;
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)
350 return NULL;
351 }
352 result->data = data->data;
353 result->datalen = data->datalen;
354 result->pos = 0;
355 return result;
356}
357
358size_t email_info_attachment_read_memory (void* handle, void* buf, size_t len)
359{
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);
363 h->pos += n;
364 return n;
365}
366
367void email_info_attachment_close_memory (void* handle)
368{
369 if (handle)
370 free(handle);
371}
372
373void email_info_attachment_filedata_free_memory (void* filedata)
374{
375 struct email_info_attachment_memory_filedata_struct* data = ((struct email_info_attachment_memory_filedata_struct*)filedata);
376 if (data) {
377 if (data->mustfree)
378 free(data->data);
379 free(data);
380 }
381}
382
383struct 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)
384{
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)
388 return NULL;
389 }
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);
394}
395
396////////////////////////////////////////////////////////////////////////
397
398DLL_EXPORT_LIBQUICKMAIL const char* quickmail_get_version ()
399{
400 return VERSION_STRINGIZE(LIBQUICKMAIL_VERSION_MAJOR, LIBQUICKMAIL_VERSION_MINOR, LIBQUICKMAIL_VERSION_MICRO)
401#if defined(NOCURL)
402 "-light"
403#endif
404 ;
405}
406
407DLL_EXPORT_LIBQUICKMAIL int quickmail_initialize ()
408{
409#if defined(NOCURL) && defined(_WIN32)
410 static WSADATA wsaData;
411 int wsaerr = WSAStartup(MAKEWORD(1, 0), &wsaData);
412 if (wsaerr)
413 return -1;
414 atexit((void(*)())WSACleanup);
415#endif
416 return 0;
417}
418
419DLL_EXPORT_LIBQUICKMAIL quickmail quickmail_create (const char* from, const char* subject)
420{
421 int i;
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)
425 return NULL;
426 }
427 mailobj->current = 0;
428 mailobj->timestamp = time(NULL);
429 mailobj->from = (from ? strdup(from) : NULL);
430 mailobj->to = NULL;
431 mailobj->cc = NULL;
432 mailobj->bcc = NULL;
433 mailobj->subject = (subject ? strdup(subject) : NULL);
434 mailobj->header = NULL;
435 mailobj->bodylist = NULL;
436 mailobj->attachmentlist = NULL;
437 mailobj->buf = NULL;
438 mailobj->buflen = 0;
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);
446 }
447 for (i = 0; i < 10; i++) {
448 mailobj->dtable[52 + i] = (char)('0' + i);
449 }
450 mailobj->dtable[62] = '+';
451 mailobj->dtable[63] = '/';
452 srand(time(NULL));
453 return mailobj;
454}
455
456DLL_EXPORT_LIBQUICKMAIL void quickmail_destroy (quickmail mailobj)
457{
458 free(mailobj->from);
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);
466 free(mailobj->buf);
467 free(mailobj->mime_boundary_body);
468 free(mailobj->mime_boundary_part);
469 free(mailobj);
470}
471
472DLL_EXPORT_LIBQUICKMAIL void quickmail_set_from (quickmail mailobj, const char* from)
473{
474 free(mailobj->from);
475 mailobj->from = strdup(from);
476}
477
478DLL_EXPORT_LIBQUICKMAIL const char* quickmail_get_from (quickmail mailobj)
479{
480 return mailobj->from;
481}
482
483DLL_EXPORT_LIBQUICKMAIL void quickmail_add_to (quickmail mailobj, const char* email)
484{
485 email_info_string_list_add(&mailobj->to, email);
486}
487
488DLL_EXPORT_LIBQUICKMAIL void quickmail_add_cc (quickmail mailobj, const char* email)
489{
490 email_info_string_list_add(&mailobj->cc, email);
491}
492
493DLL_EXPORT_LIBQUICKMAIL void quickmail_add_bcc (quickmail mailobj, const char* email)
494{
495 email_info_string_list_add(&mailobj->bcc, email);
496}
497
498DLL_EXPORT_LIBQUICKMAIL void quickmail_set_subject (quickmail mailobj, const char* subject)
499{
500 free(mailobj->subject);
501 mailobj->subject = (subject ? strdup(subject) : NULL);
502}
503
504DLL_EXPORT_LIBQUICKMAIL const char* quickmail_get_subject (quickmail mailobj)
505{
506 return mailobj->subject;
507}
508
509DLL_EXPORT_LIBQUICKMAIL void quickmail_add_header (quickmail mailobj, const char* headerline)
510{
511 str_append(&mailobj->header, headerline);
512 str_append(&mailobj->header, NEWLINE);
513}
514
515DLL_EXPORT_LIBQUICKMAIL void quickmail_set_body (quickmail mailobj, const char* body)
516{
517 email_info_attachment_list_free(&mailobj->bodylist);
518 if (body)
519 email_info_attachment_list_add_memory(&mailobj->bodylist, default_mime_type, default_mime_type, strdup(body), strlen(body), 1);
520}
521
522DLL_EXPORT_LIBQUICKMAIL char* quickmail_get_body (quickmail mailobj)
523{
524 size_t n;
525 char* p;
526 char* result = NULL;
527 size_t resultlen = 0;
528 if (mailobj->bodylist && (mailobj->bodylist->handle = mailobj->bodylist->email_info_attachment_open(mailobj->bodylist->filedata)) != NULL) {
529 do {
530 if ((p = (char*)realloc(result, resultlen + BODY_BUFFER_SIZE)) == NULL) {
531 free(result);
532 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
533 break;
534 }
535 result = p;
536 if ((n = mailobj->bodylist->email_info_attachment_read(mailobj->bodylist->handle, result + resultlen, BODY_BUFFER_SIZE)) > 0)
537 resultlen += n;
538 } while (n > 0);
539 if (mailobj->bodylist->email_info_attachment_close)
540 mailobj->bodylist->email_info_attachment_close(mailobj->bodylist->handle);
541 //else
542 // free(mailobj->bodylist->handle);
543 mailobj->bodylist->handle = NULL;
544 }
545 return result;
546}
547
548DLL_EXPORT_LIBQUICKMAIL void quickmail_add_body_file (quickmail mailobj, const char* mimetype, const char* path)
549{
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);
551}
552
553DLL_EXPORT_LIBQUICKMAIL void quickmail_add_body_memory (quickmail mailobj, const char* mimetype, char* data, size_t datalen, int mustfree)
554{
555 email_info_attachment_list_add_memory(&mailobj->bodylist, (mimetype ? mimetype : default_mime_type), (mimetype ? mimetype : default_mime_type), data, datalen, mustfree);
556}
557
558DLL_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)
559{
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);
561}
562
563DLL_EXPORT_LIBQUICKMAIL int quickmail_remove_body (quickmail mailobj, const char* mimetype)
564{
565 return email_info_attachment_list_delete(&mailobj->bodylist, mimetype);
566}
567
568DLL_EXPORT_LIBQUICKMAIL void quickmail_list_bodies (quickmail mailobj, quickmail_list_attachment_callback_fn callback, void* callbackdata)
569{
570 struct email_info_attachment_list_struct* p = mailobj->bodylist;
571 while (p) {
572 callback(mailobj, p->filename, p->mimetype, p->email_info_attachment_open, p->email_info_attachment_read, p->email_info_attachment_close, callbackdata);
573 p = p->next;
574 }
575}
576
577DLL_EXPORT_LIBQUICKMAIL void quickmail_add_attachment_file (quickmail mailobj, const char* path, const char* mimetype)
578{
579 email_info_attachment_list_add_file(&mailobj->attachmentlist, path, mimetype);
580}
581
582DLL_EXPORT_LIBQUICKMAIL void quickmail_add_attachment_memory (quickmail mailobj, const char* filename, const char* mimetype, char* data, size_t datalen, int mustfree)
583{
584 email_info_attachment_list_add_memory(&mailobj->attachmentlist, filename, mimetype, data, datalen, mustfree);
585}
586
587DLL_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)
588{
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);
590}
591
592DLL_EXPORT_LIBQUICKMAIL int quickmail_remove_attachment (quickmail mailobj, const char* filename)
593{
594 return email_info_attachment_list_delete(&mailobj->attachmentlist, filename);
595}
596
597DLL_EXPORT_LIBQUICKMAIL void quickmail_list_attachments (quickmail mailobj, quickmail_list_attachment_callback_fn callback, void* callbackdata)
598{
599 struct email_info_attachment_list_struct* p = mailobj->attachmentlist;
600 while (p) {
601 callback(mailobj, p->filename, p->mimetype, p->email_info_attachment_open, p->email_info_attachment_read, p->email_info_attachment_close, callbackdata);
602 p = p->next;
603 }
604}
605
606DLL_EXPORT_LIBQUICKMAIL void quickmail_set_debug_log (quickmail mailobj, FILE* filehandle)
607{
608 mailobj->debuglog = filehandle;
609}
610
611DLL_EXPORT_LIBQUICKMAIL void quickmail_fsave (quickmail mailobj, FILE* filehandle)
612{
613 int i;
614 size_t n;
615 char buf[80];
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]);
619 }
620}
621
622DLL_EXPORT_LIBQUICKMAIL size_t quickmail_get_data (void* ptr, size_t size, size_t nmemb, void* userp)
623{
624 struct email_info_struct* mailobj = (struct email_info_struct*)userp;
625
626 //abort if no data is requested
627 if (size * nmemb == 0)
628 return 0;
629
630 //initialize on first run
631 if (mailobj->current == MAILPART_INITIALIZE) {
632 free(mailobj->buf);
633 mailobj->buf = NULL;
634 mailobj->buflen = 0;
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;
640 mailobj->current++;
641 }
642
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) {
646 char* s;
647 //generate header part
648 char** p = &mailobj->buf;
649 mailobj->buf = NULL;
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))) {
654 str_append(p, "Date: ");
655 str_append(p, timestamptext);
656 str_append(p, NEWLINE);
657 }
658#ifdef _WIN32
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))) {
661 TIME_ZONE_INFORMATION tzinfo;
662 if (GetTimeZoneInformation(&tzinfo) != TIME_ZONE_ID_INVALID)
663 sprintf(timestamptext + strlen(timestamptext), " %c%02i%02i", (tzinfo.Bias > 0 ? '-' : '+'), (int)-tzinfo.Bias / 60, (int)-tzinfo.Bias % 60);
664 str_append(p, "Date: ");
665 str_append(p, timestamptext);
666 str_append(p, NEWLINE);
667 }
668#endif
669 }
670 if (mailobj->from && *mailobj->from) {
671 str_append(p, "From: <");
672 str_append(p, mailobj->from);
673 str_append(p, ">" NEWLINE);
674 }
675 if ((s = email_info_string_list_concatenate(mailobj->to)) != NULL) {
676 str_append(p, "To: ");
677 str_append(p, s);
678 str_append(p, NEWLINE);
679 free(s);
680 }
681 if ((s = email_info_string_list_concatenate(mailobj->cc)) != NULL) {
682 str_append(p, "Cc: ");
683 str_append(p, s);
684 str_append(p, NEWLINE);
685 free(s);
686 }
687 if (mailobj->subject) {
688 str_append(p, "Subject: ");
689 str_append(p, mailobj->subject);
690 str_append(p, NEWLINE);
691 }
692 if (mailobj->header) {
693 str_append(p, mailobj->header);
694 }
695 if (mailobj->attachmentlist) {
696 str_append(p, "MIME-Version: 1.0" NEWLINE);
697 }
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);
705 }
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);
711 }
712 mailobj->buflen = strlen(mailobj->buf);
713 mailobj->current++;
714 }
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) {
721 break;
722 }
723 mailobj->current_attachment = mailobj->current_attachment->next;
724 }
725 if (!mailobj->current_attachment) {
726 mailobj->current_attachment = mailobj->attachmentlist;
727 mailobj->current++;
728 }
729 //generate attachment header
730 if (mailobj->current_attachment && mailobj->current_attachment->handle) {
731 mailobj->buf = NULL;
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);
736 }
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);
741 }
742 }
743 if (mailobj->buflen == 0 && mailobj->current_attachment && mailobj->current_attachment->handle) {
744 //read body data
745 if ((mailobj->buf = malloc(BODY_BUFFER_SIZE)) == NULL) {
746 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
747 }
748 if (mailobj->buf == NULL || (mailobj->buflen = mailobj->current_attachment->email_info_attachment_read(mailobj->current_attachment->handle, mailobj->buf, BODY_BUFFER_SIZE)) <= 0) {
749 //end of file
750 free(mailobj->buf);
751 mailobj->buflen = 0;
752 if (mailobj->current_attachment->email_info_attachment_close)
753 mailobj->current_attachment->email_info_attachment_close(mailobj->current_attachment->handle);
754 //else
755 // free(mailobj->current_attachment->handle);
756 mailobj->current_attachment->handle = NULL;
757 mailobj->current_attachment = mailobj->current_attachment->next;
758 }
759 }
760 } else {
761 mailobj->current_attachment = mailobj->attachmentlist;
762 mailobj->current++;
763 }
764 }
765 if (mailobj->buflen == 0 && mailobj->current == MAILPART_BODY_DONE) {
766 mailobj->buf = NULL;
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;
774 }
775 mailobj->current++;
776 }
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) {
783 break;
784 }
785 mailobj->current_attachment = mailobj->current_attachment->next;
786 }
787 //generate attachment header
788 if (mailobj->current_attachment && mailobj->current_attachment->handle) {
789 mailobj->buf = NULL;
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);
794 }
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);
801 }
802 } else {
803 //generate next line of attachment data
804 size_t n = 0;
805 int mimelinepos = 0;
806 unsigned char igroup[3] = {0, 0, 0};
807 unsigned char ogroup[4];
808 mailobj->buflen = 0;
809 if ((mailobj->buf = (char*)malloc(MIME_LINE_WIDTH + NEWLINELENGTH + 1)) == NULL) {
810 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
811 n = 0;
812 } else {
813 while (mimelinepos < MIME_LINE_WIDTH && (n = mailobj->current_attachment->email_info_attachment_read(mailobj->current_attachment->handle, igroup, 3)) > 0) {
814 //code data
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
820 if (n < 3) {
821 ogroup[3] = '=';
822 if (n < 2)
823 ogroup[2] = '=';
824 }
825 memcpy(mailobj->buf + mimelinepos, ogroup, 4);
826 mailobj->buflen += 4;
827 mimelinepos += 4;
828 }
829 if (mimelinepos > 0) {
830 memcpy(mailobj->buf + mimelinepos, NEWLINE, NEWLINELENGTH);
831 mailobj->buflen += NEWLINELENGTH;
832 }
833 }
834 if (n <= 0) {
835 //end of file
836 if (mailobj->current_attachment->email_info_attachment_close)
837 mailobj->current_attachment->email_info_attachment_close(mailobj->current_attachment->handle);
838 else
839 free(mailobj->current_attachment->handle);
840 mailobj->current_attachment->handle = NULL;
841 mailobj->current_attachment = mailobj->current_attachment->next;
842 }
843 }
844 } else {
845 mailobj->current++;
846 }
847 }
848 if (mailobj->buflen == 0 && mailobj->current == MAILPART_END) {
849 mailobj->buf = NULL;
850 mailobj->buflen = 0;
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;
858 }
859 //mailobj->buf = str_append(&mailobj->buf, NEWLINE "." NEWLINE);
860 //mailobj->buflen = strlen(mailobj->buf);
861 mailobj->current++;
862 }
863 if (mailobj->buflen == 0 && mailobj->current == MAILPART_DONE) {
864 break;
865 }
866 }
867
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;
875 } else {
876 free(mailobj->buf);
877 mailobj->buf = NULL;
878 mailobj->buflen = 0;
879 }
880 return len;
881 }
882
883 //if (mailobj->current != MAILPART_DONE)
884 // ;//this should never be reached
885 mailobj->current = 0;
886 return 0;
887}
888
889#ifndef NOCURL
890char* add_angle_brackets (const char* data)
891{
892 size_t datalen = strlen(data);
893 char* result;
894 if ((result = (char*)malloc(datalen + 3)) == NULL) {
895 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
896 return NULL;
897 }
898 result[0] = '<';
899 memcpy(result + 1, data, datalen);
900 result[datalen + 1] = '>';
901 result[datalen + 2] = 0;
902 return result;
903}
904#endif
905
906DLL_EXPORT_LIBQUICKMAIL const char* quickmail_send (quickmail mailobj, const char* smtpserver, unsigned int smtpport, const char* username, const char* password)
907{
908#ifndef NOCURL
909 //libcurl based sending
910 CURL *curl;
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
917 char* addr;
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;
922 }
923 snprintf(addr, len, "smtp://%s:%u", smtpserver, smtpport);
924 curl_easy_setopt(curl, CURLOPT_URL, addr);
925 free(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);
937 if (password)
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);
943 free(addr);
944 }
945 //set recipients
946 listentry = mailobj->to;
947 while (listentry) {
948 if (listentry->data && *listentry->data) {
949 addr = add_angle_brackets(listentry->data);
950 recipients = curl_slist_append(recipients, addr);
951 free(addr);
952 }
953 listentry = listentry->next;
954 }
955 listentry = mailobj->cc;
956 while (listentry) {
957 if (listentry->data && *listentry->data) {
958 addr = add_angle_brackets(listentry->data);
959 recipients = curl_slist_append(recipients, addr);
960 free(addr);
961 }
962 listentry = listentry->next;
963 }
964 listentry = mailobj->bcc;
965 while (listentry) {
966 if (listentry->data && *listentry->data) {
967 addr = add_angle_brackets(listentry->data);
968 recipients = curl_slist_append(recipients, addr);
969 free(addr);
970 }
971 listentry = listentry->next;
972 }
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);
981 }
982 //send the message
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);
987 }
988 return (result == CURLE_OK ? NULL : curl_easy_strerror(result));
989#else
990 //minimal implementation without libcurl
991 SOCKET sock;
992 char* errmsg = NULL;
993 struct email_info_email_list_struct* listentry;
994 char local_hostname[64];
995 int statuscode;
996 //determine local host name
997 if (gethostname(local_hostname, sizeof(local_hostname)) != 0)
998 strcpy(local_hostname, "localhost");
999 //connect
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";
1004 } else {
1005 size_t n;
1006 char buf[WRITE_BUFFER_CHUNK_SIZE];
1007 do {
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";
1011 break;
1012 }
1013 }
1014 //authenticate if needed
1015 if (username || password) {
1016 int len;
1017 int inpos = 0;
1018 int outpos = 0;
1019 size_t usernamelen = (username ? strlen(username) : 0);
1020 size_t passwordlen = (password ? strlen(password) : 0);
1021 char* auth;
1022 char* base64auth;
1023 if ((auth = (char*)malloc(usernamelen + passwordlen + 4)) == NULL) {
1024 DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR)
1025 return ERRMSG_MEMORY_ALLOCATION_ERROR;
1026 }
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;
1030 }
1031 //leave the authorization identity empty to indicate it's the same the as authentication identity
1032 auth[0] = 0;
1033 len = 1;
1034 //set the authentication identity
1035 memcpy(auth + len, (username ? username : ""), usernamelen + 1);
1036 len += usernamelen + 1;
1037 //set the password
1038 memcpy(auth + len, (password ? password : ""), passwordlen + 1);
1039 len += passwordlen;
1040 //padd with extra zero so groups of 3 bytes can be read
1041 auth[usernamelen + len + 1] = 0;
1042 //encode in base64
1043 while (inpos < len) {
1044 //encode data
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] = '=';
1054 }
1055 //advance to next position
1056 inpos += 3;
1057 outpos += 4;
1058 }
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";
1063 break;
1064 }
1065 }
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";
1069 break;
1070 }
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)";
1077 }
1078 listentry = listentry->next;
1079 }
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)";
1085 }
1086 listentry = listentry->next;
1087 }
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)";
1093 }
1094 listentry = listentry->next;
1095 }
1096 if (errmsg)
1097 break;
1098 //prepare to send mail body
1099 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "DATA")) >= 400) {
1100 errmsg = "SMTP DATA returned error";
1101 break;
1102 }
1103 //send mail body data
1104 while ((n = quickmail_get_data(buf, sizeof(buf), 1, mailobj)) > 0) {
1105 socket_send(sock, buf, n);
1106 }
1107 //send end of data
1108 if ((statuscode = socket_smtp_command(sock, mailobj->debuglog, "\r\n.")) >= 400) {
1109 errmsg = "SMTP error after sending message data";
1110 break;
1111 }
1112 } while (0);
1113 //log out
1114 socket_smtp_command(sock, mailobj->debuglog, "QUIT");
1115 }
1116 }
1117 //close socket
1118 socket_close(sock);
1119 return errmsg;
1120#endif
1121}
Note: See TracBrowser for help on using the repository browser.