You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pEpEngine/src/etpan_mime.c

3295 lines
91 KiB
C

/**
* @internal
* @file etpan_mime.c
* @brief File description for doxygen missing. FIXME
* @license GNU General Public License 3.0 - see LICENSE.txt
*/
8 years ago
#include "etpan_mime.h"
#ifndef mailmime_param_new_with_data
#include <libetpan/mailprivacy_tools.h>
#endif
8 years ago
#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>
8 years ago
#include <errno.h>
8 years ago
#define MAX_MESSAGE_ID 128
8 years ago
#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
*
*
*/
8 years ago
static char * generate_boundary(void)
8 years ago
{
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();
8 years ago
8 years ago
snprintf(id, MAX_MESSAGE_ID, "%.4lx%.4lx%.4lx%.4lx", value1, value2,
value3, value4);
8 years ago
return strdup(id);
}
struct mailmime * part_new_empty(
struct mailmime_content * content,
struct mailmime_fields * mime_fields,
stringpair_list_t* param_keyvals,
8 years ago
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;
8 years ago
char *boundary = NULL;
8 years ago
list = NULL;
8 years ago
if (force_single) {
mime_type = MAILMIME_SINGLE;
}
else {
switch (content->ct_type->tp_type) {
case MAILMIME_TYPE_DISCRETE_TYPE:
mime_type = MAILMIME_SINGLE;
break;
8 years ago
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;
8 years ago
default:
goto enomem;
}
}
8 years ago
if (mime_type == MAILMIME_MULTIPLE) {
list = clist_new();
assert(list);
if (list == NULL)
goto enomem;
8 years ago
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;
8 years ago
param = mailmime_parameter_new(attr_name, attr_value);
assert(param);
if (param == NULL)
goto enomem;
attr_name = NULL;
attr_value = NULL;
8 years ago
if (content->ct_parameters == NULL) {
parameters = clist_new();
assert(parameters);
if (parameters == NULL)
goto enomem;
}
else {
parameters = content->ct_parameters;
}
8 years ago
r = clist_append(parameters, param);
if (r)
goto enomem;
param = NULL;
8 years ago
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;
}
}
8 years ago
build_info = mailmime_new(mime_type, NULL, 0, mime_fields, content, NULL,
NULL, NULL, list, NULL, NULL);
if (build_info == NULL)
goto enomem;
8 years ago
return build_info;
8 years ago
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;
8 years ago
}
8 years ago
struct mailmime * get_pgp_encrypted_part(void)
{
struct mailmime * mime = NULL;
struct mailmime_fields * mime_fields = NULL;
struct mailmime_content * content = NULL;
int r;
8 years ago
content = mailmime_content_new_with_str("application/pgp-encrypted");
if (content == NULL)
goto enomem;
8 years ago
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;
8 years ago
return mime;
enomem:
if (content)
mailmime_content_free(content);
if (mime_fields)
mailmime_fields_free(mime_fields);
if (mime)
mailmime_free(mime);
return NULL;
8 years ago
}
8 years ago
struct mailmime * get_text_part(
pEp_rid_list_t* resource,
8 years ago
const char * mime_type,
const char * text,
size_t length,
int encoding_type
)
{
8 years ago
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) {
8 years ago
encoding = mailmime_mechanism_new(encoding_type, NULL);
if (encoding == NULL)
goto enomem;
}
8 years ago
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;
8 years ago
content = mailmime_content_new_with_str(mime_type);
if (content == NULL)
goto enomem;
8 years ago
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;
8 years ago
}
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;
8 years ago
}
8 years ago
struct mailmime * get_file_part(
pEp_rid_list_t* resource,
8 years ago
const char * mime_type,
char * data,
size_t length,
bool is_nf_message_attachment // non-forwarded msg as att
8 years ago
)
{
char * disposition_name = NULL;
8 years ago
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;
}
8 years ago
}
8 years ago
content = mailmime_content_new_with_str(mime_type);
if (content == NULL)
goto enomem;
8 years ago
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;
}
8 years ago
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;
8 years ago
}
8 years ago
struct mailmime * part_multiple_new(const char *type)
8 years ago
{
struct mailmime_fields *mime_fields = NULL;
struct mailmime_content *content = NULL;
struct mailmime *mp = NULL;
8 years ago
mime_fields = mailmime_fields_new_empty();
if (mime_fields == NULL)
goto enomem;
8 years ago
content = mailmime_content_new_with_str(type);
if (content == NULL)
goto enomem;
8 years ago
mp = part_new_empty(content, mime_fields, NULL, 0);
8 years ago
if (mp == NULL)
goto enomem;
8 years ago
return mp;
enomem:
if (content)
mailmime_content_free(content);
if (mime_fields)
mailmime_fields_free(mime_fields);
8 years ago
return NULL;
}
8 years ago
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);
8 years ago
if (r)
8 years ago
_free_field(field);
return r;
}
8 years ago
// http://media2.giga.de/2014/02/Image-28.jpg
struct mailimf_date_time * timestamp_to_etpantime(const timestamp *ts)
8 years ago
{
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__
8 years ago
result->dt_zone = (int) (ts->tm_gmtoff / 36L);
#endif
8 years ago
return result;
}
timestamp * etpantime_to_timestamp(const struct mailimf_date_time *et)
8 years ago
{
timestamp * result = calloc(1, sizeof(timestamp));
8 years ago
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__
8 years ago
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
8 years ago
return result;
}
8 years ago
struct mailimf_mailbox * mailbox_from_string(
const char *name,
const char *address
)
{
3 years ago
assert(address);
if (!address)
return NULL;
8 years ago
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;
}
8 years ago
mb = mailimf_mailbox_new(_name, _address);
assert(mb);
if (mb == NULL)
goto enomem;
return mb;
enomem:
free(_name);
free(_address);
return NULL;
}
8 years ago
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);
8 years ago
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;
}
8 years ago
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)
8 years ago
{
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;
8 years ago
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 */
8 years ago
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)) {
8 years ago
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;
}
8 years ago
}
}
}
}
/* Will almost certainly usually be a singleton, but we need to be able to decide */
return rid_list;
}