ENGINE-298: transport encoding should only take place when the message is at the last stage before outbound return. Added a transport_encode flag to several of the MIME-tree building functions, as libetpan does not distinguish between building the MIME tree and encoding its parts.
1 // This file is under GNU General Public License 3.0
4 #include "etpan_mime.h"
5 #ifndef mailmime_param_new_with_data
6 #include <libetpan/mailprivacy_tools.h>
16 #define MAX_MESSAGE_ID 128
18 static char * generate_boundary(void)
20 char id[MAX_MESSAGE_ID];
22 // no cryptographically strong random needed here
23 const long value1 = random();
24 const long value2 = random();
25 const long value3 = random();
26 const long value4 = random();
28 snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
34 struct mailmime * part_new_empty(
35 struct mailmime_content * content,
36 struct mailmime_fields * mime_fields,
40 struct mailmime * build_info;
44 char * attr_name = NULL;
45 char * attr_value = NULL;
46 struct mailmime_parameter * param = NULL;
47 clist * parameters = NULL;
48 char *boundary = NULL;
53 mime_type = MAILMIME_SINGLE;
56 switch (content->ct_type->tp_type) {
57 case MAILMIME_TYPE_DISCRETE_TYPE:
58 mime_type = MAILMIME_SINGLE;
61 case MAILMIME_TYPE_COMPOSITE_TYPE:
62 switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
63 case MAILMIME_COMPOSITE_TYPE_MULTIPART:
64 mime_type = MAILMIME_MULTIPLE;
67 case MAILMIME_COMPOSITE_TYPE_MESSAGE:
68 if (strcasecmp(content->ct_subtype, "rfc822") == 0)
69 mime_type = MAILMIME_MESSAGE;
71 mime_type = MAILMIME_SINGLE;
84 if (mime_type == MAILMIME_MULTIPLE) {
90 attr_name = strdup("boundary");
92 if (attr_name == NULL)
95 boundary = generate_boundary();
97 attr_value = boundary;
98 if (attr_value == NULL)
101 param = mailmime_parameter_new(attr_name, attr_value);
108 if (content->ct_parameters == NULL) {
109 parameters = clist_new();
111 if (parameters == NULL)
115 parameters = content->ct_parameters;
118 r = clist_append(parameters, param);
123 if (content->ct_parameters == NULL)
124 content->ct_parameters = parameters;
127 build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
128 NULL, NULL, list, NULL, NULL);
129 if (build_info == NULL)
139 if (content->ct_parameters == NULL)
141 clist_free(parameters);
143 mailmime_parameter_free(param);
147 struct mailmime * get_pgp_encrypted_part(void)
149 struct mailmime * mime = NULL;
150 struct mailmime_fields * mime_fields = NULL;
151 struct mailmime_content * content = NULL;
154 content = mailmime_content_new_with_str("application/pgp-encrypted");
158 mime_fields = mailmime_fields_new_empty();
159 if (mime_fields == NULL)
162 mime = part_new_empty(content, mime_fields, 1);
168 r = mailmime_set_body_text(mime, "Version: 1\n", 10);
176 mailmime_content_free(content);
178 mailmime_fields_free(mime_fields);
185 struct mailmime * get_text_part(
186 pEp_rid_list_t* resource,
187 const char * mime_type,
193 char * disposition_name = NULL;
194 struct mailmime_fields * mime_fields = NULL;
195 struct mailmime * mime = NULL;
196 struct mailmime_content * content = NULL;
197 struct mailmime_parameter * param = NULL;
198 struct mailmime_disposition * disposition = NULL;
199 struct mailmime_mechanism * encoding = NULL;
200 char* content_id = NULL;
203 if (resource != NULL && resource->rid != NULL) {
204 switch (resource->rid_type) {
206 content_id = strdup(resource->rid);
208 case PEP_RID_FILENAME:
210 disposition_name = strdup(resource->rid);
211 if (disposition_name == NULL)
215 mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
216 disposition_name, NULL, NULL, NULL, (size_t) -1);
218 if (disposition == NULL)
221 disposition_name = NULL;
227 encoding = mailmime_mechanism_new(encoding_type, NULL);
228 if (encoding == NULL)
232 mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
234 if (mime_fields == NULL)
240 content = mailmime_content_new_with_str(mime_type);
244 if (encoding_type != MAILMIME_MECHANISM_7BIT) {
245 param = mailmime_param_new_with_data("charset", "utf-8");
246 r = clist_append(content->ct_parameters, param);
251 mime = part_new_empty(content, mime_fields, 1);
258 r = mailmime_set_body_text(mime, (char *) text, length);
266 free(disposition_name);
268 mailmime_fields_free(mime_fields);
272 mailmime_content_free(content);
274 mailmime_parameter_free(param);
276 mailmime_disposition_free(disposition);
278 mailmime_mechanism_free(encoding);
283 struct mailmime * get_file_part(
284 pEp_rid_list_t* resource,
285 const char * mime_type,
288 bool transport_encode
291 char * disposition_name = NULL;
293 struct mailmime_disposition * disposition = NULL;
294 struct mailmime_mechanism * encoding = NULL;
295 struct mailmime_content * content = NULL;
296 struct mailmime * mime = NULL;
297 struct mailmime_fields * mime_fields = NULL;
298 char* content_id = NULL;
301 if (resource != NULL && resource->rid != NULL) {
302 switch (resource->rid_type) {
304 content_id = strdup(resource->rid);
306 mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
307 NULL, NULL, NULL, NULL, (size_t) -1);
308 if (disposition == NULL)
311 case PEP_RID_FILENAME:
313 disposition_name = strdup(resource->rid);
314 if (disposition_name == NULL)
318 mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
319 disposition_name, NULL, NULL, NULL, (size_t) -1);
321 if (disposition == NULL)
323 disposition_name = NULL;
330 content = mailmime_content_new_with_str(mime_type);
336 if (transport_encode) {
337 encoding_type = MAILMIME_MECHANISM_BASE64;
338 encoding = mailmime_mechanism_new(encoding_type, NULL);
339 if (encoding == NULL)
343 mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
345 if (mime_fields == NULL)
350 mime = part_new_empty(content, mime_fields, 1);
358 r = mailmime_set_body_text(mime, data, length);
366 free(disposition_name);
368 mailmime_disposition_free(disposition);
370 mailmime_mechanism_free(encoding);
372 mailmime_content_free(content);
374 mailmime_fields_free(mime_fields);
381 struct mailmime * part_multiple_new(const char *type)
383 struct mailmime_fields *mime_fields = NULL;
384 struct mailmime_content *content = NULL;
385 struct mailmime *mp = NULL;
387 mime_fields = mailmime_fields_new_empty();
388 if (mime_fields == NULL)
391 content = mailmime_content_new_with_str(type);
395 mp = part_new_empty(content, mime_fields, 0);
403 mailmime_content_free(content);
405 mailmime_fields_free(mime_fields);
410 struct mailimf_field * _new_field(
412 _new_func_t new_func,
416 void *data = new_func(value);
421 struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
423 if (result == NULL) {
428 result->fld_type = type;
429 result->fld_data.fld_return_path = data;
434 void _free_field(struct mailimf_field *field)
437 free(field->fld_data.fld_return_path);
444 _new_func_t new_func,
449 struct mailimf_field * field;
455 field = _new_field(type, new_func, value);
459 r = clist_append(list, field);
466 // http://media2.giga.de/2014/02/Image-28.jpg
468 struct mailimf_date_time * timestamp_to_etpantime(const struct tm *ts)
470 struct mailimf_date_time * result = calloc(1,
471 sizeof(struct mailimf_date_time));
478 result->dt_sec = ts->tm_sec;
479 result->dt_min = ts->tm_min;
480 result->dt_hour = ts->tm_hour;
481 result->dt_day = ts->tm_mday;
482 result->dt_month = ts->tm_mon + 1;
483 result->dt_year = ts->tm_year + 1900;
485 result->dt_zone = (int) (ts->tm_gmtoff / 36L);
490 struct tm * etpantime_to_timestamp(const struct mailimf_date_time *et)
492 struct tm * result = calloc(1, sizeof(struct tm));
499 result->tm_sec = et->dt_sec;
500 result->tm_min = et->dt_min;
501 result->tm_hour = et->dt_hour;
502 result->tm_mday = et->dt_day;
503 result->tm_mon = et->dt_month - 1;
504 result->tm_year = et->dt_year - 1900;
506 result->tm_gmtoff = 36L * (long) et->dt_zone;
511 struct mailimf_mailbox * mailbox_from_string(
516 struct mailimf_mailbox *mb = NULL;
518 char *_address = NULL;
522 _name = name ? strdup(name) : strdup("");
526 _address = strdup(address);
527 if (_address == NULL)
530 mb = mailimf_mailbox_new(_name, _address);
544 struct mailimf_field * create_optional_field(
551 struct mailimf_optional_field *optional_field = NULL;
553 _field = strdup(field);
557 _value = mailmime_encode_subject_header("utf-8", value, 0);
561 optional_field = mailimf_optional_field_new(_field, _value);
562 if (optional_field == NULL)
565 struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
570 result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
571 result->fld_data.fld_optional_field = optional_field;
576 if (optional_field) {
577 mailimf_optional_field_free(optional_field);
587 int _append_optional_field(
594 struct mailimf_field * optional_field =
595 create_optional_field(field, value);
597 if (optional_field == NULL)
600 r = clist_append(list, optional_field);
602 mailimf_field_free(optional_field);
607 clist * _get_fields(struct mailmime * mime)
609 clist * _fieldlist = NULL;
613 if (mime->mm_data.mm_message.mm_fields &&
614 mime->mm_data.mm_message.mm_fields->fld_list) {
615 _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
621 struct mailmime_content * _get_content(struct mailmime * mime)
623 struct mailmime_content * content = NULL;
627 if (mime->mm_data.mm_message.mm_msg_mime)
628 content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
634 /* Return a list of identifier_type and resource id (filename, cid, etc) */
635 pEp_rid_list_t* _get_resource_id_list(struct mailmime *mime)
637 clist * _fieldlist = NULL;
641 if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
642 _fieldlist = mime->mm_mime_fields->fld_list;
648 pEp_rid_list_t* rid_list = NULL;
649 pEp_rid_list_t** rid_list_curr_p = &rid_list;
651 for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
652 struct mailmime_field * _field = clist_content(cur);
654 if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
655 pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
656 new_rid->rid_type = PEP_RID_CID;
657 new_rid->rid = strdup(_field->fld_data.fld_id);
658 *rid_list_curr_p = new_rid;
659 rid_list_curr_p = &new_rid->next;
661 else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
663 if (_field->fld_data.fld_disposition &&
664 _field->fld_data.fld_disposition->dsp_parms) {
666 _field->fld_data.fld_disposition->dsp_parms;
668 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
670 struct mailmime_disposition_parm * param =
672 if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
673 pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
674 new_rid->rid_type = PEP_RID_FILENAME;
675 new_rid->rid = strdup(param->pa_data.pa_filename);
676 *rid_list_curr_p = new_rid;
677 rid_list_curr_p = &new_rid->next;
683 /* Will almost certainly usually be a singleton, but we need to be able to decide */
688 /* FIXME: about to be obsoleted? */
689 char * _get_filename_or_cid(struct mailmime *mime)
691 clist * _fieldlist = NULL;
695 if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
696 _fieldlist = mime->mm_mime_fields->fld_list;
702 char* _temp_filename_ptr = NULL;
704 for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
705 struct mailmime_field * _field = clist_content(cur);
706 if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
707 /* We prefer CIDs to filenames when both are present */
708 free(_temp_filename_ptr); /* can be null, it's ok */
709 return build_uri("cid", _field->fld_data.fld_id);
711 else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
712 if (_field->fld_data.fld_disposition &&
713 _field->fld_data.fld_disposition->dsp_parms &&
714 !_temp_filename_ptr) {
716 _field->fld_data.fld_disposition->dsp_parms;
718 for (cur2 = clist_begin(_parmlist); cur2; cur2 =
720 struct mailmime_disposition_parm * param =
722 if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
723 _temp_filename_ptr = build_uri("file", param->pa_data.pa_filename);
730 /* Ok, it wasn't a CID */
731 return _temp_filename_ptr;
734 static bool parameter_has_value(
735 struct mailmime_content *content,
745 clist * list = content->ct_parameters;
749 for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
750 struct mailmime_parameter * param = clist_content(cur);
752 param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
753 param->pa_value && strcasecmp(value, param->pa_value) == 0)
760 bool _is_multipart(struct mailmime_content *content, const char *subtype)
764 if (content->ct_type && content->ct_type->tp_type ==
765 MAILMIME_TYPE_COMPOSITE_TYPE &&
766 content->ct_type->tp_data.tp_composite_type &&
767 content->ct_type->tp_data.tp_composite_type->ct_type ==
768 MAILMIME_COMPOSITE_TYPE_MULTIPART) {
770 return content->ct_subtype &&
771 strcasecmp(content->ct_subtype, subtype) == 0;
779 bool _is_PGP_MIME(struct mailmime_content *content)
783 if (_is_multipart(content, "encrypted") &&
784 parameter_has_value(content, "protocol",
785 "application/pgp-encrypted"))
791 bool _is_text_part(struct mailmime_content *content, const char *subtype)
795 if (content->ct_type && content->ct_type->tp_type ==
796 MAILMIME_TYPE_DISCRETE_TYPE &&
797 content->ct_type->tp_data.tp_discrete_type &&
798 content->ct_type->tp_data.tp_discrete_type->dt_type ==
799 MAILMIME_DISCRETE_TYPE_TEXT) {
801 return content->ct_subtype &&
802 strcasecmp(content->ct_subtype, subtype) == 0;
810 int _get_content_type(
811 const struct mailmime_content *content,
817 char *_charset = NULL;
826 if (content->ct_subtype == NULL)
829 if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
831 const char *_main_type;
833 switch (content->ct_type->tp_data.tp_discrete_type->dt_type) {
834 case MAILMIME_DISCRETE_TYPE_TEXT:
837 case MAILMIME_DISCRETE_TYPE_IMAGE:
838 _main_type = "image";
840 case MAILMIME_DISCRETE_TYPE_AUDIO:
841 _main_type = "audio";
843 case MAILMIME_DISCRETE_TYPE_VIDEO:
844 _main_type = "video";
846 case MAILMIME_DISCRETE_TYPE_APPLICATION:
847 _main_type = "application";
849 case MAILMIME_DISCRETE_TYPE_EXTENSION:
850 _main_type = "extension";
856 len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
857 _type = calloc(1, len);
862 strncpy(_type, _main_type, len);
863 len -= strlen(_main_type);
864 strncat(_type, "/", len--);
865 strncat(_type, content->ct_subtype, len);
867 if (content->ct_parameters) {
869 for (cur = clist_begin(content->ct_parameters); cur; cur =
871 struct mailmime_parameter * param = clist_content(cur);
872 if (param && param->pa_name && strcasecmp(param->pa_name,
874 _charset = param->pa_value;
879 *charset = strdup(_charset);