...
 
Commits (15)
...@@ -128,6 +128,7 @@ src\low-level\mbox\mailmbox_types.h ...@@ -128,6 +128,7 @@ src\low-level\mbox\mailmbox_types.h
src\low-level\mh\mailmh.h src\low-level\mh\mailmh.h
src\low-level\mime\mailmime.h src\low-level\mime\mailmime.h
src\low-level\mime\mailmime_content.h src\low-level\mime\mailmime_content.h
src\low-level\mime\mailmime_encode.h
src\low-level\mime\mailmime_decode.h src\low-level\mime\mailmime_decode.h
src\low-level\mime\mailmime_disposition.h src\low-level\mime\mailmime_disposition.h
src\low-level\mime\mailmime_types.h src\low-level\mime\mailmime_types.h
......
...@@ -39,6 +39,11 @@ ...@@ -39,6 +39,11 @@
#include "mailprivacy_smime.h" #include "mailprivacy_smime.h"
#include <string.h> #include <string.h>
#if __APPLE__
#include <TargetConditionals.h>
#endif
#ifdef WIN32 #ifdef WIN32
# include "win_etpan.h" # include "win_etpan.h"
# define WEXITSTATUS(r) (r) # define WEXITSTATUS(r) (r)
...@@ -1542,6 +1547,12 @@ static int get_cert_from_sig(struct mailprivacy * privacy, ...@@ -1542,6 +1547,12 @@ static int get_cert_from_sig(struct mailprivacy * privacy,
int r; int r;
char command[PATH_MAX]; char command[PATH_MAX];
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
//https://github.com/dinhviethoa/libetpan/issues/275
//get_cert_from_sig is not needed on iOS
return MAIL_ERROR_COMMAND;
#endif
if (* cert_dir == '\0') if (* cert_dir == '\0')
return MAIL_ERROR_INVAL; return MAIL_ERROR_INVAL;
...@@ -1561,7 +1572,7 @@ static int get_cert_from_sig(struct mailprivacy * privacy, ...@@ -1561,7 +1572,7 @@ static int get_cert_from_sig(struct mailprivacy * privacy,
cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list); cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
if (cur == NULL) { if (cur == NULL) {
res = MAIL_ERROR_INVAL; res = MAIL_ERROR_INVAL;
goto err; goto err;
} }
signed_mime = cur->data; signed_mime = cur->data;
...@@ -1602,7 +1613,13 @@ static int get_cert_from_sig(struct mailprivacy * privacy, ...@@ -1602,7 +1613,13 @@ static int get_cert_from_sig(struct mailprivacy * privacy,
"openssl pkcs7 -inform DER -in '%s' -out '%s' -print_certs 2>/dev/null", "openssl pkcs7 -inform DER -in '%s' -out '%s' -print_certs 2>/dev/null",
quoted_signature_filename, quoted_store_cert_filename); quoted_signature_filename, quoted_store_cert_filename);
r = system(command); // N.B. This is only about compilation - we already don't execute this function with iPhone.
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
//https://github.com/dinhviethoa/libetpan/issues/275
//system() is not supported on iOS 11.
r = system(command);
#endif
if (WEXITSTATUS(r) != 0) { if (WEXITSTATUS(r) != 0) {
res = MAIL_ERROR_COMMAND; res = MAIL_ERROR_COMMAND;
goto unlink_signature; goto unlink_signature;
......
...@@ -40,6 +40,10 @@ ...@@ -40,6 +40,10 @@
#include "mailprivacy_tools.h" #include "mailprivacy_tools.h"
#include "mailprivacy_tools_private.h" #include "mailprivacy_tools_private.h"
#if __APPLE__
#include <TargetConditionals.h>
#endif
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
...@@ -1331,6 +1335,12 @@ int mailprivacy_spawn_and_wait(char * command, char * passphrase, ...@@ -1331,6 +1335,12 @@ int mailprivacy_spawn_and_wait(char * command, char * passphrase,
char * stdoutfile, char * stderrfile, char * stdoutfile, char * stderrfile,
int * bad_passphrase) int * bad_passphrase)
{ {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
//https://github.com/dinhviethoa/libetpan/issues/275
//mailprivacy_spawn_and_wait is not needed on iOS
return MAIL_ERROR_COMMAND;
#endif
#ifdef WIN32 #ifdef WIN32
int res; int res;
SECURITY_ATTRIBUTES sec_attr; SECURITY_ATTRIBUTES sec_attr;
...@@ -1517,11 +1527,16 @@ int mailprivacy_spawn_and_wait(char * command, char * passphrase, ...@@ -1517,11 +1527,16 @@ int mailprivacy_spawn_and_wait(char * command, char * passphrase,
Dup2(fd_err, 2); Dup2(fd_err, 2);
Close(fd_err); Close(fd_err);
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
//https://github.com/dinhviethoa/libetpan/issues/275
//system() is not supported on iOS 11.
// BUG: status may be -1 and errno may be EINTR if waitpid(2) was // BUG: status may be -1 and errno may be EINTR if waitpid(2) was
// interrupted by a signal; to handle that properly the PID of the // interrupted by a signal; to handle that properly the PID of the
// fork(2)ed child has to be determined and waitpid(2) has to be // fork(2)ed child has to be determined and waitpid(2) has to be
// called again // called again
status = system(command); status = system(command);
#endif
exit(WEXITSTATUS(status)); exit(WEXITSTATUS(status));
} }
......
...@@ -1170,7 +1170,7 @@ int mailimf_atom_parse(const char * message, size_t length, ...@@ -1170,7 +1170,7 @@ int mailimf_atom_parse(const char * message, size_t length,
} }
LIBETPAN_EXPORT LIBETPAN_EXPORT
static int mailimf_fws_atom_for_word_parse(const char * message, size_t length, int mailimf_fws_atom_for_word_parse(const char * message, size_t length,
size_t * indx, char ** result, int * p_missing_closing_quote) size_t * indx, char ** result, int * p_missing_closing_quote)
{ {
size_t end; size_t end;
...@@ -3029,7 +3029,8 @@ static int mailimf_group_parse(const char * message, size_t length, ...@@ -3029,7 +3029,8 @@ static int mailimf_group_parse(const char * message, size_t length,
struct mailimf_group * group; struct mailimf_group * group;
int r; int r;
int res; int res;
clist * list;
cur_token = * indx; cur_token = * indx;
mailbox_list = NULL; mailbox_list = NULL;
...@@ -3056,6 +3057,17 @@ static int mailimf_group_parse(const char * message, size_t length, ...@@ -3056,6 +3057,17 @@ static int mailimf_group_parse(const char * message, size_t length,
res = r; res = r;
goto free_display_name; goto free_display_name;
} }
list = clist_new();
if (list == NULL) {
res = MAILIMF_ERROR_MEMORY;
goto free_display_name;
}
mailbox_list = mailimf_mailbox_list_new(list);
if (mailbox_list == NULL) {
res = MAILIMF_ERROR_MEMORY;
clist_free(list);
goto free_display_name;
}
break; break;
default: default:
res = r; res = r;
...@@ -7773,52 +7785,3 @@ mailimf_optional_fields_parse(const char * message, size_t length, ...@@ -7773,52 +7785,3 @@ mailimf_optional_fields_parse(const char * message, size_t length,
return res; return res;
} }
LIBETPAN_EXPORT
int
mailimf_memoryhole_fields_parse(const char * message, size_t length,
size_t * indx,
struct mailimf_fields ** result)
{
size_t cur_token;
clist * list;
struct mailimf_fields * fields;
int r = MAILIMF_ERROR_PARSE;
int res;
cur_token = * indx;
list = NULL;
const char* rfc822_header_string = "Content-Type: text/rfc822-headers;";
const size_t rfc822_header_strlen = 34;
const char* cur_char = message + cur_token;
if ((cur_token + rfc822_header_strlen) <= length &&
strncmp(cur_char, rfc822_header_string, rfc822_header_strlen) == 0) {
r = mailimf_envelope_fields_parse(message, length, &cur_token,
result);
}
switch (r) {
case MAILIMF_NO_ERROR:
/* do nothing */
break;
default:
res = r;
goto err;
}
* indx = cur_token;
return MAILIMF_NO_ERROR;
free:
if (list != NULL) {
clist_foreach(list, (clist_func) mailimf_field_free, NULL);
clist_free(list);
}
err:
return res;
}
...@@ -342,6 +342,10 @@ LIBETPAN_EXPORT ...@@ -342,6 +342,10 @@ LIBETPAN_EXPORT
int mailimf_atom_parse(const char * message, size_t length, int mailimf_atom_parse(const char * message, size_t length,
size_t * indx, char ** result); size_t * indx, char ** result);
LIBETPAN_EXPORT
int mailimf_fws_atom_for_word_parse(const char * message, size_t length,
size_t * indx, char ** result, int * p_missing_closing_quote);
LIBETPAN_EXPORT LIBETPAN_EXPORT
int mailimf_fws_atom_parse(const char * message, size_t length, int mailimf_fws_atom_parse(const char * message, size_t length,
size_t * indx, char ** result); size_t * indx, char ** result);
......
...@@ -189,6 +189,139 @@ mailmime_composite_type_parse(const char * message, size_t length, ...@@ -189,6 +189,139 @@ mailmime_composite_type_parse(const char * message, size_t length,
return res; return res;
} }
void
hex_to_byte(char* retval_byte, const char* hex_bytes) {
*retval_byte = 0;
char curr_char = hex_bytes[0];
if (isdigit(curr_char))
*retval_byte |= curr_char - '0';
else {
curr_char = tolower(curr_char);
if (curr_char >= 'a' && curr_char <= 'f') {
*retval_byte |= (curr_char - 'a') + 10;
}
else {
*retval_byte = 0;
return;
}
}
*retval_byte <<= 4;
curr_char = hex_bytes[1];
if (isdigit(curr_char))
*retval_byte |= curr_char - '0';
else {
curr_char = tolower(curr_char);
if (curr_char >= 'a' && curr_char <= 'f') {
*retval_byte |= (curr_char - 'a') + 10;
}
else {
*retval_byte = 0;
return;
}
}
}
void
byte_to_hex(char* upper_hex_value, char* lower_hex_value, char byte) {
if (!upper_hex_value || !lower_hex_value) {
*upper_hex_value = 'F';
*lower_hex_value = 'F';
return;
}
unsigned char lower_byte = (unsigned char)byte & 0xF;
unsigned char upper_byte = (unsigned char)((byte >> 4) & 0xF);
*lower_hex_value = ((lower_byte < 10) ? ('0' + lower_byte) : 'A' + (lower_byte - 10));
*upper_hex_value = ((upper_byte < 10) ? ('0' + upper_byte) : 'A' + (upper_byte - 10));
}
// Required by RFC2231 - src is always a utf-8 string in our case.
LIBETPAN_EXPORT
void mailmime_parm_value_escape(char** dst, const char* src) {
if (!src || !dst)
return;
*dst = NULL;
int number_of_octets = strlen(src);
if (number_of_octets < 1)
return;
const char* ESCAPED_ENCODING_PREFIX = "utf-8''";
const int ESCAPED_ENCODING_PREFIX_LENGTH = 7;
size_t retval_len = ESCAPED_ENCODING_PREFIX_LENGTH + (number_of_octets * 3);
char* unbroken_string = calloc(retval_len + 1, 1); // 8 = utf-8'' + \0
strncpy(unbroken_string, ESCAPED_ENCODING_PREFIX, retval_len);
char* srcend = src + number_of_octets;
char* curr_src_ptr = src;
char* curr_dst_ptr = unbroken_string + ESCAPED_ENCODING_PREFIX_LENGTH;
while (curr_src_ptr < srcend) {
char upper = 0;
char lower = 0;
byte_to_hex(&upper, &lower, *curr_src_ptr);
// detect FF? Is FF even possible? Leave it for now.
*curr_dst_ptr++ = '%';
*curr_dst_ptr++ = upper;
*curr_dst_ptr++ = lower;
curr_src_ptr++;
}
*dst = unbroken_string; // splitting is the caller's responsibility
}
// Required by RFC2231
LIBETPAN_EXPORT
void mailmime_parm_value_unescape(char** dst, const char* src) {
*dst = NULL;
int percent_count = 0;
size_t srclen = strlen(src);
const char* srcpointer = src;
const char* end = src + srclen;
while (srcpointer && srcpointer < end) {
srcpointer = (strstr(srcpointer, "%"));
if (srcpointer) {
percent_count++;
srcpointer++;
}
}
if (percent_count) {
size_t new_len = srclen + percent_count; // - 1 byte for %, + 2 bytes for 2nd hex digit
char* retstr = (char*)calloc(new_len + 1, 1);
char* dstpointer = retstr;
srcpointer = src;
while (*srcpointer && srcpointer < end) {
if (*srcpointer != '%') {
*dstpointer = *srcpointer;
dstpointer++;
srcpointer++;
}
else {
srcpointer++;
if (!(*srcpointer) || (srcpointer + 1) >= end) {
// Badness! Stop!
free(retstr);
return;
}
hex_to_byte(dstpointer, srcpointer);
if (*dstpointer == 0) {
free(retstr);
return;
}
dstpointer++;
srcpointer += 2;
}
}
*dst = retstr;
}
}
/* /*
x content := "Content-Type" ":" type "/" subtype x content := "Content-Type" ":" type "/" subtype
*(";" parameter) *(";" parameter)
...@@ -1291,6 +1424,69 @@ static int mailmime_type_parse(const char * message, size_t length, ...@@ -1291,6 +1424,69 @@ static int mailmime_type_parse(const char * message, size_t length,
return res; return res;
} }
/*
x extended-initial-value := [charset] "'" [language] "'"
x extended-other-values
*/
LIBETPAN_EXPORT
int mailmime_extended_initial_value_parse(const char * message, size_t length,
size_t * indx, char ** result, char** charset, char** language)
{
int r;
char* value = NULL;
size_t value_length = 0;
size_t cur_token = * indx;
r = mailimf_atom_parse(message, length, &cur_token, &value);
if (r != MAILIMF_NO_ERROR)
return r;
if (value)
value_length = strlen(value);
// ok, let's see what happens here...
char* end_charset = strstr(value, "'");
if (end_charset == NULL || (value + value_length <= end_charset + 1)) {
free(value);
return MAILIMF_ERROR_PARSE;
}
char* end_lang = strstr(end_charset + 1, "'");
if (end_lang == NULL || (value + value_length < end_lang)) { // could be empty after
free(value);
return MAILIMF_ERROR_PARSE;
}
size_t charset_len = end_charset - value;
size_t lang_len = end_lang - (end_charset + 1);
size_t retval_len = strlen(value) - (charset_len + lang_len + 2);
char* _charset = calloc(charset_len + 1, 1);
char* _lang = calloc(lang_len + 1, 1);
char* _value = calloc(retval_len + 1, 1);
if (charset_len > 0) {
strncpy(_charset, value, charset_len);
}
if (lang_len > 0) {
strncpy(_lang, end_charset + 1, lang_len);
}
if (retval_len > 0) {
strncpy(_value, end_lang + 1, retval_len);
}
free(value);
* result = _value;
* charset = _charset;
* language = _lang;
* indx = cur_token;
return MAILIMF_NO_ERROR;
}
/* /*
x value := token / quoted-string x value := token / quoted-string
*/ */
......
...@@ -112,10 +112,19 @@ int mailmime_language_parse(const char * message, size_t length, ...@@ -112,10 +112,19 @@ int mailmime_language_parse(const char * message, size_t length,
struct mailmime_language ** result); struct mailmime_language ** result);
LIBETPAN_EXPORT LIBETPAN_EXPORT
int int mailmime_extended_initial_value_parse(const char * message, size_t length,
mailimf_memoryhole_fields_parse(const char * message, size_t length, size_t * indx, char ** result, char** charset, char** language);
size_t * indx,
struct mailimf_fields ** result);
void hex_to_byte(char* retval_byte, const char* hex_bytes);
void byte_to_hex(char* upper_hex_value, char* lower_hex_value, char byte);
// Required by RFC2231
LIBETPAN_EXPORT
void mailmime_parm_value_unescape(char** dst, const char* src);
LIBETPAN_EXPORT
void mailmime_parm_value_escape(char** dst, const char* src);
#ifdef __cplusplus #ifdef __cplusplus
} }
......
...@@ -831,7 +831,6 @@ int mailmime_multipart_next_parse(const char * message, size_t length, ...@@ -831,7 +831,6 @@ int mailmime_multipart_next_parse(const char * message, size_t length,
return MAILIMF_NO_ERROR; return MAILIMF_NO_ERROR;
} }
/* N.B. Modified to allow add enigmail memoryhole Subject header to fields. */
static int static int
mailmime_multipart_body_parse(const char * message, size_t length, mailmime_multipart_body_parse(const char * message, size_t length,
size_t * indx, char * boundary, size_t * indx, char * boundary,
...@@ -967,38 +966,25 @@ mailmime_multipart_body_parse(const char * message, size_t length, ...@@ -967,38 +966,25 @@ mailmime_multipart_body_parse(const char * message, size_t length,
if (r == MAILIMF_NO_ERROR) { if (r == MAILIMF_NO_ERROR) {
bp_token = 0; bp_token = 0;
struct mailimf_fields * envelope_fields = NULL; r = mailimf_optional_fields_parse(data_str, data_size,
int is_memoryhole_part = 0;
r = mailimf_memoryhole_fields_parse(data_str, data_size,
&bp_token, &fields); &bp_token, &fields);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
if (r == MAILIMF_NO_ERROR) { res = r;
is_memoryhole_part = 1; goto free;
} }
else {
r = mailimf_optional_fields_parse(data_str, data_size, r = mailimf_crlf_parse(data_str, data_size, &bp_token);
&bp_token, &fields); if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) { mailimf_fields_free(fields);
res = r; res = r;
goto free; goto free;
} }
r = mailimf_crlf_parse(data_str, data_size, &bp_token);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
mailimf_fields_free(fields);
res = r;
goto free;
}
}
mime_fields = NULL; mime_fields = NULL;
r = mailmime_fields_parse(fields, &mime_fields); r = mailmime_fields_parse(fields, &mime_fields);
/* mailimf_fields_free(fields); */ /* we don't do this now to preserve for memoryhole */ mailimf_fields_free(fields);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) { if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
res = r; res = r;
...@@ -1026,8 +1012,6 @@ mailmime_multipart_body_parse(const char * message, size_t length, ...@@ -1026,8 +1012,6 @@ mailmime_multipart_body_parse(const char * message, size_t length,
goto free; goto free;
} }
mime_bp->mm_imf_fields = fields;
r = mailmime_multipart_next_parse(message, length, &cur_token); r = mailmime_multipart_next_parse(message, length, &cur_token);
if (r == MAILIMF_NO_ERROR) { if (r == MAILIMF_NO_ERROR) {
/* do nothing */ /* do nothing */
...@@ -2233,4 +2217,4 @@ int mailmime_get_section_id(struct mailmime * mime, ...@@ -2233,4 +2217,4 @@ int mailmime_get_section_id(struct mailmime * mime,
mailmime_section_free(section_id); mailmime_section_free(section_id);
err: err:
return res; return res;
} }
\ No newline at end of file
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "mailmime_disposition.h" #include "mailmime_disposition.h"
#include "mailmime.h" #include "mailmime.h"
#include "charconv.h"
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -422,6 +423,225 @@ mailmime_disposition_parm_parse(const char * message, size_t length, ...@@ -422,6 +423,225 @@ mailmime_disposition_parm_parse(const char * message, size_t length,
return res; return res;
} }
/*
// filename-parm := "filename" "=" value
*/
static int
mailmime_extended_parm_parse(const char * message, size_t length,
char * key, size_t * indx, char ** result)
{
int r;
size_t cur_token;
char* built_str = NULL;
size_t built_len = 0;
cur_token = * indx;
r = mailimf_token_case_insensitive_parse(message, length,
&cur_token, key);
if (r != MAILIMF_NO_ERROR)
return r;
// Ok, we know it's of this type.
// So let's see if it's encoded or extended or both
int encoded = 0;
int extended = 0;
// Find out if message is extended, encoded, or both
r = mailimf_char_parse(message, length, &cur_token, '*');
if (r == MAILIMF_NO_ERROR) {
r = mailimf_char_parse(message, length, &cur_token, '0');
if (r == MAILIMF_NO_ERROR) {
extended = 1;
r = mailimf_char_parse(message, length, &cur_token, '*');
if (r == MAILIMF_NO_ERROR)
encoded = 1;
else if (r != MAILIMF_ERROR_PARSE)
return r;
}
else if (r != MAILIMF_ERROR_PARSE)
return r;
else
encoded = 1;
}
else //if (r != MAILIMF_ERROR_PARSE)
return r;
r = mailimf_unstrict_char_parse(message, length, &cur_token, '=');
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_cfws_parse(message, length, &cur_token);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
return r;
// Ok, let's go.
if (encoded || extended) {
char* _charset = NULL;
char* _lang = NULL;
// Get the first of either
if (encoded) {
r = mailmime_extended_initial_value_parse(message, length, &cur_token, &built_str, &_charset,
&_lang);
if (r != MAILIMF_NO_ERROR)
return r;
}
else if (extended) {
r = mailmime_value_parse(message, length, &cur_token, &built_str);
if (r != MAILIMF_NO_ERROR)
return r;
}
// Ok, we have an initial string and know it's extended, so let's roll.
if (extended && built_str) {
built_len = strlen(built_str);
while (1) {
r = mailimf_unstrict_char_parse(message, length, &cur_token, ';');
if (r != MAILIMF_NO_ERROR && r != MAILIMF_ERROR_PARSE)
return r;
r = mailimf_cfws_parse(message, length, &cur_token);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
return r;
// FIXME: this is where we have to check and see what really happens...
r = mailimf_token_case_insensitive_parse(message, length,
&cur_token, key);
if (r == MAILIMF_ERROR_PARSE)
break;
if (r != MAILIMF_NO_ERROR)
return r;
// Ok, we know it's of this type.
// So let's see if it's encoded or extended or both
// int part_encoded = 0;
// int part_extended = 0;
// Find out if message part is extended, encoded, or both
r = mailimf_char_parse(message, length, &cur_token, '*');
if (r == MAILIMF_NO_ERROR) {
uint32_t part_num = 0;
r = mailimf_number_parse(message, length, &cur_token, &part_num);
if (r == MAILIMF_NO_ERROR) {
// part_extended = 1;
r = mailimf_char_parse(message, length, &cur_token, '*');
// See RFC2231, Section 4.1. FIXME - it's possible to have unencoded parts interspersed
// with encoded post per RFC, so this may not be smart. Depends on if decoding is an issue with
// interspersed ASCII segments.
// However, at this point, we know that the first part of the parameter either contained encoding information,
// or it shouldn't be encoded. Also, it seems very doubtful most clients would go to the trouble of mixing encoded
// and non-encoded information when splitting the string.
// The fix right now is to ignore the encoding flag at this point, as we will either decode the whole string,
// or not at all.
}
else if (r != MAILIMF_ERROR_PARSE)
return r;
}
else if (r != MAILIMF_ERROR_PARSE)
return r;
r = mailimf_unstrict_char_parse(message, length, &cur_token, '=');
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_cfws_parse(message, length, &cur_token);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
return r;
// Ok, let's go.
// if (part_encoded || part_extended) {
char* part_str = NULL;
// See RFC2231, Section 4.1. FIXME - it's possible to have unencoded parts interspersed
// with encoded post per RFC, so this may not be smart. Depends on if decoding is an issue with
// interspersed ASCII segments.
r = mailmime_value_parse(message, length, &cur_token, &part_str);
if (r != MAILIMF_NO_ERROR)
return r;
size_t part_size = strlen(part_str);
size_t new_size = built_len + part_size + 1;
char* new_str = NULL;
new_str = realloc((void*)built_str, new_size);
if (new_str) {
strncat(new_str, part_str, part_size);
built_str = new_str;
free(part_str);
part_str = NULL;
}
else {
free(built_str);
return MAILIMF_ERROR_MEMORY;
}
// }
built_len = strlen(built_str);
}
}
if (encoded && built_str && _charset && _charset[0] != '\0') {
char* replace_str = NULL;
mailmime_parm_value_unescape(&replace_str, built_str);
if (replace_str) {
free(built_str);
built_str = replace_str;
replace_str = NULL;
}
if (strcasecmp(_charset, "utf-8") != 0 &&
strcasecmp(_charset, "utf8") != 0) {
// best effort
r = charconv("utf-8", _charset, built_str,
strlen(built_str), &replace_str);
switch(r) {
case MAIL_CHARCONV_ERROR_UNKNOWN_CHARSET:
r = charconv("utf-8", "iso-8859-1", built_str,
strlen(built_str), &replace_str);
break;
case MAIL_CHARCONV_ERROR_MEMORY:
return MAILIMF_ERROR_MEMORY;
case MAIL_CHARCONV_ERROR_CONV:
return MAILIMF_ERROR_PARSE;
}
switch (r) {
case MAIL_CHARCONV_ERROR_MEMORY:
return MAILIMF_ERROR_MEMORY;
case MAIL_CHARCONV_ERROR_CONV:
return MAILIMF_ERROR_PARSE;
}
}
if (replace_str) {
built_str = replace_str;
replace_str = NULL;
}
}
}
*indx = cur_token;
*result = built_str;
return MAILIMF_NO_ERROR;
}
/* /*
filename-parm := "filename" "=" value filename-parm := "filename" "=" value
*/ */
...@@ -435,23 +655,29 @@ mailmime_filename_parm_parse(const char * message, size_t length, ...@@ -435,23 +655,29 @@ mailmime_filename_parm_parse(const char * message, size_t length,
size_t cur_token; size_t cur_token;
cur_token = * indx; cur_token = * indx;
r = mailimf_token_case_insensitive_parse(message, length,
&cur_token, "filename");
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_unstrict_char_parse(message, length, &cur_token, '=');
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_cfws_parse(message, length, &cur_token); r = mailmime_extended_parm_parse(message, length, "filename", &cur_token, &value);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
return r;
r = mailmime_value_parse(message, length, &cur_token, &value); if (r != MAILIMF_NO_ERROR) {
if (r != MAILIMF_NO_ERROR)
return r; r = mailimf_token_case_insensitive_parse(message, length,
&cur_token, "filename");
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_unstrict_char_parse(message, length, &cur_token, '=');
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_cfws_parse(message, length, &cur_token);
if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
return r;
r = mailmime_value_parse(message, length, &cur_token, &value);
if (r != MAILIMF_NO_ERROR)
return r;
}
* indx = cur_token; * indx = cur_token;
* result = value; * result = value;
......
...@@ -436,7 +436,6 @@ struct mailmime * mailmime_new(int mm_type, ...@@ -436,7 +436,6 @@ struct mailmime * mailmime_new(int mm_type,
mime->mm_mime_fields = mm_mime_fields; mime->mm_mime_fields = mm_mime_fields;
mime->mm_content_type = mm_content_type; mime->mm_content_type = mm_content_type;
mime->mm_imf_fields = NULL;
mime->mm_body = mm_body; mime->mm_body = mm_body;
switch (mm_type) { switch (mm_type) {
...@@ -506,8 +505,6 @@ void mailmime_free(struct mailmime * mime) ...@@ -506,8 +505,6 @@ void mailmime_free(struct mailmime * mime)
if (mime->mm_mime_fields != NULL) if (mime->mm_mime_fields != NULL)
mailmime_fields_free(mime->mm_mime_fields); mailmime_fields_free(mime->mm_mime_fields);
if (mime->mm_imf_fields != NULL)
mailimf_fields_free(mime->mm_imf_fields);
if (mime->mm_content_type != NULL) if (mime->mm_content_type != NULL)
mailmime_content_free(mime->mm_content_type); mailmime_content_free(mime->mm_content_type);
free(mime); free(mime);
......
...@@ -322,7 +322,6 @@ struct mailmime { ...@@ -322,7 +322,6 @@ struct mailmime {
struct mailmime_fields * mm_mime_fields; struct mailmime_fields * mm_mime_fields;
struct mailmime_content * mm_content_type; struct mailmime_content * mm_content_type;
struct mailimf_fields * mm_imf_fields; /* preserves the whole set of fields from a part for memoryhole */
struct mailmime_data * mm_body; struct mailmime_data * mm_body;
union { union {
......
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
#include "mailimf_write_generic.h" #include "mailimf_write_generic.h"
#include "mailmime_content.h" #include "mailmime_content.h"
#include "mailmime_types_helper.h" #include "mailmime_types_helper.h"
#include "mailmime.h"
#include "syscall_wrappers.h" #include "syscall_wrappers.h"
...@@ -428,6 +429,166 @@ static int mailmime_disposition_write_driver(int (* do_write)(void *, const char ...@@ -428,6 +429,166 @@ static int mailmime_disposition_write_driver(int (* do_write)(void *, const char
return MAILIMF_NO_ERROR; return MAILIMF_NO_ERROR;
} }
static int contains_non_ascii(const char* check_string) {
const char* start = check_string;
size_t len = strlen(check_string);
const char* end = start + len;
while (start != end) {
char cur_char = *start++;
if (cur_char > 127 || cur_char < 0)
return TRUE;
}
return FALSE;
}
static void
break_filename(char** extended_filename, const char* filename_str,
size_t length, int is_encoded) {
if (!extended_filename || !filename_str || length < 1)
return;
clist* line_list = clist_new();
// We'll be adding a lot of ";\r\n" into the output, so we make an initial educated guess on size
size_t filename_key_len = (is_encoded ? 11 : 10); // " filename*0*=" | " filename*0="
size_t addl_chars_len = (is_encoded ? 3 : 5); // ";\r\n" + 2 quotes
const char* equals_str = (is_encoded ? "*=" : "=\"");
const char* end_str = (is_encoded ? ";\r\n" : "\";\r\n");
size_t key_buffer_size = filename_key_len + 5; // This is ridiculous, because 1000+ -part filenames??? But ok.
int curr_line_count = 0;
int curr_char_count = 0;
const char* curr_src_ptr = filename_str;
const char* curr_src_end = filename_str + strlen(filename_str);
size_t end_string_size = (is_encoded ? 3 : 4);
char curr_line_buf[80];
snprintf(curr_line_buf, key_buffer_size, "\r\n");
clist_append(line_list, strdup(curr_line_buf));
for (curr_char_count = 2; curr_src_ptr < curr_src_end; curr_line_count++) {
// Start line.
if (curr_line_count > 9999)
return; // FIXME - free stuff
char* curr_line_ptr = curr_line_buf;
snprintf(curr_line_buf, key_buffer_size, " filename*%d%s", curr_line_count, equals_str);
size_t curr_key_len = strlen(curr_line_buf);
curr_line_ptr += curr_key_len;
curr_char_count += curr_key_len;
size_t max_remaining_line_chars = length - curr_key_len - addl_chars_len;
unsigned int i;
if (!is_encoded) {
for (i = 0; i < max_remaining_line_chars && curr_src_ptr < curr_src_end; i++) {
*curr_line_ptr++ = *curr_src_ptr++;
curr_char_count++;
}
}
else {
if (curr_line_count == 0) {
int k = curr_char_count;
while (*curr_src_ptr != '%') {
*curr_line_ptr++ = *curr_src_ptr++;
curr_char_count++;
}
k = curr_char_count - k;
max_remaining_line_chars -= k;
}
// Fun fun fun.
// UTF-8 chars run between one and four bytes. But these are UTF-8
// encoded now, so each byte is listed as two characters (hex value)
// and a percentage.
// Because they are between one and four bytes, always be safe
// copying the first max_remaining - (3 * 3) of them
// and then finding a break either there or in the next 3
// character byte representations..
// Copy in the first max - 3 * 3 chars
max_remaining_line_chars = max_remaining_line_chars - (max_remaining_line_chars % 3);
size_t max_safe = max_remaining_line_chars - 9;
// Copy in three at a time.
for (i = 0; i < max_safe && curr_src_ptr < curr_src_end; i += 3) {
int j;
for (j = 0; j < 3; j++) {
*curr_line_ptr++ = *curr_src_ptr++;
curr_char_count++;
}
}
if (curr_src_ptr != curr_src_end) {
char last_byte = 0;
if (curr_src_ptr > (filename_str + 2)) { // FIXME: This is just a crap error. Dunno how this'd NOT happen.
hex_to_byte(&last_byte, curr_src_ptr - 2);
// Check to see if it's the start of a four-byte char
if ((unsigned char)last_byte >= 0xF0) {
// Add the next three
int j;
if ((curr_src_ptr + 9) <= curr_src_end) {
for (j = 0; j < 9 && curr_src_ptr < curr_src_end; j++) {
*curr_line_ptr++ = *curr_src_ptr++;
curr_char_count++;
}
}
}
else if ((unsigned char)last_byte >= 0x80) {
// Find a break in the next 3 characters.
for (i = 0; i < 2; i++) {
if ((curr_src_ptr + 2) < curr_src_end) {
last_byte = 0;
hex_to_byte(&last_byte, curr_src_ptr + 1);
// Is this byte the start of a new character?
if ((unsigned char)last_byte < 0x7F || (unsigned char)last_byte > 0xC0)
break;
// Nope, check the next
int j;
for (j = 0; j < 3; j++) {
*curr_line_ptr++ = *curr_src_ptr++;
curr_char_count++;
}
}
}
}
}
}
}
if (curr_src_ptr >= curr_src_end) {
strcpy(curr_line_ptr, (is_encoded ? "\r\n" : "\"\r\n"));
curr_char_count += (is_encoded ? 2 : 3);
clist_append(line_list, strdup(curr_line_buf));
break;
}
else {
strcpy(curr_line_ptr, end_str);
curr_char_count += end_string_size;
clist_append(line_list, strdup(curr_line_buf));
}
}
size_t fname_size = curr_char_count + 1;
*extended_filename = calloc(fname_size, 1);
clistiter* cur;
// int start = 1;
if (line_list) {
for(cur = clist_begin(line_list) ; cur != NULL ; cur = clist_next(cur)) {
// if (start) {
// strcpy(*extended_filename, ((char*)(cur->data)));
// start = 0;
// }
// else {
strcat(*extended_filename, ((char*)(cur->data)));
// }
free(cur->data);
cur->data = NULL;
}
}
clist_free(line_list);
}
static int static int
mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
struct mailmime_disposition_parm * param) struct mailmime_disposition_parm * param)
...@@ -435,10 +596,49 @@ mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, s ...@@ -435,10 +596,49 @@ mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, s
size_t len; size_t len;
char sizestr[20]; char sizestr[20];
int r; int r;
int has_extended_filename = 0;
int has_encoded_filename = 0;
char* extended_filename = NULL;
const char* filename_key = "filename=";
size_t filename_key_len = strlen(filename_key);
char* fname = param->pa_data.pa_filename;
const int _MIME_LINE_LENGTH = 72;
const int _QUOTES_PLUS_SPACE_LEN = 3;
size_t filename_strlen;
char* new_fname = NULL;
switch (param->pa_type) { switch (param->pa_type) {
case MAILMIME_DISPOSITION_PARM_FILENAME: case MAILMIME_DISPOSITION_PARM_FILENAME:
len = strlen("filename=") + strlen(param->pa_data.pa_filename); if (!fname)
break;
if (contains_non_ascii(fname)) {
mailmime_parm_value_escape(&new_fname, fname);
fname = new_fname;
}
filename_strlen = strlen(fname);
if (strstr(fname, "utf-8''") == fname) {
// we're in for some fun here...
has_encoded_filename = 1;
filename_key_len++;
}
if ((filename_strlen + filename_key_len + _QUOTES_PLUS_SPACE_LEN) > _MIME_LINE_LENGTH) {
has_extended_filename = 1;
}
if (!has_extended_filename) {
if (has_encoded_filename) {
filename_key = "filename*=";
extended_filename = fname;
}
len = filename_key_len + strlen(fname);
}
else {
break_filename(&extended_filename, fname, _MIME_LINE_LENGTH, has_encoded_filename);
if (new_fname == fname) {
free(fname);
fname = new_fname = NULL;
}
len = (extended_filename ? strlen(extended_filename) : 0);
}
break; break;
case MAILMIME_DISPOSITION_PARM_CREATION_DATE: case MAILMIME_DISPOSITION_PARM_CREATION_DATE:
...@@ -467,8 +667,10 @@ mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, s ...@@ -467,8 +667,10 @@ mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, s
default: default:
return MAILIMF_ERROR_INVAL; return MAILIMF_ERROR_INVAL;
} }
if (* col > 1) {
// KG: FIXME - this could cause us trouble
if (* col > 1 && !extended_filename) {
if (* col + len > MAX_MAIL_COL) { if (* col + len > MAX_MAIL_COL) {
r = mailimf_string_write_driver(do_write, data, col, "\r\n ", 3); r = mailimf_string_write_driver(do_write, data, col, "\r\n ", 3);
...@@ -482,14 +684,32 @@ mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, s ...@@ -482,14 +684,32 @@ mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, s
switch (param->pa_type) { switch (param->pa_type) {
case MAILMIME_DISPOSITION_PARM_FILENAME: case MAILMIME_DISPOSITION_PARM_FILENAME:
r = mailimf_string_write_driver(do_write, data, col, "filename=", 9); if (extended_filename) {
if (r != MAILIMF_NO_ERROR) if (has_encoded_filename && !has_extended_filename) {
return r; r = mailimf_string_write_driver(do_write, data, col,
filename_key, filename_key_len);
r = mailimf_quoted_string_write_driver(do_write, data, col, if (r != MAILIMF_NO_ERROR)
param->pa_data.pa_filename, strlen(param->pa_data.pa_filename)); return r;
if (r != MAILIMF_NO_ERROR) }
return r; r = mailimf_string_write_driver(do_write, data, col,
extended_filename,
strlen(extended_filename));
if (r != MAILIMF_NO_ERROR)
return r;
free(extended_filename);
}
else {
r = mailimf_string_write_driver(do_write, data, col,
filename_key, filename_key_len);
if (r != MAILIMF_NO_ERROR)
return r;
r = mailimf_quoted_string_write_driver(do_write, data, col,
param->pa_data.pa_filename, strlen(param->pa_data.pa_filename));
if (r != MAILIMF_NO_ERROR)
return r;
}
// param->pa_data.pa_filename, strlen(param->pa_data.pa_filename));
// r = mailimf_quoted_string_write_driver(do_write, data, col,
break; break;
case MAILMIME_DISPOSITION_PARM_CREATION_DATE: case MAILMIME_DISPOSITION_PARM_CREATION_DATE:
......