|
|
- /**
- * @file etpan_mime.c
- * @brief File description for doxygen missing. FIXME
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #include "etpan_mime.h"
- #ifndef mailmime_param_new_with_data
- #include <libetpan/mailprivacy_tools.h>
- #endif
-
- #include "pEp_internal.h"
- #include "platform.h"
- #include "mime.h"
- #include "wrappers.h"
- #include "resource_id.h"
-
- #include <string.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <errno.h>
-
- #define MAX_MESSAGE_ID 128
-
- #define MAX_IMF_LINE_LEN 998
-
- static bool ascii_exceeds_line_length(const char* data, size_t size) {
-
- const char* curr_pos = data;
- const char* last_pos = data;
- const char* end_pos = data + size;
- const char* crlf = "\r\n";
-
- while ((curr_pos + MAX_IMF_LINE_LEN) < end_pos) {
- last_pos = curr_pos;
- curr_pos = strnstr(curr_pos, crlf, end_pos - curr_pos);
- if (!curr_pos)
- return true;
- if (curr_pos - last_pos > MAX_IMF_LINE_LEN)
- return true;
- curr_pos += 2;
- }
-
- return false;
- }
-
- /**
- * @internal
- *
- * <!-- generate_boundary() -->
- *
- * @brief TODO
- *
- *
- */
-
- static char * generate_boundary(void)
- {
- char id[MAX_MESSAGE_ID];
-
- // no cryptographically strong random needed here
- const long value1 = random();
- const long value2 = random();
- const long value3 = random();
- const long value4 = random();
-
- snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
- value3, value4);
-
- return strdup(id);
- }
-
- struct mailmime * part_new_empty(
- struct mailmime_content * content,
- struct mailmime_fields * mime_fields,
- stringpair_list_t* param_keyvals,
- int force_single
- )
- {
- struct mailmime * build_info;
- clist * list = NULL;
- int r;
- int mime_type;
- char * attr_name = NULL;
- char * attr_value = NULL;
- struct mailmime_parameter * param = NULL;
- clist * parameters = NULL;
- char *boundary = NULL;
-
- list = NULL;
-
- if (force_single) {
- mime_type = MAILMIME_SINGLE;
- }
- else {
- switch (content->ct_type->tp_type) {
- case MAILMIME_TYPE_DISCRETE_TYPE:
- mime_type = MAILMIME_SINGLE;
- break;
-
- case MAILMIME_TYPE_COMPOSITE_TYPE:
- switch (content->ct_type->tp_data.tp_composite_type->ct_type) {
- case MAILMIME_COMPOSITE_TYPE_MULTIPART:
- mime_type = MAILMIME_MULTIPLE;
- break;
-
- case MAILMIME_COMPOSITE_TYPE_MESSAGE:
- if (strcasecmp(content->ct_subtype, "rfc822") == 0)
- mime_type = MAILMIME_MESSAGE;
- else
- mime_type = MAILMIME_SINGLE;
- break;
-
- default:
- goto enomem;
- }
- break;
-
- default:
- goto enomem;
- }
- }
-
- if (mime_type == MAILMIME_MULTIPLE) {
- list = clist_new();
- assert(list);
- if (list == NULL)
- goto enomem;
-
- attr_name = strdup("boundary");
- assert(attr_name);
- if (attr_name == NULL)
- goto enomem;
-
- boundary = generate_boundary();
- assert(boundary);
- attr_value = boundary;
- if (attr_value == NULL)
- goto enomem;
-
- param = mailmime_parameter_new(attr_name, attr_value);
- assert(param);
- if (param == NULL)
- goto enomem;
- attr_name = NULL;
- attr_value = NULL;
-
- if (content->ct_parameters == NULL) {
- parameters = clist_new();
- assert(parameters);
- if (parameters == NULL)
- goto enomem;
- }
- else {
- parameters = content->ct_parameters;
- }
-
- r = clist_append(parameters, param);
- if (r)
- goto enomem;
- param = NULL;
-
- if (content->ct_parameters == NULL)
- content->ct_parameters = parameters;
- }
-
- if (param_keyvals) {
- stringpair_list_t* cur;
- for (cur = param_keyvals; cur; cur = cur->next) {
- attr_name = strdup(cur->value->key);
- attr_value = strdup(cur->value->value);
-
- param = mailmime_parameter_new(attr_name, attr_value);
- assert(param);
- if (param == NULL)
- goto enomem;
-
- attr_name = NULL;
- attr_value = NULL;
-
- if (content->ct_parameters == NULL) {
- parameters = clist_new();
- assert(parameters);
- if (parameters == NULL)
- goto enomem;
- }
- else {
- parameters = content->ct_parameters;
- }
-
- r = clist_append(parameters, param);
- if (r)
- goto enomem;
- param = NULL;
-
- if (content->ct_parameters == NULL)
- content->ct_parameters = parameters;
- }
- }
-
- build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
- NULL, NULL, list, NULL, NULL);
- if (build_info == NULL)
- goto enomem;
-
- return build_info;
-
- enomem:
- if (list)
- clist_free(list);
- free(attr_name);
- free(attr_value);
- if (content->ct_parameters == NULL)
- if (parameters)
- clist_free(parameters);
- if (param)
- mailmime_parameter_free(param);
- return NULL;
- }
-
- struct mailmime * get_pgp_encrypted_part(void)
- {
- struct mailmime * mime = NULL;
- struct mailmime_fields * mime_fields = NULL;
- struct mailmime_content * content = NULL;
- int r;
-
- content = mailmime_content_new_with_str("application/pgp-encrypted");
- if (content == NULL)
- goto enomem;
-
- mime_fields = mailmime_fields_new_empty();
- if (mime_fields == NULL)
- goto enomem;
-
- mime = part_new_empty(content, mime_fields, NULL, 1);
- if (mime == NULL)
- goto enomem;
- mime_fields = NULL;
- content = NULL;
-
- r = mailmime_set_body_text(mime, "Version: 1\n", 10);
- if (r != 0)
- goto enomem;
-
- return mime;
-
- enomem:
- if (content)
- mailmime_content_free(content);
- if (mime_fields)
- mailmime_fields_free(mime_fields);
- if (mime)
- mailmime_free(mime);
-
- return NULL;
- }
-
- struct mailmime * get_text_part(
- pEp_rid_list_t* resource,
- const char * mime_type,
- const char * text,
- size_t length,
- int encoding_type
- )
- {
- char * disposition_name = NULL;
- struct mailmime_fields * mime_fields = NULL;
- struct mailmime * mime = NULL;
- struct mailmime_content * content = NULL;
- struct mailmime_parameter * param = NULL;
- struct mailmime_disposition * disposition = NULL;
- struct mailmime_mechanism * encoding = NULL;
- char* content_id = NULL;
- int r;
-
- if (resource != NULL && resource->rid != NULL) {
- switch (resource->rid_type) {
- case PEP_RID_CID:
- content_id = strdup(resource->rid);
- break;
- case PEP_RID_FILENAME:
- default:
- disposition_name = strdup(resource->rid);
- if (disposition_name == NULL)
- goto enomem;
-
- disposition =
- mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
- disposition_name, NULL, NULL, NULL, (size_t) -1);
-
- if (disposition == NULL)
- goto enomem;
-
- disposition_name = NULL;
- break;
- }
- }
-
- if (encoding_type) {
- encoding = mailmime_mechanism_new(encoding_type, NULL);
- if (encoding == NULL)
- goto enomem;
- }
-
- mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
- disposition, NULL);
- if (mime_fields == NULL)
- goto enomem;
- encoding = NULL;
- disposition = NULL;
- content_id = NULL;
-
- content = mailmime_content_new_with_str(mime_type);
- if (content == NULL)
- goto enomem;
-
- if (encoding_type != MAILMIME_MECHANISM_7BIT) {
- param = mailmime_param_new_with_data("charset", "utf-8");
- r = clist_append(content->ct_parameters, param);
- if (r != 0)
- goto enomem;
- }
-
- mime = part_new_empty(content, mime_fields, NULL, 1);
- if (mime == NULL)
- goto enomem;
- content = NULL;
- mime_fields = NULL;
-
- if (text) {
- r = mailmime_set_body_text(mime, (char *) text, length);
- if (r != 0)
- goto enomem;
- }
-
- return mime;
-
- enomem:
- free(disposition_name);
- if (mime_fields)
- mailmime_fields_free(mime_fields);
- if (mime)
- mailmime_free(mime);
- if (content)
- mailmime_content_free(content);
- if (param)
- mailmime_parameter_free(param);
- if (disposition)
- mailmime_disposition_free(disposition);
- if (encoding)
- mailmime_mechanism_free(encoding);
-
- return NULL;
- }
-
- struct mailmime * get_file_part(
- pEp_rid_list_t* resource,
- const char * mime_type,
- char * data,
- size_t length,
- bool is_nf_message_attachment // non-forwarded msg as att
- )
- {
- char * disposition_name = NULL;
- int encoding_type;
- struct mailmime_disposition * disposition = NULL;
- struct mailmime_mechanism * encoding = NULL;
- struct mailmime_content * content = NULL;
- struct mailmime * mime = NULL;
- struct mailmime_fields * mime_fields = NULL;
- char* content_id = NULL;
- int r;
-
- if (resource != NULL && resource->rid != NULL) {
- switch (resource->rid_type) {
- case PEP_RID_CID:
- content_id = strdup(resource->rid);
- disposition =
- mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE,
- NULL, NULL, NULL, NULL, (size_t) -1);
- if (disposition == NULL)
- goto enomem;
- break;
- case PEP_RID_FILENAME:
- default:
- disposition_name = strdup(resource->rid);
- if (disposition_name == NULL)
- goto enomem;
-
- disposition =
- mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT,
- disposition_name, NULL, NULL, NULL, (size_t) -1);
-
- if (disposition == NULL)
- goto enomem;
- disposition_name = NULL;
-
- break;
- }
- }
-
-
- content = mailmime_content_new_with_str(mime_type);
- if (content == NULL)
- goto enomem;
-
- encoding = NULL;
-
- bool already_ascii = !(must_chunk_be_encoded(data, length, true));
-
- // check to be sure, if it is already ascii, that line lengths aren't also
- // exceeded. Otherwise, we should base64-encode anyway.
-
- if (!is_nf_message_attachment && !already_ascii) {
- encoding_type = MAILMIME_MECHANISM_BASE64;
- encoding = mailmime_mechanism_new(encoding_type, NULL);
- if (encoding == NULL)
- goto enomem;
- }
-
- mime_fields = mailmime_fields_new_with_data(encoding, content_id, NULL,
- disposition, NULL);
- if (mime_fields == NULL)
- goto enomem;
- encoding = NULL;
- disposition = NULL;
-
- stringpair_list_t* extra_params = NULL;
-
- if (is_nf_message_attachment)
- extra_params = new_stringpair_list(new_stringpair("forwarded", "no"));
-
- mime = part_new_empty(content, mime_fields, extra_params, 1);
- free_stringpair_list(extra_params);
- if (mime == NULL)
- goto enomem;
- content = NULL;
- mime_fields = NULL;
-
- if(length > 0)
- {
- r = mailmime_set_body_text(mime, data, length);
- if (r != 0)
- goto enomem;
- }
-
- return mime;
-
- enomem:
- free(disposition_name);
- if (disposition)
- mailmime_disposition_free(disposition);
- if (encoding)
- mailmime_mechanism_free(encoding);
- if (content)
- mailmime_content_free(content);
- if (mime_fields)
- mailmime_fields_free(mime_fields);
- if (mime)
- mailmime_free(mime);
-
- return NULL;
- }
-
- struct mailmime * part_multiple_new(const char *type)
- {
- struct mailmime_fields *mime_fields = NULL;
- struct mailmime_content *content = NULL;
- struct mailmime *mp = NULL;
-
- mime_fields = mailmime_fields_new_empty();
- if (mime_fields == NULL)
- goto enomem;
-
- content = mailmime_content_new_with_str(type);
- if (content == NULL)
- goto enomem;
-
- mp = part_new_empty(content, mime_fields, NULL, 0);
- if (mp == NULL)
- goto enomem;
-
- return mp;
-
- enomem:
- if (content)
- mailmime_content_free(content);
- if (mime_fields)
- mailmime_fields_free(mime_fields);
-
- return NULL;
- }
-
- struct mailimf_field * _new_field(
- int type,
- _new_func_t new_func,
- void *value
- )
- {
- void *data = new_func(value);
- assert(data);
- if (data == NULL)
- return NULL;
-
- struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
- assert(result);
- if (result == NULL) {
- free(data);
- return NULL;
- }
-
- result->fld_type = type;
- result->fld_data.fld_return_path = data;
-
- return result;
- }
-
- void _free_field(struct mailimf_field *field)
- {
- if (field)
- free(field->fld_data.fld_return_path);
- free(field);
- }
-
- int _append_field(
- clist *list,
- int type,
- _new_func_t new_func,
- void *value
- )
- {
- int r;
- struct mailimf_field * field;
-
- assert(list);
- assert(new_func);
- assert(value);
-
- field = _new_field(type, new_func, value);
- if (field == NULL)
- return -1;
-
- r = clist_append(list, field);
- if (r)
- _free_field(field);
-
- return r;
- }
-
- // http://media2.giga.de/2014/02/Image-28.jpg
-
- struct mailimf_date_time * timestamp_to_etpantime(const timestamp *ts)
- {
- struct mailimf_date_time * result = calloc(1,
- sizeof(struct mailimf_date_time));
- assert(result);
- if (result == NULL)
- return NULL;
-
- assert(ts);
-
- result->dt_sec = ts->tm_sec;
- result->dt_min = ts->tm_min;
- result->dt_hour = ts->tm_hour;
- result->dt_day = ts->tm_mday;
- result->dt_month = ts->tm_mon + 1;
- result->dt_year = ts->tm_year + 1900;
- #ifndef __MVS__
- result->dt_zone = (int) (ts->tm_gmtoff / 36L);
- #endif
- return result;
- }
-
- timestamp * etpantime_to_timestamp(const struct mailimf_date_time *et)
- {
- timestamp * result = calloc(1, sizeof(timestamp));
- assert(result);
- if (result == NULL)
- return NULL;
-
- assert(et);
-
- result->tm_sec = et->dt_sec;
- result->tm_min = et->dt_min;
- result->tm_hour = et->dt_hour;
- result->tm_mday = et->dt_day;
- result->tm_mon = et->dt_month - 1;
- result->tm_year = et->dt_year - 1900;
- #ifndef __MVS__
- result->tm_gmtoff = 36L * (long) et->dt_zone;
- #endif
-
- // Normalize to UTC and then forget the offset.
- time_t t = timegm_with_gmtoff(result);
- gmtime_r(&t, result);
- #ifndef __MVS__
- result->tm_gmtoff = 0;
- #endif
- return result;
- }
-
- struct mailimf_mailbox * mailbox_from_string(
- const char *name,
- const char *address
- )
- {
- assert(address);
- if (!address)
- return NULL;
-
- struct mailimf_mailbox *mb = NULL;
- char *_name = NULL;
- char *_address = NULL;
-
- _name = name ? strdup(name) : strdup("");
- if (_name == NULL)
- goto enomem;
-
- char* at = strstr(address, "@");
- if (!at) {
- // Presumed URI
- int added_char_len = 6; // " " @URI
- int new_addr_len = strlen(address) + added_char_len + 1;
- _address = calloc(new_addr_len, 1);
- if (_address == NULL)
- goto enomem;
-
- _address[0] = '"';
- strlcat(_address, address, new_addr_len);
- strlcat(_address, "\"@URI", new_addr_len);
- }
- else {
- _address = strdup(address);
- if (_address == NULL)
- goto enomem;
- }
-
- mb = mailimf_mailbox_new(_name, _address);
- assert(mb);
- if (mb == NULL)
- goto enomem;
-
- return mb;
-
- enomem:
- free(_name);
- free(_address);
-
- return NULL;
- }
-
-
- struct mailimf_field * create_optional_field(
- const char *field,
- const char *value
- )
- {
- char *_field = NULL;
- char *_value = NULL;
- struct mailimf_optional_field *optional_field = NULL;
-
- _field = strdup(field);
- if (_field == NULL)
- goto enomem;
-
- if (!must_field_value_be_encoded(value))
- _value = strdup(value);
- else
- _value = mailmime_encode_subject_header("utf-8", value, 0);
- if (_value == NULL)
- goto enomem;
-
- optional_field = mailimf_optional_field_new(_field, _value);
- if (optional_field == NULL)
- goto enomem;
-
- struct mailimf_field * result = calloc(1, sizeof(struct mailimf_field));
- assert(result);
- if (result == NULL)
- goto enomem;
-
- result->fld_type = MAILIMF_FIELD_OPTIONAL_FIELD;
- result->fld_data.fld_optional_field = optional_field;
-
- return result;
-
- enomem:
- if (optional_field) {
- mailimf_optional_field_free(optional_field);
- }
- else {
- free(_field);
- free(_value);
- }
-
- return NULL;
- }
-
- int _append_optional_field(
- clist *list,
- const char *field,
- const char *value
- )
- {
- int r;
- struct mailimf_field * optional_field =
- create_optional_field(field, value);
-
- if (optional_field == NULL)
- return -1;
-
- r = clist_append(list, optional_field);
- if (r)
- mailimf_field_free(optional_field);
-
- return r;
- }
-
- clist * _get_fields(struct mailmime * mime)
- {
- clist * _fieldlist = NULL;
-
- assert(mime);
-
- if (mime->mm_data.mm_message.mm_fields &&
- mime->mm_data.mm_message.mm_fields->fld_list) {
- _fieldlist = mime->mm_data.mm_message.mm_fields->fld_list;
- }
-
- return _fieldlist;
- }
-
- struct mailmime_content * _get_content(struct mailmime * mime)
- {
- struct mailmime_content * content = NULL;
-
- assert(mime);
-
- if (mime->mm_data.mm_message.mm_msg_mime)
- content = mime->mm_data.mm_message.mm_msg_mime->mm_content_type;
-
- return content;
- }
-
-
- /* Return a list of identifier_type and resource id (filename, cid, etc) */
- pEp_rid_list_t* _get_resource_id_list(struct mailmime *mime)
- {
- clist * _fieldlist = NULL;
-
- assert(mime);
-
- if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
- _fieldlist = mime->mm_mime_fields->fld_list;
- else
- return NULL;
-
- clistiter *cur;
-
- pEp_rid_list_t* rid_list = NULL;
- pEp_rid_list_t** rid_list_curr_p = &rid_list;
-
- for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
- struct mailmime_field * _field = clist_content(cur);
- /* content_id */
- if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
- pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
- new_rid->rid_type = PEP_RID_CID;
- new_rid->rid = strdup(_field->fld_data.fld_id);
- *rid_list_curr_p = new_rid;
- rid_list_curr_p = &new_rid->next;
- }
- else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
- /* filename */
- if (_field->fld_data.fld_disposition &&
- _field->fld_data.fld_disposition->dsp_parms) {
- clist * _parmlist =
- _field->fld_data.fld_disposition->dsp_parms;
- clistiter *cur2;
- for (cur2 = clist_begin(_parmlist); cur2; cur2 =
- clist_next(cur2)) {
- struct mailmime_disposition_parm * param =
- clist_content(cur2);
- if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
- pEp_rid_list_t* new_rid = (pEp_rid_list_t*)calloc(1, sizeof(pEp_rid_list_t));
- new_rid->rid_type = PEP_RID_FILENAME;
- new_rid->rid = strdup(param->pa_data.pa_filename);
- *rid_list_curr_p = new_rid;
- rid_list_curr_p = &new_rid->next;
- }
- }
- }
- }
- }
- /* Will almost certainly usually be a singleton, but we need to be able to decide */
- return rid_list;
- }
-
-
- /* FIXME: about to be obsoleted? */
- char * _get_filename_or_cid(struct mailmime *mime)
- {
- clist * _fieldlist = NULL;
-
- assert(mime);
-
- if (mime->mm_mime_fields && mime->mm_mime_fields->fld_list)
- _fieldlist = mime->mm_mime_fields->fld_list;
- else
- return NULL;
-
- clistiter *cur;
-
- char* _temp_filename_ptr = NULL;
-
- for (cur = clist_begin(_fieldlist); cur; cur = clist_next(cur)) {
- struct mailmime_field * _field = clist_content(cur);
- if (_field && _field->fld_type == MAILMIME_FIELD_ID) {
- /* We prefer CIDs to filenames when both are present */
- free(_temp_filename_ptr); /* can be null, it's ok */
- return build_uri("cid", _field->fld_data.fld_id);
- }
- else if (_field && _field->fld_type == MAILMIME_FIELD_DISPOSITION) {
- if (_field->fld_data.fld_disposition &&
- _field->fld_data.fld_disposition->dsp_parms &&
- !_temp_filename_ptr) {
- clist * _parmlist =
- _field->fld_data.fld_disposition->dsp_parms;
- clistiter *cur2;
- for (cur2 = clist_begin(_parmlist); cur2; cur2 =
- clist_next(cur2)) {
- struct mailmime_disposition_parm * param =
- clist_content(cur2);
- if (param->pa_type == MAILMIME_DISPOSITION_PARM_FILENAME) {
- _temp_filename_ptr = build_uri("file", param->pa_data.pa_filename);
- break;
- }
- }
- }
- }
- }
- /* Ok, it wasn't a CID */
- return _temp_filename_ptr;
- }
-
- /**
- * @internal
- *
- * <!-- parameter_has_value() -->
- *
- * @brief TODO
- *
- * @param[in] *content structmailmime_content
- * @param[in] *name constchar
- * @param[in] *value constchar
- *
- */
- static bool parameter_has_value(
- struct mailmime_content *content,
- const char *name,
- const char *value
- )
- {
- clistiter *cur;
-
- assert(name);
- assert(value);
-
- clist * list = content->ct_parameters;
- if (list == NULL)
- return false;
-
- for (cur = clist_begin(list); cur != NULL ; cur = clist_next(cur)) {
- struct mailmime_parameter * param = clist_content(cur);
- if (param &&
- param->pa_name && strcasecmp(name, param->pa_name) == 0 &&
- param->pa_value && strcasecmp(value, param->pa_value) == 0)
- return true;
- }
-
- return false;
- }
-
- bool _is_multipart(struct mailmime_content *content, const char *subtype)
- {
- assert(content);
-
- if (content->ct_type && content->ct_type->tp_type ==
- MAILMIME_TYPE_COMPOSITE_TYPE &&
- content->ct_type->tp_data.tp_composite_type &&
- content->ct_type->tp_data.tp_composite_type->ct_type ==
- MAILMIME_COMPOSITE_TYPE_MULTIPART) {
- if (subtype)
- return content->ct_subtype &&
- strcasecmp(content->ct_subtype, subtype) == 0;
- else
- return true;
- }
-
- return false;
- }
-
- bool _is_PGP_MIME(struct mailmime_content *content)
- {
- assert(content);
-
- if (_is_multipart(content, "encrypted") &&
- parameter_has_value(content, "protocol",
- "application/pgp-encrypted"))
- return true;
-
- return false;
- }
-
- bool _is_text_part(struct mailmime_content *content, const char *subtype)
- {
- assert(content);
-
- if (content->ct_type && content->ct_type->tp_type ==
- MAILMIME_TYPE_DISCRETE_TYPE &&
- content->ct_type->tp_data.tp_discrete_type &&
- content->ct_type->tp_data.tp_discrete_type->dt_type ==
- MAILMIME_DISCRETE_TYPE_TEXT) {
- if (subtype)
- return content->ct_subtype &&
- strcasecmp(content->ct_subtype, subtype) == 0;
- else
- return true;
- }
-
- return false;
- }
-
- /**
- * @internal
- *
- * <!-- _is_message_part() -->
- *
- * @brief TODO
- *
- * @param[in] *content structmailmime_content
- * @param[in] *subtype constchar
- *
- */
- bool _is_message_part(struct mailmime_content *content, const char* subtype) {
- assert(content);
- if (content->ct_type && content->ct_type->tp_type == MAILMIME_TYPE_COMPOSITE_TYPE &&
- content->ct_type->tp_data.tp_composite_type &&
- content->ct_type->tp_data.tp_composite_type->ct_type ==
- MAILMIME_COMPOSITE_TYPE_MESSAGE) {
- if (subtype)
- return content->ct_subtype &&
- strcasecmp(content->ct_subtype, subtype) == 0;
- else
- return true;
- }
-
- return false;
- }
-
- int _get_content_type(
- const struct mailmime_content *content,
- char **type,
- char **charset
- )
- {
- char *_type = NULL;
- char *_charset = NULL;
-
- assert(content);
- assert(type);
- assert(charset);
-
- *type = NULL;
- *charset = NULL;
-
- if (content->ct_subtype == NULL)
- return EINVAL;
-
- if (content->ct_type && content->ct_type->tp_data.tp_discrete_type) {
- size_t len;
- const char *_main_type;
-
- switch (content->ct_type->tp_data.tp_discrete_type->dt_type) {
- case MAILMIME_DISCRETE_TYPE_TEXT:
- _main_type = (content->ct_subtype &&
- strcasecmp(content->ct_subtype, "rfc822") == 0 ?
- "message" : "text");
- break;
- case MAILMIME_DISCRETE_TYPE_IMAGE:
- _main_type = "image";
- break;
- case MAILMIME_DISCRETE_TYPE_AUDIO:
- _main_type = "audio";
- break;
- case MAILMIME_DISCRETE_TYPE_VIDEO:
- _main_type = "video";
- break;
- case MAILMIME_DISCRETE_TYPE_APPLICATION:
- _main_type = "application";
- break;
- case MAILMIME_DISCRETE_TYPE_EXTENSION:
- _main_type = "extension";
- break;
- default:
- return EINVAL;
- }
-
- len = strlen(_main_type) + 1 + strlen(content->ct_subtype) + 1;
- _type = calloc(1, len);
- assert(_type);
- if (_type == NULL)
- return ENOMEM;
-
- strncpy(_type, _main_type, len);
- len -= strlen(_main_type);
- strncat(_type, "/", len--);
- strncat(_type, content->ct_subtype, len);
-
- if (content->ct_parameters) {
- clistiter *cur;
- for (cur = clist_begin(content->ct_parameters); cur; cur =
- clist_next(cur)) {
- struct mailmime_parameter * param = clist_content(cur);
- if (param && param->pa_name && strcasecmp(param->pa_name,
- "charset") == 0) {
- _charset = param->pa_value;
- break;
- }
- }
- if (_charset)
- *charset = strdup(_charset);
- }
-
- *type = _type;
- return 0;
- }
-
- return EINVAL;
- }
-
- // Only for null-terminated field strings.
- // can this field be transported as is without modification?)
- // (See rfc2822, section 2.2.3 - libetpan's handling isn't quite what
- // we need here.)
- bool must_field_value_be_encoded(const char* field_value) {
- if (!field_value)
- return false;
-
- return must_chunk_be_encoded((const void*)field_value, strlen(field_value), false);
- }
-
- bool must_chunk_be_encoded(const void* value, size_t size, bool ignore_fws) {
-
- const char* begin_ptr = (const char*)value;
-
- const char* end_ptr = begin_ptr + size;
-
- const char* cur_char_ptr = begin_ptr;
- while (cur_char_ptr < end_ptr) {
- char cur_char = *cur_char_ptr;
- if (cur_char > 127 || cur_char < 0)
- return true;
- // FIXME - do we need to deal with CRCRLF here?
- // I guess in the worst case, it gets encoded, which
- // is *supposed* to be harmless...
- if (!ignore_fws) {
- if (cur_char == '\r') {
- const char* next = cur_char_ptr + 1;
- const char* nextnext = next + 1;
- if (next >= end_ptr || nextnext >= end_ptr
- || *next != '\n'
- || (*nextnext != ' ' && *nextnext != '\t')) {
- return true;
- }
- }
- else if (cur_char == '\n') {
- const char* prev = cur_char_ptr - 1;
- if (prev == begin_ptr || *prev != '\r')
- return true;
- }
- }
- cur_char_ptr++;
- }
-
- return ascii_exceeds_line_length(value, size);
- }
-
- #define TMP_TEMPLATE "pEp.XXXXXXXXXXXXXXXXXXXX"
- #ifdef _WIN32
- #define PATH_SEP '\\'
- #else
- #define PATH_SEP '/'
- #endif
-
- static PEP_STATUS interpret_MIME(struct mailmime *mime,
- message *msg,
- bool* has_possible_pEp_msg);
-
- // This function was rewritten to use in-memory buffers instead of
- // temporary files when the pgp/mime support was implemented for
- // outlook, as the existing code did not work well on windows.
-
- /**
- * @internal
- *
- * <!-- render_mime() -->
- *
- * @brief TODO
- *
- * @param[in] *mime structmailmime
- * @param[in] **mimetext char
- *
- * @retval PEP_STATUS_OK
- * @retval PEP_OUT_OF_MEMORY out of memory
- * @retval any other value on error
- */
- static PEP_STATUS render_mime(struct mailmime *mime, char **mimetext)
- {
- PEP_STATUS status = PEP_STATUS_OK;
- int col;
- int r;
- size_t len;
- char* buf = NULL;
-
- MMAPString* buffer;
-
- buffer = mmap_string_new(NULL);
- if (buffer == NULL)
- goto enomem;
-
- col = 0;
- r = mailmime_write_mem(buffer, &col, mime);
- assert(r == MAILIMF_NO_ERROR);
- if (r == MAILIMF_ERROR_MEMORY)
- goto enomem;
- else if (r != MAILIMF_NO_ERROR)
- goto err_file;
-
- // we overallocate by 1 byte, so we have a terminating 0.
- len = buffer->len;
- buf = calloc(len + 1, 1);
- if (buf == NULL)
- goto enomem;
-
- memcpy(buf, buffer->str, len);
- mmap_string_free(buffer);
-
- *mimetext = buf;
- return PEP_STATUS_OK;
-
- err_file:
- status = PEP_CANNOT_CREATE_TEMP_FILE;
- goto pEp_error;
-
- enomem:
- status = PEP_OUT_OF_MEMORY;
-
- pEp_error:
- if (buffer)
- mmap_string_free(buffer);
- if (buf)
- free(buf);
- return status;
- }
-
- /**
- * @internal
- *
- * <!-- mime_attachment() -->
- *
- * @brief TODO
- *
- * @param[in] *blob bloblist_t
- * @param[in] **result structmailmime
- * @param[in] is_nf_message_attachment bool
- *
- * @retval PEP_STATUS_OK
- * @retval PEP_OUT_OF_MEMORY out of memory
- * @retval any other value on error
- *
- */
- static PEP_STATUS mime_attachment(
- bloblist_t *blob,
- struct mailmime **result,
- bool is_nf_message_attachment // non-forwarded msg as att
- )
- {
- PEP_STATUS status = PEP_STATUS_OK;
- struct mailmime * mime = NULL;
- char * mime_type;
- assert(blob);
- assert(result);
-
- *result = NULL;
-
- // TODO: It seems the pEp COM server adapter sends an empty string here,
- // which leads to a crash later. Thus, we workaround here by treating an
- // empty string as NULL. We need to check whether the bug really is here,
- // or the pEp COM server adapter needs to be changed.
- if (blob->mime_type == NULL || blob->mime_type[0] == '\0')
- mime_type = "application/octet-stream";
- else
- mime_type = blob->mime_type;
-
- pEp_rid_list_t* resource = parse_uri(blob->filename);
-
- mime = get_file_part(resource, mime_type, blob->value, blob->size,
- is_nf_message_attachment);
- free_rid_list(resource);
-
- assert(mime);
- if (mime == NULL)
- goto enomem;
-
- *result = mime;
- return PEP_STATUS_OK;
-
- enomem:
- status = PEP_OUT_OF_MEMORY;
-
- if (mime)
- mailmime_free(mime);
-
- return status;
- }
-
-
- // This ONLY deals with handling the body
- // content when html parts are present - thus,
- // text/plain and text/html of the body, and
- // related inline attachments for the html
- // part. Non-inline attachments are handled
- // outside this call!!!!
- //
- // N.B. As a result, this will only touch the
- // "contained message" of pEp 2.x messages
- // on the initial encoding where it is turned
- // into attachment data!!
- /**
- * @internal
- *
- * <!-- mime_html_text() -->
- *
- * @brief TODO
- *
- * @param[in] *plaintext constchar
- * @param[in] *htmltext constchar
- * @param[in] *attachments bloblist_t
- * @param[in] **result structmailmime
- *
- * @retval PEP_STATUS_OK
- * @retval PEP_OUT_OF_MEMORY out of memory
- * @retval any other value on error
- */
- static PEP_STATUS mime_html_text(
- const char *plaintext,
- const char *htmltext,
- bloblist_t *attachments,
- struct mailmime **result
- )
- {
- PEP_STATUS status = PEP_STATUS_OK;
- struct mailmime * top_level_html_mime = NULL;
- struct mailmime * mime = NULL;
- struct mailmime * submime = NULL;
- int r;
-
- assert(plaintext);
- assert(htmltext);
- assert(result);
-
- *result = NULL;
-
- pEp_rid_list_t* resource = NULL;
-
- bool already_ascii = false;
- int encoding_type = 0;
- if (*plaintext != '\0') {
- mime = part_multiple_new("multipart/alternative");
- assert(mime);
- if (mime == NULL)
- goto enomem;
-
- // KB: pEpMIME transition comment - if we start getting
- // underencoding errors here, the change to checking
- // for ASCII and then encoding - or not - is one place
- // to start looking.
- int pt_length = strlen(plaintext);
- already_ascii = !(must_chunk_be_encoded(plaintext, pt_length, true));
- encoding_type = (already_ascii ? 0 : MAILMIME_MECHANISM_QUOTED_PRINTABLE);
-
- submime = get_text_part(NULL, "text/plain", plaintext,
- pt_length,
- encoding_type);
-
- // reset
- already_ascii = false;
- encoding_type = 0;
-
- free_rid_list(resource);
- resource = NULL;
-
- assert(submime);
- if (submime == NULL)
- goto enomem;
-
- r = mailmime_smart_add_part(mime, submime);
- assert(r == MAILIMF_NO_ERROR);
- if (r == MAILIMF_ERROR_MEMORY) {
- goto enomem;
- }
- else {
- // mailmime_smart_add_part() takes ownership of submime
- submime = NULL;
- }
- }
-
- bool inlined_attachments = false;
-
- bloblist_t* traversal_ptr = attachments;
-
- while (traversal_ptr) {
- if (traversal_ptr->disposition == PEP_CONTENT_DISP_INLINE) {
- inlined_attachments = true;
- break;
- }
- traversal_ptr = traversal_ptr->next;
- }
-
- if (inlined_attachments) {
- /* Noooooo... dirk, why do you do this to me? */
- submime = part_multiple_new("multipart/related");
- assert(submime);
- if (submime == NULL)
- goto enomem;
-
- // This is where all of the html MIME stuff will go
- top_level_html_mime = submime;
-
- if (!mime)
- mime = top_level_html_mime;
- else {
- r = mailmime_smart_add_part(mime, top_level_html_mime);
- assert(r == MAILIMF_NO_ERROR);
- if (r == MAILIMF_ERROR_MEMORY) {
- goto enomem;
- }
- else {
- // mailmime_smart_add_part() takes ownership of submime
- submime = NULL;
- }
- }
- }
- else {
- // Otherwise, html MIME stuff gets added to the top node
- // - may be NULL if there's no multipart!
- top_level_html_mime = mime;
- }
-
- // resource = new_rid_node(PEP_RID_FILENAME, "msg.html");
- int ht_length = strlen(htmltext);
- already_ascii = !(must_chunk_be_encoded(htmltext, ht_length, true));
- encoding_type = (already_ascii ? 0 : MAILMIME_MECHANISM_QUOTED_PRINTABLE);
-
- submime = get_text_part(NULL, "text/html", htmltext,
- ht_length,
- encoding_type);
-
- free_rid_list(resource);
- resource = NULL;
-
- assert(submime);
- if (submime == NULL)
- goto enomem;
-
- // IF there are no inlined attachments AND mime is NULL, then
- // we just have an HTML body here and won't need to
- // process inlined attachments - submime will actually be
- // the mime root of from this function, at least.
-
- if (!top_level_html_mime) {
- mime = submime;
- submime = NULL;
- }
- else {
- r = mailmime_smart_add_part(top_level_html_mime, submime);
- assert(r == MAILIMF_NO_ERROR);
- if (r == MAILIMF_ERROR_MEMORY)
- goto enomem;
- else {
- // mailmime_smart_add_part() takes ownership of submime
- submime = NULL;
- }
-
- bloblist_t *_a;
-
- // This will never have an embedded pEp message attachment
- // sent for encoding here, so we don't need to pass down
- // "(don't) transport encode this" info. If it's here and
- // it's not an ASCII "text/*" attachment, it'll get encoded
- for (_a = attachments; _a != NULL; _a = _a->next) {
- if (_a->disposition != PEP_CONTENT_DISP_INLINE)
- continue;
- status = mime_attachment(_a, &submime, false);
- if (status != PEP_STATUS_OK)
- return PEP_UNKNOWN_ERROR; // FIXME
-
- r = mailmime_smart_add_part(top_level_html_mime, submime);
- assert(r == MAILIMF_NO_ERROR);
- if (r == MAILIMF_ERROR_MEMORY) {
- goto enomem;
- }
- else {
- // mailmime_smart_add_part() takes ownership of submime
- submime = NULL;
- }
- }
- }
- *result = mime;
- return PEP_STATUS_OK;
-
- enomem:
- status = PEP_OUT_OF_MEMORY;
-
- if (mime)
- mailmime_free(mime);
-
- if (submime)
- mailmime_free(submime);
-
- return status;
- }
-
-
- /**
- * @internal
- *
- * <!-- identity_to_mailbox() -->
- *
- * @brief TODO
- *
- * @param[in] *ident constpEp_identity
- *
- */
- static struct mailimf_mailbox * identity_to_mailbox(const pEp_identity *ident)
- {
- char *_username = NULL;
- struct mailimf_mailbox *mb;
-
- if (!ident->username)
- _username = strdup("");
- else
- _username = must_field_value_be_encoded(ident->username) ?
- mailmime_encode_subject_header("utf-8", ident->username, 0) :
- strdup(ident->username);
-
- assert(_username);
- if (_username == NULL)
- goto enomem;
-
- mb = mailbox_from_string(_username, ident->address);
- if (mb == NULL)
- goto enomem;
-
- free(_username);
- _username = NULL;
-
- return mb;
-
- enomem:
- free(_username);
- return NULL;
- }
-
- /**
- * @internal
- *
- * <!-- identity_to_mbl() -->
- *
- * @brief TODO
- *
- * @param[in] *ident constpEp_identity
- *
- */
- static struct mailimf_mailbox_list * identity_to_mbl(
- const pEp_identity *ident)
- {
- struct mailimf_mailbox_list *mbl = NULL;
- struct mailimf_mailbox *mb = NULL;
- clist *list = NULL;
- int r;
-
- assert(ident);
-
- list = clist_new();
- if (list == NULL)
- goto enomem;
-
- mb = identity_to_mailbox(ident);
- if (mb == NULL)
- goto enomem;
-
- r = clist_append(list, mb);
- if (r)
- goto enomem;
-
- mbl = mailimf_mailbox_list_new(list);
- if (mbl == NULL)
- goto enomem;
-
- return mbl;
-
- enomem:
- if (mb)
- mailimf_mailbox_free(mb);
-
- if (list)
- clist_free(list);
-
- return NULL;
- }
-
- /**
- * @internal
- *
- * <!-- identity_list_to_mal() -->
- *
- * @brief TODO
- *
- * @param[in] *il identity_list
- *
- */
- static struct mailimf_address_list * identity_list_to_mal(identity_list *il)
- {
- struct mailimf_address_list *mal = NULL;
- struct mailimf_mailbox *mb = NULL;
- struct mailimf_address * addr = NULL;
- clist *list = NULL;
- int r;
-
- assert(il);
-
- list = clist_new();
- if (list == NULL)
- goto enomem;
-
- identity_list *_il;
- for (_il = il; _il && _il->ident; _il = _il->next) {
- mb = identity_to_mailbox(_il->ident);
- if (mb == NULL)
- goto enomem;
-
- addr = mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mb, NULL);
- if (addr == NULL)
- goto enomem;
- mb = NULL;
-
- r = clist_append(list, addr);
- if (r)
- goto enomem;
- addr = NULL;
- }
- mal = mailimf_address_list_new(list);
- if (mal == NULL)
- goto enomem;
-
- return mal;
-
- enomem:
- if (mb)
- mailimf_mailbox_free(mb);
-
- if (addr)
- mailimf_address_free(addr);
-
- if (list)
- clist_free(list);
-
- return NULL;
- }
-
- // KB: This seems to be always called with "true",
- // but there was probably a reason for this. So
- // leave it for now.
- /**
- * @internal
- *
- * <!-- stringlist_to_clist() -->
- *
- * @brief TODO
- *
- * @param[in] *sl stringlist_t
- * @param[in] transport_encode bool
- *
- */
- static clist * stringlist_to_clist(stringlist_t *sl, bool transport_encode)
- {
- clist * cl = clist_new();
- assert(cl);
- if (cl == NULL)
- return NULL;
-
- if (!sl || ((!sl->value || sl->value[0] == '\0') && sl->next == NULL))
- return cl;
-
- stringlist_t *_sl;
- for (_sl = sl; _sl; _sl = _sl->next) {
- int r;
- char * value = ((transport_encode && must_field_value_be_encoded(_sl->value)) ?
- mailmime_encode_subject_header("utf-8", _sl->value, 0) :
- strdup(_sl->value));
- assert(value);
- if (value == NULL) {
- clist_free(cl);
- return NULL;
- }
- r = clist_append(cl, value);
- assert(r == 0);
- if (r) {
- free(value);
- clist_free(cl);
- return NULL;
- }
- }
-
- return cl;
- }
-
- /**
- * @internal
- *
- * <!-- build_fields() -->
- *
- * @brief TODO
- *
- * @param[in] *msg constmessage
- * @param[in] **result structmailimf_fields
- *
- * @retval PEP_STATUS_OK
- * @retval PEP_OUT_OF_MEMORY out of memory
- * @retval any other value on error
- */
- static PEP_STATUS build_fields(const message *msg, struct mailimf_fields **result)
- {
- PEP_STATUS status = PEP_STATUS_OK;
- struct mailimf_fields * fields = NULL;
- int r;
- clist * fields_list = NULL;
- unsigned char pEpstr[] = PEP_SUBJ_STRING; // unsigned due to UTF-8 byte fun
- #ifdef WIN32
- char* altstr = "pEp";
- #else
- char* altstr = (char*)pEpstr;
- #endif
- char *subject = msg->shortmsg && msg->shortmsg[0] ? msg->shortmsg : altstr;
-
- assert(msg);
- assert(result);
-
- *result = NULL;
-
- fields_list = clist_new();
- assert(fields_list);
- if (fields_list == NULL)
- goto enomem;
-
- if (msg->id && msg->id[0]) {
- char *_msgid = strdup(msg->id);
- assert(_msgid);
- if (_msgid == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_MESSAGE_ID,
- (_new_func_t) mailimf_message_id_new, _msgid);
- if (r) {
- free(_msgid);
- goto enomem;
- }
- }
-
- if (msg->sent) {
- struct mailimf_date_time * dt = timestamp_to_etpantime(msg->sent);
- if (dt == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_ORIG_DATE,
- (_new_func_t) mailimf_orig_date_new, dt);
- if (r) {
- mailimf_date_time_free(dt);
- goto enomem;
- }
- dt = NULL;
- }
-
- if (msg->from) {
- struct mailimf_mailbox_list *from = identity_to_mbl(msg->from);
- if (from == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_FROM,
- (_new_func_t) mailimf_from_new, from);
- if (r) {
- mailimf_mailbox_list_free(from);
- goto enomem;
- }
- }
-
- if (msg->to && msg->to->ident) {
- struct mailimf_address_list *to = identity_list_to_mal(msg->to);
- if (to == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_TO,
- (_new_func_t) mailimf_to_new, to);
- if (r) {
- mailimf_address_list_free(to);
- goto enomem;
- }
- }
-
- char* _subject = NULL;
- if (!must_field_value_be_encoded(subject)) {
- _subject = strdup(subject);
- assert(_subject);
- }
- else {
- _subject = mailmime_encode_subject_header("utf-8", subject, 1);
- }
- if (_subject == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_SUBJECT,
- (_new_func_t) mailimf_subject_new, _subject);
- if (r) {
- free(_subject);
- goto enomem;
- }
-
- if (msg->cc && msg->cc->ident) {
- struct mailimf_address_list *cc = identity_list_to_mal(msg->cc);
- if (cc == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_CC,
- (_new_func_t) mailimf_cc_new, cc);
- if (r) {
- mailimf_address_list_free(cc);
- goto enomem;
- }
- }
-
- if (msg->bcc && msg->bcc->ident) {
- struct mailimf_address_list *bcc = identity_list_to_mal(msg->bcc);
- if (bcc == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_BCC,
- (_new_func_t) mailimf_bcc_new, bcc);
- if (r) {
- mailimf_address_list_free(bcc);
- goto enomem;
- }
- }
-
- if (msg->reply_to && msg->reply_to->ident) {
- struct mailimf_address_list *reply_to = identity_list_to_mal(msg->reply_to);
- if (reply_to == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_REPLY_TO,
- (_new_func_t) mailimf_reply_to_new, reply_to);
- if (r) {
- mailimf_address_list_free(reply_to);
- goto enomem;
- }
- }
-
- if (msg->in_reply_to && msg->in_reply_to->value) {
- clist *in_reply_to = stringlist_to_clist(msg->in_reply_to, true);
- if (in_reply_to == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_IN_REPLY_TO,
- (_new_func_t) mailimf_in_reply_to_new, in_reply_to);
- if (r) {
- clist_free(in_reply_to);
- goto enomem;
- }
- }
-
- if (msg->references && msg->references->value) {
- clist *references = stringlist_to_clist(msg->references, true);
- if (references == NULL)
- goto enomem;
-
- r = _append_field(fields_list, MAILIMF_FIELD_REFERENCES,
- (_new_func_t) mailimf_references_new, references);
- if (
|